Index: test/cctest/test-regexp.cc |
=================================================================== |
--- test/cctest/test-regexp.cc (revision 0) |
+++ test/cctest/test-regexp.cc (revision 830) |
@@ -0,0 +1,922 @@ |
+// Copyright 2006-2008 the V8 project authors. All rights reserved. |
Erik Corry
2008/11/25 11:03:52
Correct year?
Christian Plesner Hansen
2008/11/26 06:49:56
Fixed
|
+// Redistribution and use in source and binary forms, with or without |
+// modification, are permitted provided that the following conditions are |
+// met: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following |
+// disclaimer in the documentation and/or other materials provided |
+// with the distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived |
+// from this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+ |
+#include <stdlib.h> |
+#include <set> |
Mads Ager (chromium)
2008/11/25 21:09:41
Please remove. We are not using <set>, right?
Christian Plesner Hansen
2008/11/26 06:49:56
Yes we are.
|
+ |
+#include "v8.h" |
+ |
+#include "cctest.h" |
+#include "zone-inl.h" |
+#include "parser.h" |
+#include "ast.h" |
+#include "jsregexp-inl.h" |
+#include "assembler-irregexp.h" |
+#include "regexp-macro-assembler.h" |
+#include "regexp-macro-assembler-irregexp.h" |
+#include "regexp-macro-assembler-ia32.h" |
+#include "interpreter-irregexp.h" |
+ |
+ |
+using namespace v8::internal; |
+ |
+ |
+static SmartPointer<const char> Parse(const char* input) { |
+ v8::HandleScope scope; |
+ ZoneScope zone_scope(DELETE_ON_EXIT); |
+ FlatStringReader reader(CStrVector(input)); |
+ RegExpParseResult result; |
+ CHECK(v8::internal::ParseRegExp(&reader, &result)); |
+ CHECK(result.tree != NULL); |
+ CHECK(result.error.is_null()); |
+ SmartPointer<const char> output = result.tree->ToString(); |
+ return output; |
+} |
+ |
+static bool ParseEscapes(const char* input) { |
+ v8::HandleScope scope; |
+ unibrow::Utf8InputBuffer<> buffer(input, strlen(input)); |
+ ZoneScope zone_scope(DELETE_ON_EXIT); |
+ FlatStringReader reader(CStrVector(input)); |
+ RegExpParseResult result; |
+ CHECK(v8::internal::ParseRegExp(&reader, &result)); |
+ CHECK(result.tree != NULL); |
+ CHECK(result.error.is_null()); |
+ return result.has_character_escapes; |
+} |
+ |
+ |
+#define CHECK_PARSE_EQ(input, expected) CHECK_EQ(expected, *Parse(input)) |
+#define CHECK_ESCAPES(input, has_escapes) CHECK_EQ(has_escapes, \ |
Mads Ager (chromium)
2008/11/25 21:09:41
How about:
#define CHECK_ESCAPES(input, has_escap
|
+ ParseEscapes(input)); |
+ |
+TEST(Parser) { |
+ V8::Initialize(NULL); |
+ CHECK_PARSE_EQ("abc", "'abc'"); |
+ CHECK_PARSE_EQ("", "%"); |
+ CHECK_PARSE_EQ("abc|def", "(| 'abc' 'def')"); |
+ CHECK_PARSE_EQ("abc|def|ghi", "(| 'abc' 'def' 'ghi')"); |
+ CHECK_PARSE_EQ("^xxx$", "(: @^i 'xxx' @$i)"); |
+ CHECK_PARSE_EQ("ab\\b\\d\\bcd", "(: 'ab' @b [0-9] @b 'cd')"); |
+ CHECK_PARSE_EQ("\\w|\\d", "(| [0-9 A-Z _ a-z] [0-9])"); |
+ CHECK_PARSE_EQ("a*", "(# 0 - g 'a')"); |
+ CHECK_PARSE_EQ("a*?", "(# 0 - n 'a')"); |
+ CHECK_PARSE_EQ("abc+", "(: 'ab' (# 1 - g 'c'))"); |
+ CHECK_PARSE_EQ("abc+?", "(: 'ab' (# 1 - n 'c'))"); |
+ CHECK_PARSE_EQ("xyz?", "(: 'xy' (# 0 1 g 'z'))"); |
+ CHECK_PARSE_EQ("xyz??", "(: 'xy' (# 0 1 n 'z'))"); |
+ CHECK_PARSE_EQ("xyz{0,1}", "(: 'xy' (# 0 1 g 'z'))"); |
+ CHECK_PARSE_EQ("xyz{0,1}?", "(: 'xy' (# 0 1 n 'z'))"); |
+ CHECK_PARSE_EQ("xyz{93}", "(: 'xy' (# 93 93 g 'z'))"); |
+ CHECK_PARSE_EQ("xyz{93}?", "(: 'xy' (# 93 93 n 'z'))"); |
+ CHECK_PARSE_EQ("xyz{1,32}", "(: 'xy' (# 1 32 g 'z'))"); |
+ CHECK_PARSE_EQ("xyz{1,32}?", "(: 'xy' (# 1 32 n 'z'))"); |
+ CHECK_PARSE_EQ("xyz{1,}", "(: 'xy' (# 1 - g 'z'))"); |
+ CHECK_PARSE_EQ("xyz{1,}?", "(: 'xy' (# 1 - n 'z'))"); |
+ CHECK_PARSE_EQ("a\\fb\\nc\\rd\\te\\vf", "'a\\x0cb\\x0ac\\x0dd\\x09e\\x0bf'"); |
+ CHECK_PARSE_EQ("a\\nb\\bc", "(: 'a\\x0ab' @b 'c')"); |
+ CHECK_PARSE_EQ("(?:foo)", "'foo'"); |
+ CHECK_PARSE_EQ("(?: foo )", "' foo '"); |
+ CHECK_PARSE_EQ("(foo|bar|baz)", "(^ (| 'foo' 'bar' 'baz'))"); |
+ CHECK_PARSE_EQ("foo|(bar|baz)|quux", "(| 'foo' (^ (| 'bar' 'baz')) 'quux')"); |
+ CHECK_PARSE_EQ("foo(?=bar)baz", "(: 'foo' (-> + 'bar') 'baz')"); |
+ CHECK_PARSE_EQ("foo(?!bar)baz", "(: 'foo' (-> - 'bar') 'baz')"); |
+ CHECK_PARSE_EQ("()", "(^ %)"); |
+ CHECK_PARSE_EQ("(?=)", "(-> + %)"); |
+ CHECK_PARSE_EQ("[]", "^[\\x00-\\uffff]"); // Doesn't compile on windows |
+ CHECK_PARSE_EQ("[^]", "[\\x00-\\uffff]"); // \uffff isn't in codepage 1252 |
+ CHECK_PARSE_EQ("[x]", "[x]"); |
+ CHECK_PARSE_EQ("[xyz]", "[x y z]"); |
+ CHECK_PARSE_EQ("[a-zA-Z0-9]", "[a-z A-Z 0-9]"); |
+ CHECK_PARSE_EQ("[-123]", "[- 1 2 3]"); |
+ CHECK_PARSE_EQ("[^123]", "^[1 2 3]"); |
+ CHECK_PARSE_EQ("]", "']'"); |
+ CHECK_PARSE_EQ("}", "'}'"); |
+ CHECK_PARSE_EQ("[a-b-c]", "[a-b - c]"); |
+ CHECK_PARSE_EQ("[\\d]", "[0-9]"); |
+ CHECK_PARSE_EQ("[x\\dz]", "[x 0-9 z]"); |
+ CHECK_PARSE_EQ("[\\d-z]", "[0-9 - z]"); |
+ CHECK_PARSE_EQ("[\\d-\\d]", "[0-9 - 0-9]"); |
+ CHECK_PARSE_EQ("\\cj\\cJ\\ci\\cI\\ck\\cK", |
+ "'\\x0a\\x0a\\x09\\x09\\x0b\\x0b'"); |
+ CHECK_PARSE_EQ("\\c!", "'c!'"); |
+ CHECK_PARSE_EQ("\\c_", "'c_'"); |
+ CHECK_PARSE_EQ("\\c~", "'c~'"); |
+ CHECK_PARSE_EQ("[a\\]c]", "[a ] c]"); |
+ CHECK_PARSE_EQ("\\[\\]\\{\\}\\(\\)\\%\\^\\#\\ ", "'[]{}()%^# '"); |
+ CHECK_PARSE_EQ("[\\[\\]\\{\\}\\(\\)\\%\\^\\#\\ ]", "[[ ] { } ( ) % ^ # ]"); |
+ CHECK_PARSE_EQ("\\0", "'\\x00'"); |
+ CHECK_PARSE_EQ("\\8", "'8'"); |
+ CHECK_PARSE_EQ("\\9", "'9'"); |
+ CHECK_PARSE_EQ("\\11", "'\\x09'"); |
+ CHECK_PARSE_EQ("\\11a", "'\\x09a'"); |
+ CHECK_PARSE_EQ("\\011", "'\\x09'"); |
+ CHECK_PARSE_EQ("\\00011", "'\\x0011'"); |
+ CHECK_PARSE_EQ("\\118", "'\\x098'"); |
+ CHECK_PARSE_EQ("\\111", "'I'"); |
+ CHECK_PARSE_EQ("\\1111", "'I1'"); |
+ CHECK_PARSE_EQ("(x)(x)(x)\\1", "(: (^ 'x') (^ 'x') (^ 'x') (<- 1))"); |
+ CHECK_PARSE_EQ("(x)(x)(x)\\2", "(: (^ 'x') (^ 'x') (^ 'x') (<- 2))"); |
+ CHECK_PARSE_EQ("(x)(x)(x)\\3", "(: (^ 'x') (^ 'x') (^ 'x') (<- 3))"); |
+ CHECK_PARSE_EQ("(x)(x)(x)\\4", "(: (^ 'x') (^ 'x') (^ 'x') '\\x04')"); |
+ CHECK_PARSE_EQ("(x)(x)(x)\\1*", "(: (^ 'x') (^ 'x') (^ 'x')" |
+ " (# 0 - g (<- 1)))"); |
+ CHECK_PARSE_EQ("(x)(x)(x)\\2*", "(: (^ 'x') (^ 'x') (^ 'x')" |
+ " (# 0 - g (<- 2)))"); |
+ CHECK_PARSE_EQ("(x)(x)(x)\\3*", "(: (^ 'x') (^ 'x') (^ 'x')" |
+ " (# 0 - g (<- 3)))"); |
+ CHECK_PARSE_EQ("(x)(x)(x)\\4*", "(: (^ 'x') (^ 'x') (^ 'x')" |
+ " (# 0 - g '\\x04'))"); |
+ CHECK_PARSE_EQ("(x)(x)(x)(x)(x)(x)(x)(x)(x)(x)\\10", |
+ "(: (^ 'x') (^ 'x') (^ 'x') (^ 'x') (^ 'x') (^ 'x')" |
+ " (^ 'x') (^ 'x') (^ 'x') (^ 'x') (<- 10))"); |
+ CHECK_PARSE_EQ("(x)(x)(x)(x)(x)(x)(x)(x)(x)(x)\\11", |
+ "(: (^ 'x') (^ 'x') (^ 'x') (^ 'x') (^ 'x') (^ 'x')" |
+ " (^ 'x') (^ 'x') (^ 'x') (^ 'x') '\\x09')"); |
+ CHECK_PARSE_EQ("(a)\\1", "(: (^ 'a') (<- 1))"); |
+ CHECK_PARSE_EQ("(a\\1)", "(^ 'a')"); |
+ CHECK_PARSE_EQ("(\\1a)", "(^ 'a')"); |
+ CHECK_PARSE_EQ("\\1(a)", "(^ 'a')"); |
+ CHECK_PARSE_EQ("(?!(a))\\1", "(-> - (^ 'a'))"); |
+ CHECK_PARSE_EQ("(?!\\1(a\\1)\\1)\\1", "(-> - (: (^ 'a') (<- 1)))"); |
+ CHECK_PARSE_EQ("[\\0]", "[\\x00]"); |
+ CHECK_PARSE_EQ("[\\11]", "[\\x09]"); |
+ CHECK_PARSE_EQ("[\\11a]", "[\\x09 a]"); |
+ CHECK_PARSE_EQ("[\\011]", "[\\x09]"); |
+ CHECK_PARSE_EQ("[\\00011]", "[\\x00 1 1]"); |
+ CHECK_PARSE_EQ("[\\118]", "[\\x09 8]"); |
+ CHECK_PARSE_EQ("[\\111]", "[I]"); |
+ CHECK_PARSE_EQ("[\\1111]", "[I 1]"); |
+ CHECK_PARSE_EQ("\\x34", "'\x34'"); |
+ CHECK_PARSE_EQ("\\x60", "'\x60'"); |
+ CHECK_PARSE_EQ("\\x3z", "'x3z'"); |
+ CHECK_PARSE_EQ("\\u0034", "'\x34'"); |
+ CHECK_PARSE_EQ("\\u003z", "'u003z'"); |
+ CHECK_PARSE_EQ("foo[z]*", "(: 'foo' (# 0 - g [z]))"); |
+ |
+ CHECK_ESCAPES("a", false); |
+ CHECK_ESCAPES("a|b", false); |
+ CHECK_ESCAPES("a\\n", true); |
+ CHECK_ESCAPES("^a", false); |
+ CHECK_ESCAPES("a$", false); |
+ CHECK_ESCAPES("a\\b!", false); |
+ CHECK_ESCAPES("a\\Bb", false); |
+ CHECK_ESCAPES("a*", false); |
+ CHECK_ESCAPES("a*?", false); |
+ CHECK_ESCAPES("a?", false); |
+ CHECK_ESCAPES("a??", false); |
+ CHECK_ESCAPES("a{0,1}?", false); |
+ CHECK_ESCAPES("a{1,1}?", false); |
+ CHECK_ESCAPES("a{1,2}?", false); |
+ CHECK_ESCAPES("a+?", false); |
+ CHECK_ESCAPES("(a)", false); |
+ CHECK_ESCAPES("(a)\\1", false); |
+ CHECK_ESCAPES("(\\1a)", false); |
+ CHECK_ESCAPES("\\1(a)", false); |
+ CHECK_ESCAPES("a\\s", false); |
+ CHECK_ESCAPES("a\\S", false); |
+ CHECK_ESCAPES("a\\d", false); |
+ CHECK_ESCAPES("a\\D", false); |
+ CHECK_ESCAPES("a\\w", false); |
+ CHECK_ESCAPES("a\\W", false); |
+ CHECK_ESCAPES("a.", false); |
+ CHECK_ESCAPES("a\\q", true); |
+ CHECK_ESCAPES("a[a]", false); |
+ CHECK_ESCAPES("a[^a]", false); |
+ CHECK_ESCAPES("a[a-z]", false); |
+ CHECK_ESCAPES("a[\\q]", false); |
+ CHECK_ESCAPES("a(?:b)", false); |
+ CHECK_ESCAPES("a(?=b)", false); |
+ CHECK_ESCAPES("a(?!b)", false); |
+ CHECK_ESCAPES("\\x60", true); |
+ CHECK_ESCAPES("\\u0060", true); |
+ CHECK_ESCAPES("\\cA", true); |
+ CHECK_ESCAPES("\\q", true); |
+ CHECK_ESCAPES("\\1112", true); |
+ CHECK_ESCAPES("\\0", true); |
+ CHECK_ESCAPES("(a)\\1", false); |
+ |
+ CHECK_PARSE_EQ("a{}", "'a{}'"); |
+ CHECK_PARSE_EQ("a{,}", "'a{,}'"); |
+ CHECK_PARSE_EQ("a{", "'a{'"); |
+ CHECK_PARSE_EQ("a{z}", "'a{z}'"); |
+ CHECK_PARSE_EQ("a{1z}", "'a{1z}'"); |
+ CHECK_PARSE_EQ("a{12z}", "'a{12z}'"); |
+ CHECK_PARSE_EQ("a{12,", "'a{12,'"); |
+ CHECK_PARSE_EQ("a{12,3b", "'a{12,3b'"); |
+ CHECK_PARSE_EQ("{}", "'{}'"); |
+ CHECK_PARSE_EQ("{,}", "'{,}'"); |
+ CHECK_PARSE_EQ("{", "'{'"); |
+ CHECK_PARSE_EQ("{z}", "'{z}'"); |
+ CHECK_PARSE_EQ("{1z}", "'{1z}'"); |
+ CHECK_PARSE_EQ("{12z}", "'{12z}'"); |
+ CHECK_PARSE_EQ("{12,", "'{12,'"); |
+ CHECK_PARSE_EQ("{12,3b", "'{12,3b'"); |
+} |
+ |
+TEST(ParserRegression) { |
+ CHECK_PARSE_EQ("[A-Z$-][x]", "(! [A-Z $ -] [x])"); |
+ CHECK_PARSE_EQ("a{3,4*}", "(: 'a{3,' (# 0 - g '4') '}')"); |
+ CHECK_PARSE_EQ("{", "'{'"); |
+ CHECK_PARSE_EQ("a|", "(| 'a' %)"); |
+} |
+ |
+static void ExpectError(const char* input, |
+ const char* expected) { |
+ v8::HandleScope scope; |
+ ZoneScope zone_scope(DELETE_ON_EXIT); |
+ FlatStringReader reader(CStrVector(input)); |
+ RegExpParseResult result; |
+ CHECK_EQ(false, v8::internal::ParseRegExp(&reader, &result)); |
+ CHECK(result.tree == NULL); |
+ CHECK(!result.error.is_null()); |
+ SmartPointer<char> str = result.error->ToCString(ALLOW_NULLS); |
+ CHECK_EQ(expected, *str); |
+} |
+ |
+ |
+TEST(Errors) { |
+ V8::Initialize(NULL); |
+ const char* kEndBackslash = "\\ at end of pattern"; |
+ ExpectError("\\", kEndBackslash); |
+ const char* kUnterminatedGroup = "Unterminated group"; |
+ ExpectError("(foo", kUnterminatedGroup); |
+ const char* kInvalidGroup = "Invalid group"; |
+ ExpectError("(?", kInvalidGroup); |
+ const char* kUnterminatedCharacterClass = "Unterminated character class"; |
+ ExpectError("[", kUnterminatedCharacterClass); |
+ ExpectError("[a-", kUnterminatedCharacterClass); |
+ const char* kIllegalCharacterClass = "Illegal character class"; |
+ ExpectError("[a-\\w]", kIllegalCharacterClass); |
+ const char* kEndControl = "\\c at end of pattern"; |
+ ExpectError("\\c", kEndControl); |
+ const char* kNothingToRepeat = "Nothing to repeat"; |
+ ExpectError("*", kNothingToRepeat); |
+ ExpectError("?", kNothingToRepeat); |
+ ExpectError("+", kNothingToRepeat); |
+ ExpectError("{1}", kNothingToRepeat); |
+ ExpectError("{1,2}", kNothingToRepeat); |
+ ExpectError("{1,}", kNothingToRepeat); |
+} |
+ |
+ |
+static bool IsDigit(uc16 c) { |
+ return ('0' <= c && c <= '9'); |
+} |
+ |
+ |
+static bool NotDigit(uc16 c) { |
+ return !IsDigit(c); |
+} |
+ |
+ |
+static bool IsWhiteSpace(uc16 c) { |
+ switch (c) { |
+ case 0x09: |
+ case 0x0A: |
+ case 0x0B: |
+ case 0x0C: |
+ case 0x0d: |
+ case 0x20: |
+ case 0xA0: |
+ case 0x2028: |
+ case 0x2029: |
+ return true; |
+ default: |
+ return unibrow::Space::Is(c); |
+ } |
+} |
+ |
+ |
+static bool NotWhiteSpace(uc16 c) { |
+ return !IsWhiteSpace(c); |
+} |
+ |
+ |
+static bool IsWord(uc16 c) { |
+ return ('a' <= c && c <= 'z') |
+ || ('A' <= c && c <= 'Z') |
+ || ('0' <= c && c <= '9') |
+ || (c == '_'); |
+} |
+ |
+ |
+static bool NotWord(uc16 c) { |
+ return !IsWord(c); |
+} |
+ |
+ |
+static bool Dot(uc16 c) { |
+ switch (c) { |
+ // CR LF LS PS |
+ case 0x000A: case 0x000D: case 0x2028: case 0x2029: |
+ return false; |
+ default: |
+ return true; |
+ } |
+} |
+ |
+ |
+static void TestCharacterClassEscapes(uc16 c, bool (pred)(uc16 c)) { |
+ ZoneScope scope(DELETE_ON_EXIT); |
+ ZoneList<CharacterRange>* ranges = new ZoneList<CharacterRange>(2); |
+ CharacterRange::AddClassEscape(c, ranges); |
+ for (unsigned i = 0; i < (1 << 16); i++) { |
+ bool in_class = false; |
+ for (int j = 0; !in_class && j < ranges->length(); j++) { |
+ CharacterRange& range = ranges->at(j); |
+ in_class = (range.from() <= i && i <= range.to()); |
+ } |
+ CHECK_EQ(pred(i), in_class); |
+ } |
+} |
+ |
+ |
+TEST(CharacterClassEscapes) { |
+ TestCharacterClassEscapes('.', Dot); |
+ TestCharacterClassEscapes('d', IsDigit); |
+ TestCharacterClassEscapes('D', NotDigit); |
+ TestCharacterClassEscapes('s', IsWhiteSpace); |
+ TestCharacterClassEscapes('S', NotWhiteSpace); |
+ TestCharacterClassEscapes('w', IsWord); |
+ TestCharacterClassEscapes('W', NotWord); |
+} |
+ |
+ |
+static RegExpNode* Compile(const char* input) { |
+ FlatStringReader reader(CStrVector(input)); |
+ RegExpParseResult result; |
+ if (!v8::internal::ParseRegExp(&reader, &result)) |
+ return NULL; |
+ RegExpNode* node = NULL; |
+ RegExpEngine::Compile(&result, &node, false); |
+ return node; |
+} |
+ |
+ |
+static void Execute(const char* input, |
+ const char* str, |
+ bool dot_output = false) { |
+ v8::HandleScope scope; |
+ ZoneScope zone_scope(DELETE_ON_EXIT); |
+ RegExpNode* node = Compile(input); |
+ USE(node); |
+#ifdef DEBUG |
+ if (dot_output) { |
+ RegExpEngine::DotPrint(input, node); |
+ exit(0); |
+ } |
+#endif // DEBUG |
+} |
+ |
+ |
+TEST(Execution) { |
+ V8::Initialize(NULL); |
+ Execute(".*?(?:a[bc]d|e[fg]h)", "xxxabbegh"); |
+ Execute(".*?(?:a[bc]d|e[fg]h)", "xxxabbefh"); |
+ Execute(".*?(?:a[bc]d|e[fg]h)", "xxxabbefd"); |
+} |
+ |
+ |
+class TestConfig { |
+ public: |
+ typedef int Key; |
+ typedef int Value; |
+ static const int kNoKey; |
+ static const int kNoValue; |
+ static inline int Compare(int a, int b) { |
+ if (a < b) |
+ return -1; |
+ else if (a > b) |
+ return 1; |
+ else |
+ return 0; |
+ } |
+}; |
+ |
+ |
+const int TestConfig::kNoKey = 0; |
+const int TestConfig::kNoValue = 0; |
+ |
+ |
+static int PseudoRandom(int i, int j) { |
+ return ~(~((i * 781) ^ (j * 329))); |
+} |
+ |
+ |
+TEST(SplayTreeSimple) { |
+ static const int kLimit = 1000; |
+ ZoneScope zone_scope(DELETE_ON_EXIT); |
+ ZoneSplayTree<TestConfig> tree; |
+ std::set<int> seen; |
+#define CHECK_MAPS_EQUAL() do { \ |
+ for (int k = 0; k < kLimit; k++) \ |
+ CHECK_EQ(seen.find(k) != seen.end(), tree.Find(k, &loc)); \ |
+ } while (false) |
+ for (int i = 0; i < 50; i++) { |
+ for (int j = 0; j < 50; j++) { |
+ int next = PseudoRandom(i, j) % kLimit; |
+ if (seen.find(next) != seen.end()) { |
+ // We've already seen this one. Check the value and remove |
+ // it. |
+ ZoneSplayTree<TestConfig>::Locator loc; |
+ CHECK(tree.Find(next, &loc)); |
+ CHECK_EQ(next, loc.key()); |
+ CHECK_EQ(3 * next, loc.value()); |
+ tree.Remove(next); |
+ seen.erase(next); |
+ CHECK_MAPS_EQUAL(); |
+ } else { |
+ // Check that it wasn't there already and then add it. |
+ ZoneSplayTree<TestConfig>::Locator loc; |
+ CHECK(!tree.Find(next, &loc)); |
+ CHECK(tree.Insert(next, &loc)); |
+ CHECK_EQ(next, loc.key()); |
+ loc.set_value(3 * next); |
+ seen.insert(next); |
+ CHECK_MAPS_EQUAL(); |
+ } |
+ int val = PseudoRandom(j, i) % kLimit; |
+ for (int k = val; k >= 0; k--) { |
+ if (seen.find(val) != seen.end()) { |
+ ZoneSplayTree<TestConfig>::Locator loc; |
+ CHECK(tree.FindGreatestLessThan(val, &loc)); |
+ CHECK_EQ(loc.key(), val); |
+ break; |
+ } |
+ } |
+ val = PseudoRandom(i + j, i - j) % kLimit; |
+ for (int k = val; k < kLimit; k++) { |
+ if (seen.find(val) != seen.end()) { |
+ ZoneSplayTree<TestConfig>::Locator loc; |
+ CHECK(tree.FindLeastGreaterThan(val, &loc)); |
+ CHECK_EQ(loc.key(), val); |
+ break; |
+ } |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+TEST(DispatchTableConstruction) { |
+ // Initialize test data. |
+ static const int kLimit = 1000; |
+ static const int kRangeCount = 8; |
+ static const int kRangeSize = 16; |
+ uc16 ranges[kRangeCount][2 * kRangeSize]; |
+ for (int i = 0; i < kRangeCount; i++) { |
+ Vector<uc16> range(ranges[i], 2 * kRangeSize); |
+ for (int j = 0; j < 2 * kRangeSize; j++) { |
+ range[j] = PseudoRandom(i + 25, j + 87) % kLimit; |
+ } |
+ range.Sort(); |
+ for (int j = 1; j < 2 * kRangeSize; j++) { |
+ CHECK(range[j-1] <= range[j]); |
+ } |
+ } |
+ // Enter test data into dispatch table. |
+ ZoneScope zone_scope(DELETE_ON_EXIT); |
+ DispatchTable table; |
+ for (int i = 0; i < kRangeCount; i++) { |
+ uc16* range = ranges[i]; |
+ for (int j = 0; j < 2 * kRangeSize; j += 2) |
+ table.AddRange(CharacterRange(range[j], range[j + 1]), i); |
+ } |
+ // Check that the table looks as we would expect |
+ for (int p = 0; p < kLimit; p++) { |
+ OutSet* outs = table.Get(p); |
+ for (int j = 0; j < kRangeCount; j++) { |
+ uc16* range = ranges[j]; |
+ bool is_on = false; |
+ for (int k = 0; !is_on && (k < 2 * kRangeSize); k += 2) |
+ is_on = (range[k] <= p && p <= range[k + 1]); |
+ CHECK_EQ(is_on, outs->Get(j)); |
+ } |
+ } |
+} |
+ |
+ |
+TEST(Assembler) { |
+ V8::Initialize(NULL); |
+ byte codes[1024]; |
+ IrregexpAssembler assembler(Vector<byte>(codes, 1024)); |
+#define __ assembler. |
+ Label advance; |
+ Label look_for_foo; |
+ Label fail; |
+ __ GoTo(&look_for_foo); |
+ __ Bind(&advance); |
+ __ AdvanceCP(1); |
+ __ Bind(&look_for_foo); |
+ __ LoadCurrentChar(0, &fail); |
+ __ CheckNotCharacter('f', &advance); |
+ __ LoadCurrentChar(1, &fail); |
+ __ CheckNotCharacter('o', &advance); |
+ __ LoadCurrentChar(2, &fail); |
+ __ CheckNotCharacter('o', &advance); |
+ __ WriteCurrentPositionToRegister(0); |
+ __ WriteCurrentPositionToRegister(1, 2); |
+ __ Succeed(); |
+ __ Bind(&fail); |
+ __ Fail(); |
+ |
+ v8::HandleScope scope; |
+ Handle<ByteArray> array = Factory::NewByteArray(assembler.length()); |
+ assembler.Copy(array->GetDataStartAddress()); |
+ int captures[2]; |
+ |
+ Handle<String> f1 = |
+ Factory::NewStringFromAscii(CStrVector("Now is the time")); |
+ Handle<String> f1_16 = RegExpImpl::StringToTwoByte(f1); |
+ CHECK(!IrregexpInterpreter::Match(array, f1_16, captures, 0)); |
+ |
+ Handle<String> f2 = Factory::NewStringFromAscii(CStrVector("foo bar baz")); |
+ Handle<String> f2_16 = RegExpImpl::StringToTwoByte(f2); |
+ CHECK(IrregexpInterpreter::Match(array, f2_16, captures, 0)); |
+ CHECK_EQ(0, captures[0]); |
+ CHECK_EQ(2, captures[1]); |
+ |
+ Handle<String> f3 = Factory::NewStringFromAscii(CStrVector("tomfoolery")); |
+ Handle<String> f3_16 = RegExpImpl::StringToTwoByte(f3); |
+ CHECK(IrregexpInterpreter::Match(array, f3_16, captures, 0)); |
+ CHECK_EQ(3, captures[0]); |
+ CHECK_EQ(5, captures[1]); |
+} |
+ |
+ |
+TEST(Assembler2) { |
+ V8::Initialize(NULL); |
+ byte codes[1024]; |
+ IrregexpAssembler assembler(Vector<byte>(codes, 1024)); |
+#define __ assembler. |
+ // /^.*foo/ |
+ Label more_dots; |
+ Label unwind_dot; |
+ Label failure; |
+ Label foo; |
+ Label foo_failed; |
+ Label dot_match; |
+ // ^ |
+ __ PushCurrentPosition(); |
+ __ PushRegister(0); |
+ __ WriteCurrentPositionToRegister(0); |
+ __ PushBacktrack(&failure); |
+ __ GoTo(&dot_match); |
+ // .* |
+ __ Bind(&more_dots); |
+ __ AdvanceCP(1); |
+ __ Bind(&dot_match); |
+ __ PushCurrentPosition(); |
+ __ PushBacktrack(&unwind_dot); |
+ __ LoadCurrentChar(0, &foo); |
+ __ CheckNotCharacter('\n', &more_dots); |
+ // foo |
+ __ Bind(&foo); |
+ __ CheckNotCharacter('f', &foo_failed); |
+ __ LoadCurrentChar(1, &foo_failed); |
+ __ CheckNotCharacter('o', &foo_failed); |
+ __ LoadCurrentChar(2, &foo_failed); |
+ __ CheckNotCharacter('o', &foo_failed); |
+ __ WriteCurrentPositionToRegister(1, 2); |
+ __ Succeed(); |
+ __ Break(); |
+ |
+ __ Bind(&foo_failed); |
+ __ PopBacktrack(); |
+ __ Break(); |
+ |
+ __ Bind(&unwind_dot); |
+ __ PopCurrentPosition(); |
+ __ LoadCurrentChar(0, &foo_failed); |
+ __ GoTo(&foo); |
+ |
+ __ Bind(&failure); |
+ __ PopRegister(0); |
+ __ PopCurrentPosition(); |
+ __ Fail(); |
+ |
+ v8::HandleScope scope; |
+ Handle<ByteArray> array = Factory::NewByteArray(assembler.length()); |
+ assembler.Copy(array->GetDataStartAddress()); |
+ int captures[2]; |
+ |
+ Handle<String> f1 = |
+ Factory::NewStringFromAscii(CStrVector("Now is the time")); |
+ Handle<String> f1_16 = RegExpImpl::StringToTwoByte(f1); |
+ CHECK(!IrregexpInterpreter::Match(array, f1_16, captures, 0)); |
+ |
+ Handle<String> f2 = Factory::NewStringFromAscii(CStrVector("foo bar baz")); |
+ Handle<String> f2_16 = RegExpImpl::StringToTwoByte(f2); |
+ CHECK(IrregexpInterpreter::Match(array, f2_16, captures, 0)); |
+ CHECK_EQ(0, captures[0]); |
+ CHECK_EQ(2, captures[1]); |
+ |
+ Handle<String> f3 = Factory::NewStringFromAscii(CStrVector("tomfoolery")); |
+ Handle<String> f3_16 = RegExpImpl::StringToTwoByte(f3); |
+ CHECK(IrregexpInterpreter::Match(array, f3_16, captures, 0)); |
+ CHECK_EQ(0, captures[0]); |
+ CHECK_EQ(5, captures[1]); |
+ |
+ Handle<String> f4 = |
+ Factory::NewStringFromAscii(CStrVector("football buffoonery")); |
+ Handle<String> f4_16 = RegExpImpl::StringToTwoByte(f4); |
+ CHECK(IrregexpInterpreter::Match(array, f4_16, captures, 0)); |
+ CHECK_EQ(0, captures[0]); |
+ CHECK_EQ(14, captures[1]); |
+ |
+ Handle<String> f5 = |
+ Factory::NewStringFromAscii(CStrVector("walking\nbarefoot")); |
+ Handle<String> f5_16 = RegExpImpl::StringToTwoByte(f5); |
+ CHECK(!IrregexpInterpreter::Match(array, f5_16, captures, 0)); |
+} |
+ |
+ |
+TEST(MacroAssembler) { |
+ V8::Initialize(NULL); |
+ byte codes[1024]; |
+ IrregexpAssembler assembler(Vector<byte>(codes, 1024)); |
+ RegExpMacroAssemblerIrregexp m(&assembler); |
+ // ^f(o)o. |
+ Label fail, fail2, start; |
+ uc16 foo_chars[3]; |
+ foo_chars[0] = 'f'; |
+ foo_chars[1] = 'o'; |
+ foo_chars[2] = 'o'; |
+ Vector<const uc16> foo(foo_chars, 3); |
+ m.SetRegister(4, 42); |
+ m.PushRegister(4); |
+ m.AdvanceRegister(4, 42); |
+ m.GoTo(&start); |
+ m.Fail(); |
+ m.Bind(&start); |
+ m.PushBacktrack(&fail2); |
+ m.CheckCharacters(foo, 0, &fail); |
+ m.WriteCurrentPositionToRegister(0); |
+ m.PushCurrentPosition(); |
+ m.AdvanceCurrentPosition(3); |
+ m.WriteCurrentPositionToRegister(1); |
+ m.PopCurrentPosition(); |
+ m.AdvanceCurrentPosition(1); |
+ m.WriteCurrentPositionToRegister(2); |
+ m.AdvanceCurrentPosition(1); |
+ m.WriteCurrentPositionToRegister(3); |
+ m.Succeed(); |
+ |
+ m.Bind(&fail); |
+ m.Backtrack(); |
+ m.Succeed(); |
+ |
+ m.Bind(&fail2); |
+ m.PopRegister(0); |
+ m.Fail(); |
+ |
+ v8::HandleScope scope; |
+ |
+ Handle<ByteArray> array = Factory::NewByteArray(assembler.length()); |
+ assembler.Copy(array->GetDataStartAddress()); |
+ int captures[5]; |
+ |
+ Handle<String> f1 = |
+ Factory::NewStringFromAscii(CStrVector("foobar")); |
+ Handle<String> f1_16 = RegExpImpl::StringToTwoByte(f1); |
+ CHECK(IrregexpInterpreter::Match(array, f1_16, captures, 0)); |
+ CHECK_EQ(0, captures[0]); |
+ CHECK_EQ(3, captures[1]); |
+ CHECK_EQ(1, captures[2]); |
+ CHECK_EQ(2, captures[3]); |
+ CHECK_EQ(84, captures[4]); |
+ |
+ Handle<String> f2 = |
+ Factory::NewStringFromAscii(CStrVector("barfoo")); |
+ Handle<String> f2_16 = RegExpImpl::StringToTwoByte(f2); |
+ CHECK(!IrregexpInterpreter::Match(array, f2_16, captures, 0)); |
+ CHECK_EQ(42, captures[0]); |
+} |
+ |
+ |
+TEST(AddInverseToTable) { |
+ static const int kLimit = 1000; |
+ static const int kRangeCount = 16; |
+ for (int t = 0; t < 10; t++) { |
+ ZoneScope zone_scope(DELETE_ON_EXIT); |
+ ZoneList<CharacterRange>* ranges = |
+ new ZoneList<CharacterRange>(kRangeCount); |
+ for (int i = 0; i < kRangeCount; i++) { |
+ int from = PseudoRandom(t + 87, i + 25) % kLimit; |
+ int to = from + (PseudoRandom(i + 87, t + 25) % (kLimit / 20)); |
+ if (to > kLimit) to = kLimit; |
+ ranges->Add(CharacterRange(from, to)); |
+ } |
+ DispatchTable table; |
+ DispatchTableConstructor cons(&table); |
+ cons.set_choice_index(0); |
+ cons.AddInverse(ranges); |
+ for (int i = 0; i < kLimit; i++) { |
+ bool is_on = false; |
+ for (int j = 0; !is_on && j < kRangeCount; j++) |
+ is_on = ranges->at(j).Contains(i); |
+ OutSet* set = table.Get(i); |
+ CHECK_EQ(is_on, set->Get(0) == false); |
+ } |
+ } |
+ ZoneScope zone_scope(DELETE_ON_EXIT); |
+ ZoneList<CharacterRange>* ranges = |
+ new ZoneList<CharacterRange>(1); |
+ ranges->Add(CharacterRange(0xFFF0, 0xFFFE)); |
+ DispatchTable table; |
+ DispatchTableConstructor cons(&table); |
+ cons.set_choice_index(0); |
+ cons.AddInverse(ranges); |
+ CHECK(!table.Get(0xFFFE)->Get(0)); |
+ CHECK(table.Get(0xFFFF)->Get(0)); |
+} |
+ |
+ |
+static uc32 canonicalize(uc32 c) { |
+ unibrow::uchar canon[unibrow::Ecma262Canonicalize::kMaxWidth]; |
+ int count = unibrow::Ecma262Canonicalize::Convert(c, '\0', canon, NULL); |
+ if (count == 0) { |
+ return c; |
+ } else { |
+ CHECK_EQ(1, count); |
+ return canon[0]; |
+ } |
+} |
+ |
+ |
+TEST(LatinCanonicalize) { |
+ unibrow::Mapping<unibrow::Ecma262UnCanonicalize> un_canonicalize; |
+ for (char lower = 'a'; lower <= 'z'; lower++) { |
+ char upper = lower + ('A' - 'a'); |
+ CHECK_EQ(canonicalize(lower), canonicalize(upper)); |
+ unibrow::uchar uncanon[unibrow::Ecma262UnCanonicalize::kMaxWidth]; |
+ int length = un_canonicalize.get(lower, '\0', uncanon); |
+ CHECK_EQ(2, length); |
+ CHECK_EQ(upper, uncanon[0]); |
+ CHECK_EQ(lower, uncanon[1]); |
+ } |
+ for (uc32 c = 128; c < (1 << 21); c++) |
+ CHECK_GE(canonicalize(c), 128); |
+ unibrow::Mapping<unibrow::ToUppercase> to_upper; |
+ for (uc32 c = 0; c < (1 << 21); c++) { |
+ unibrow::uchar upper[unibrow::ToUppercase::kMaxWidth]; |
+ int length = to_upper.get(c, '\0', upper); |
+ if (length == 0) { |
+ length = 1; |
+ upper[0] = c; |
+ } |
+ uc32 u = upper[0]; |
+ if (length > 1 || (c >= 128 && u < 128)) |
+ u = c; |
+ CHECK_EQ(u, canonicalize(c)); |
+ } |
+} |
+ |
+ |
+TEST(SimplePropagation) { |
+ v8::HandleScope scope; |
+ ZoneScope zone_scope(DELETE_ON_EXIT); |
+ RegExpNode* node = Compile("(a|^b|c)"); |
+ CHECK(node->info()->determine_start); |
+} |
+ |
+ |
+static uc32 CanonRange(uc32 c) { |
+ unibrow::uchar canon[unibrow::CanonicalizationRange::kMaxWidth]; |
+ int count = unibrow::CanonicalizationRange::Convert(c, '\0', canon, NULL); |
+ if (count == 0) { |
+ return c; |
+ } else { |
+ CHECK_EQ(1, count); |
+ return canon[0]; |
+ } |
+} |
+ |
+ |
+TEST(RangeCanonicalization) { |
+ ASSERT((CanonRange(0) & CharacterRange::kStartMarker) != 0); |
+ // Check that we arrive at the same result when using the basic |
+ // range canonicalization primitives as when using immediate |
+ // canonicalization. |
+ unibrow::Mapping<unibrow::Ecma262UnCanonicalize> un_canonicalize; |
+ for (int i = 0; i < CharacterRange::kRangeCanonicalizeMax; i++) { |
+ int range = CanonRange(i); |
+ int indirect_length = 0; |
+ unibrow::uchar indirect[unibrow::Ecma262UnCanonicalize::kMaxWidth]; |
+ if ((range & CharacterRange::kStartMarker) == 0) { |
+ indirect_length = un_canonicalize.get(i - range, '\0', indirect); |
+ for (int i = 0; i < indirect_length; i++) |
+ indirect[i] += range; |
+ } else { |
+ indirect_length = un_canonicalize.get(i, '\0', indirect); |
+ } |
+ unibrow::uchar direct[unibrow::Ecma262UnCanonicalize::kMaxWidth]; |
+ int direct_length = un_canonicalize.get(i, '\0', direct); |
+ CHECK_EQ(direct_length, indirect_length); |
+ } |
+ // Check that we arrive at the same results when skipping over |
+ // canonicalization ranges. |
+ int next_block = 0; |
+ while (next_block < CharacterRange::kRangeCanonicalizeMax) { |
+ uc32 start = CanonRange(next_block); |
+ CHECK_NE((start & CharacterRange::kStartMarker), 0); |
+ unsigned dist = start & CharacterRange::kPayloadMask; |
+ unibrow::uchar first[unibrow::Ecma262UnCanonicalize::kMaxWidth]; |
+ int first_length = un_canonicalize.get(next_block, '\0', first); |
+ for (unsigned i = 1; i < dist; i++) { |
+ CHECK_EQ(i, CanonRange(i)); |
+ unibrow::uchar succ[unibrow::Ecma262UnCanonicalize::kMaxWidth]; |
+ int succ_length = un_canonicalize.get(next_block + i, '\0', succ); |
+ CHECK_EQ(first_length, succ_length); |
+ for (int j = 0; j < succ_length; j++) { |
+ int calc = first[j] + i; |
+ int found = succ[j]; |
+ CHECK_EQ(calc, found); |
+ } |
+ } |
+ next_block = next_block + dist; |
+ } |
+} |
+ |
+ |
+static void TestRangeCaseIndependence(CharacterRange input, |
+ Vector<CharacterRange> expected) { |
+ ZoneScope zone_scope(DELETE_ON_EXIT); |
+ int count = expected.length(); |
+ ZoneList<CharacterRange>* list = new ZoneList<CharacterRange>(count); |
+ input.AddCaseEquivalents(list); |
+ CHECK_EQ(count, list->length()); |
+ for (int i = 0; i < list->length(); i++) { |
+ CHECK_EQ(expected[i].from(), list->at(i).from()); |
+ CHECK_EQ(expected[i].to(), list->at(i).to()); |
+ } |
+} |
+ |
+ |
+static void TestSimpleRangeCaseIndependence(CharacterRange input, |
+ CharacterRange expected) { |
+ EmbeddedVector<CharacterRange, 1> vector; |
+ vector[0] = expected; |
+ TestRangeCaseIndependence(input, vector); |
+} |
+ |
+ |
+TEST(CharacterRangeCaseIndependence) { |
+ TestSimpleRangeCaseIndependence(CharacterRange::Singleton('a'), |
+ CharacterRange::Singleton('A')); |
+ TestSimpleRangeCaseIndependence(CharacterRange::Singleton('z'), |
+ CharacterRange::Singleton('Z')); |
+ TestSimpleRangeCaseIndependence(CharacterRange('a', 'z'), |
+ CharacterRange('A', 'Z')); |
+ TestSimpleRangeCaseIndependence(CharacterRange('c', 'f'), |
+ CharacterRange('C', 'F')); |
+ TestSimpleRangeCaseIndependence(CharacterRange('a', 'b'), |
+ CharacterRange('A', 'B')); |
+ TestSimpleRangeCaseIndependence(CharacterRange('y', 'z'), |
+ CharacterRange('Y', 'Z')); |
+ TestSimpleRangeCaseIndependence(CharacterRange('a' - 1, 'z' + 1), |
+ CharacterRange('A', 'Z')); |
+ TestSimpleRangeCaseIndependence(CharacterRange('A', 'Z'), |
+ CharacterRange('a', 'z')); |
+ TestSimpleRangeCaseIndependence(CharacterRange('C', 'F'), |
+ CharacterRange('c', 'f')); |
+ TestSimpleRangeCaseIndependence(CharacterRange('A' - 1, 'Z' + 1), |
+ CharacterRange('a', 'z')); |
+ // Here we need to add [l-z] to complete the case independence of |
+ // [A-Za-z] but we expect [a-z] to be added since we always add a |
+ // whole block at a time. |
+ TestSimpleRangeCaseIndependence(CharacterRange('A', 'k'), |
+ CharacterRange('a', 'z')); |
+} |
+ |
+ |
+TEST(Graph) { |
+ V8::Initialize(NULL); |
+ Execute("(x)?\\1y", "", true); |
+} |