mirror of
				https://github.com/tc39/test262.git
				synced 2025-10-31 03:34:08 +01:00 
			
		
		
		
	sourceRevisionAtLastExport: 33f2fb0e53d135f0ee17cfccd9d993eb2a6f47de targetRevisionAtLastExport: 31340cbd9add103f586d501b0c3354b7b182abc0
		
			
				
	
	
		
			811 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			811 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // Copyright 2012 the V8 project authors. All rights reserved.
 | |
| // 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.
 | |
| 
 | |
| function testEscape(str, regex) {
 | |
|   assertEquals("foo:bar:baz", str.split(regex).join(":"));
 | |
| }
 | |
| 
 | |
| testEscape("foo\nbar\nbaz", /\n/);
 | |
| testEscape("foo bar baz", /\s/);
 | |
| testEscape("foo\tbar\tbaz", /\s/);
 | |
| testEscape("foo-bar-baz", /\u002D/);
 | |
| 
 | |
| // Test containing null char in regexp.
 | |
| var s = '[' + String.fromCharCode(0) + ']';
 | |
| var re = new RegExp(s);
 | |
| assertEquals(s.match(re).length, 1);
 | |
| assertEquals(s.match(re)[0], String.fromCharCode(0));
 | |
| 
 | |
| // Test strings containing all line separators
 | |
| s = 'aA\nbB\rcC\r\ndD\u2028eE\u2029fF';
 | |
| re = /^./gm; // any non-newline character at the beginning of a line
 | |
| var result = s.match(re);
 | |
| assertEquals(result.length, 6);
 | |
| assertEquals(result[0], 'a');
 | |
| assertEquals(result[1], 'b');
 | |
| assertEquals(result[2], 'c');
 | |
| assertEquals(result[3], 'd');
 | |
| assertEquals(result[4], 'e');
 | |
| assertEquals(result[5], 'f');
 | |
| 
 | |
| re = /.$/gm; // any non-newline character at the end of a line
 | |
| result = s.match(re);
 | |
| assertEquals(result.length, 6);
 | |
| assertEquals(result[0], 'A');
 | |
| assertEquals(result[1], 'B');
 | |
| assertEquals(result[2], 'C');
 | |
| assertEquals(result[3], 'D');
 | |
| assertEquals(result[4], 'E');
 | |
| assertEquals(result[5], 'F');
 | |
| 
 | |
| re = /^[^]/gm; // *any* character at the beginning of a line
 | |
| result = s.match(re);
 | |
| assertEquals(result.length, 7);
 | |
| assertEquals(result[0], 'a');
 | |
| assertEquals(result[1], 'b');
 | |
| assertEquals(result[2], 'c');
 | |
| assertEquals(result[3], '\n');
 | |
| assertEquals(result[4], 'd');
 | |
| assertEquals(result[5], 'e');
 | |
| assertEquals(result[6], 'f');
 | |
| 
 | |
| re = /[^]$/gm; // *any* character at the end of a line
 | |
| result = s.match(re);
 | |
| assertEquals(result.length, 7);
 | |
| assertEquals(result[0], 'A');
 | |
| assertEquals(result[1], 'B');
 | |
| assertEquals(result[2], 'C');
 | |
| assertEquals(result[3], '\r');
 | |
| assertEquals(result[4], 'D');
 | |
| assertEquals(result[5], 'E');
 | |
| assertEquals(result[6], 'F');
 | |
| 
 | |
| // Some tests from the Mozilla tests, where our behavior used to differ from
 | |
| // SpiderMonkey.
 | |
| // From ecma_3/RegExp/regress-334158.js
 | |
| assertTrue(/\ca/.test( "\x01" ));
 | |
| assertFalse(/\ca/.test( "\\ca" ));
 | |
| assertFalse(/\ca/.test( "ca" ));
 | |
| assertTrue(/\c[a/]/.test( "\\ca" ));
 | |
| assertTrue(/\c[a/]/.test( "\\c/" ));
 | |
| 
 | |
| // Test \c in character class
 | |
| re = /^[\cM]$/;
 | |
| assertTrue(re.test("\r"));
 | |
| assertFalse(re.test("M"));
 | |
| assertFalse(re.test("c"));
 | |
| assertFalse(re.test("\\"));
 | |
| assertFalse(re.test("\x03"));  // I.e., read as \cc
 | |
| 
 | |
| re = /^[\c]]$/;
 | |
| assertTrue(re.test("c]"));
 | |
| assertTrue(re.test("\\]"));
 | |
| assertFalse(re.test("\x1d"));  // ']' & 0x1f
 | |
| assertFalse(re.test("\x03]"));  // I.e., read as \cc
 | |
| 
 | |
| re = /^[\c1]$/;  // Digit control characters are masked in character classes.
 | |
| assertTrue(re.test("\x11"));
 | |
| assertFalse(re.test("\\"));
 | |
| assertFalse(re.test("c"));
 | |
| assertFalse(re.test("1"));
 | |
| 
 | |
| re = /^[\c_]$/;  // Underscore control character is masked in character classes.
 | |
| assertTrue(re.test("\x1f"));
 | |
| assertFalse(re.test("\\"));
 | |
| assertFalse(re.test("c"));
 | |
| assertFalse(re.test("_"));
 | |
| 
 | |
| re = /^[\c$]$/;  // Other characters are interpreted literally.
 | |
| assertFalse(re.test("\x04"));
 | |
| assertTrue(re.test("\\"));
 | |
| assertTrue(re.test("c"));
 | |
| assertTrue(re.test("$"));
 | |
| 
 | |
| assertTrue(/^[Z-\c-e]*$/.test("Z[\\cde"));
 | |
| 
 | |
| // Test that we handle \s and \S correctly on special Unicode characters.
 | |
| re = /\s/;
 | |
| assertTrue(re.test("\u2028"));
 | |
| assertTrue(re.test("\u2029"));
 | |
| assertTrue(re.test("\uFEFF"));
 | |
| 
 | |
| re = /\S/;
 | |
| assertFalse(re.test("\u2028"));
 | |
| assertFalse(re.test("\u2029"));
 | |
| assertFalse(re.test("\uFEFF"));
 | |
| 
 | |
| // Test that we handle \s and \S correctly inside some bizarre
 | |
| // character classes.
 | |
| re = /[\s-:]/;
 | |
| assertTrue(re.test('-'));
 | |
| assertTrue(re.test(':'));
 | |
| assertTrue(re.test(' '));
 | |
| assertTrue(re.test('\t'));
 | |
| assertTrue(re.test('\n'));
 | |
| assertFalse(re.test('a'));
 | |
| assertFalse(re.test('Z'));
 | |
| 
 | |
| re = /[\S-:]/;
 | |
| assertTrue(re.test('-'));
 | |
| assertTrue(re.test(':'));
 | |
| assertFalse(re.test(' '));
 | |
| assertFalse(re.test('\t'));
 | |
| assertFalse(re.test('\n'));
 | |
| assertTrue(re.test('a'));
 | |
| assertTrue(re.test('Z'));
 | |
| 
 | |
| re = /[^\s-:]/;
 | |
| assertFalse(re.test('-'));
 | |
| assertFalse(re.test(':'));
 | |
| assertFalse(re.test(' '));
 | |
| assertFalse(re.test('\t'));
 | |
| assertFalse(re.test('\n'));
 | |
| assertTrue(re.test('a'));
 | |
| assertTrue(re.test('Z'));
 | |
| 
 | |
| re = /[^\S-:]/;
 | |
| assertFalse(re.test('-'));
 | |
| assertFalse(re.test(':'));
 | |
| assertTrue(re.test(' '));
 | |
| assertTrue(re.test('\t'));
 | |
| assertTrue(re.test('\n'));
 | |
| assertFalse(re.test('a'));
 | |
| assertFalse(re.test('Z'));
 | |
| 
 | |
| re = /[\s]/;
 | |
| assertFalse(re.test('-'));
 | |
| assertFalse(re.test(':'));
 | |
| assertTrue(re.test(' '));
 | |
| assertTrue(re.test('\t'));
 | |
| assertTrue(re.test('\n'));
 | |
| assertFalse(re.test('a'));
 | |
| assertFalse(re.test('Z'));
 | |
| 
 | |
| re = /[^\s]/;
 | |
| assertTrue(re.test('-'));
 | |
| assertTrue(re.test(':'));
 | |
| assertFalse(re.test(' '));
 | |
| assertFalse(re.test('\t'));
 | |
| assertFalse(re.test('\n'));
 | |
| assertTrue(re.test('a'));
 | |
| assertTrue(re.test('Z'));
 | |
| 
 | |
| re = /[\S]/;
 | |
| assertTrue(re.test('-'));
 | |
| assertTrue(re.test(':'));
 | |
| assertFalse(re.test(' '));
 | |
| assertFalse(re.test('\t'));
 | |
| assertFalse(re.test('\n'));
 | |
| assertTrue(re.test('a'));
 | |
| assertTrue(re.test('Z'));
 | |
| 
 | |
| re = /[^\S]/;
 | |
| assertFalse(re.test('-'));
 | |
| assertFalse(re.test(':'));
 | |
| assertTrue(re.test(' '));
 | |
| assertTrue(re.test('\t'));
 | |
| assertTrue(re.test('\n'));
 | |
| assertFalse(re.test('a'));
 | |
| assertFalse(re.test('Z'));
 | |
| 
 | |
| re = /[\s\S]/;
 | |
| assertTrue(re.test('-'));
 | |
| assertTrue(re.test(':'));
 | |
| assertTrue(re.test(' '));
 | |
| assertTrue(re.test('\t'));
 | |
| assertTrue(re.test('\n'));
 | |
| assertTrue(re.test('a'));
 | |
| assertTrue(re.test('Z'));
 | |
| 
 | |
| re = /[^\s\S]/;
 | |
| assertFalse(re.test('-'));
 | |
| assertFalse(re.test(':'));
 | |
| assertFalse(re.test(' '));
 | |
| assertFalse(re.test('\t'));
 | |
| assertFalse(re.test('\n'));
 | |
| assertFalse(re.test('a'));
 | |
| assertFalse(re.test('Z'));
 | |
| 
 | |
| // First - is treated as range operator, second as literal minus.
 | |
| // This follows the specification in parsing, but doesn't throw on
 | |
| // the \s at the beginning of the range.
 | |
| re = /[\s-0-9]/;
 | |
| assertTrue(re.test(' '));
 | |
| assertTrue(re.test('\xA0'));
 | |
| assertTrue(re.test('-'));
 | |
| assertTrue(re.test('0'));
 | |
| assertTrue(re.test('9'));
 | |
| assertFalse(re.test('1'));
 | |
| 
 | |
| // Test beginning and end of line assertions with or without the
 | |
| // multiline flag.
 | |
| re = /^\d+/;
 | |
| assertFalse(re.test("asdf\n123"));
 | |
| re = /^\d+/m;
 | |
| assertTrue(re.test("asdf\n123"));
 | |
| 
 | |
| re = /\d+$/;
 | |
| assertFalse(re.test("123\nasdf"));
 | |
| re = /\d+$/m;
 | |
| assertTrue(re.test("123\nasdf"));
 | |
| 
 | |
| // Test that empty matches are handled correctly for multiline global
 | |
| // regexps.
 | |
| re = /^(.*)/mg;
 | |
| assertEquals(3, "a\n\rb".match(re).length);
 | |
| assertEquals("*a\n*b\r*c\n*\r*d\r*\n*e", "a\nb\rc\n\rd\r\ne".replace(re, "*$1"));
 | |
| 
 | |
| // Test that empty matches advance one character
 | |
| re = new RegExp("", "g");
 | |
| assertEquals("xAx", "A".replace(re, "x"));
 | |
| assertEquals(3, String.fromCharCode(161).replace(re, "x").length);
 | |
| 
 | |
| // Test that we match the KJS behavior with regard to undefined constructor
 | |
| // arguments:
 | |
| re = new RegExp();
 | |
| // KJS actually shows this as '//'.  Here we match the Firefox behavior (ie,
 | |
| // giving a syntactically legal regexp literal).
 | |
| assertEquals('/(?:)/', re.toString());
 | |
| re = new RegExp(void 0);
 | |
| assertEquals('/(?:)/', re.toString());
 | |
| re.compile();
 | |
| assertEquals('/(?:)/', re.toString());
 | |
| re.compile(void 0);
 | |
| assertEquals('/(?:)/', re.toString());
 | |
| 
 | |
| 
 | |
| // Check for lazy RegExp literal creation
 | |
| function lazyLiteral(doit) {
 | |
|   if (doit) return "".replace(/foo(/gi, "");
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| assertTrue(lazyLiteral(false));
 | |
| assertThrows("lazyLiteral(true)");
 | |
| 
 | |
| // Check $01 and $10
 | |
| re = new RegExp("(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)");
 | |
| assertEquals("t", "123456789t".replace(re, "$10"), "$10");
 | |
| assertEquals("15", "123456789t".replace(re, "$15"), "$10");
 | |
| assertEquals("1", "123456789t".replace(re, "$01"), "$01");
 | |
| assertEquals("$001", "123456789t".replace(re, "$001"), "$001");
 | |
| re = new RegExp("foo(.)");
 | |
| assertEquals("bar$0", "foox".replace(re, "bar$0"), "$0");
 | |
| assertEquals("bar$00", "foox".replace(re, "bar$00"), "$00");
 | |
| assertEquals("bar$000", "foox".replace(re, "bar$000"), "$000");
 | |
| assertEquals("barx", "foox".replace(re, "bar$01"), "$01 2");
 | |
| assertEquals("barx5", "foox".replace(re, "bar$15"), "$15");
 | |
| 
 | |
| assertFalse(/()foo$\1/.test("football"), "football1");
 | |
| assertFalse(/foo$(?=ball)/.test("football"), "football2");
 | |
| assertFalse(/foo$(?!bar)/.test("football"), "football3");
 | |
| assertTrue(/()foo$\1/.test("foo"), "football4");
 | |
| assertTrue(/foo$(?=(ball)?)/.test("foo"), "football5");
 | |
| assertTrue(/()foo$(?!bar)/.test("foo"), "football6");
 | |
| assertFalse(/(x?)foo$\1/.test("football"), "football7");
 | |
| assertFalse(/foo$(?=ball)/.test("football"), "football8");
 | |
| assertFalse(/foo$(?!bar)/.test("football"), "football9");
 | |
| assertTrue(/(x?)foo$\1/.test("foo"), "football10");
 | |
| assertTrue(/foo$(?=(ball)?)/.test("foo"), "football11");
 | |
| assertTrue(/foo$(?!bar)/.test("foo"), "football12");
 | |
| 
 | |
| // Check that the back reference has two successors.  See
 | |
| // BackReferenceNode::PropagateForward.
 | |
| assertFalse(/f(o)\b\1/.test('foo'));
 | |
| assertTrue(/f(o)\B\1/.test('foo'));
 | |
| 
 | |
| // Back-reference, ignore case:
 | |
| // ASCII
 | |
| assertEquals("xaAx,a", String(/x(a)\1x/i.exec("xaAx")), "backref-ASCII");
 | |
| assertFalse(/x(...)\1/i.test("xaaaaa"), "backref-ASCII-short");
 | |
| assertTrue(/x((?:))\1\1x/i.test("xx"), "backref-ASCII-empty");
 | |
| assertTrue(/x(?:...|(...))\1x/i.test("xabcx"), "backref-ASCII-uncaptured");
 | |
| assertTrue(/x(?:...|(...))\1x/i.test("xabcABCx"), "backref-ASCII-backtrack");
 | |
| assertEquals("xaBcAbCABCx,aBc",
 | |
|              String(/x(...)\1\1x/i.exec("xaBcAbCABCx")),
 | |
|              "backref-ASCII-twice");
 | |
| 
 | |
| for (var i = 0; i < 128; i++) {
 | |
|   var testName = "backref-ASCII-char-" + i + "," + (i^0x20);
 | |
|   var test = /^(.)\1$/i.test(String.fromCharCode(i, i ^ 0x20))
 | |
|   var c = String.fromCharCode(i);
 | |
|   if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
 | |
|     assertTrue(test, testName);
 | |
|   } else {
 | |
|     assertFalse(test, testName);
 | |
|   }
 | |
| }
 | |
| 
 | |
| assertFalse(/f(o)$\1/.test('foo'), "backref detects at_end");
 | |
| 
 | |
| // Check decimal escapes doesn't overflow.
 | |
| // (Note: \214 is interpreted as octal).
 | |
| assertArrayEquals(["\x8c7483648"],
 | |
|                   /\2147483648/.exec("\x8c7483648"),
 | |
|                   "Overflow decimal escape");
 | |
| 
 | |
| 
 | |
| // Check numbers in quantifiers doesn't overflow and doesn't throw on
 | |
| // too large numbers.
 | |
| assertFalse(/a{111111111111111111111111111111111111111111111}/.test('b'),
 | |
|             "overlarge1");
 | |
| assertFalse(/a{999999999999999999999999999999999999999999999}/.test('b'),
 | |
|             "overlarge2");
 | |
| assertFalse(/a{1,111111111111111111111111111111111111111111111}/.test('b'),
 | |
|             "overlarge3");
 | |
| assertFalse(/a{1,999999999999999999999999999999999999999999999}/.test('b'),
 | |
|             "overlarge4");
 | |
| assertFalse(/a{2147483648}/.test('b'),
 | |
|             "overlarge5");
 | |
| assertFalse(/a{21474836471}/.test('b'),
 | |
|             "overlarge6");
 | |
| assertFalse(/a{1,2147483648}/.test('b'),
 | |
|             "overlarge7");
 | |
| assertFalse(/a{1,21474836471}/.test('b'),
 | |
|             "overlarge8");
 | |
| assertFalse(/a{2147483648,2147483648}/.test('b'),
 | |
|             "overlarge9");
 | |
| assertFalse(/a{21474836471,21474836471}/.test('b'),
 | |
|             "overlarge10");
 | |
| assertFalse(/a{2147483647}/.test('b'),
 | |
|             "overlarge11");
 | |
| assertFalse(/a{1,2147483647}/.test('b'),
 | |
|             "overlarge12");
 | |
| assertTrue(/a{1,2147483647}/.test('a'),
 | |
|             "overlarge13");
 | |
| assertFalse(/a{2147483647,2147483647}/.test('a'),
 | |
|             "overlarge14");
 | |
| 
 | |
| 
 | |
| // Check that we don't read past the end of the string.
 | |
| assertFalse(/f/.test('b'));
 | |
| assertFalse(/[abc]f/.test('x'));
 | |
| assertFalse(/[abc]f/.test('xa'));
 | |
| assertFalse(/[abc]</.test('x'));
 | |
| assertFalse(/[abc]</.test('xa'));
 | |
| assertFalse(/f/i.test('b'));
 | |
| assertFalse(/[abc]f/i.test('x'));
 | |
| assertFalse(/[abc]f/i.test('xa'));
 | |
| assertFalse(/[abc]</i.test('x'));
 | |
| assertFalse(/[abc]</i.test('xa'));
 | |
| assertFalse(/f[abc]/.test('x'));
 | |
| assertFalse(/f[abc]/.test('xa'));
 | |
| assertFalse(/<[abc]/.test('x'));
 | |
| assertFalse(/<[abc]/.test('xa'));
 | |
| assertFalse(/f[abc]/i.test('x'));
 | |
| assertFalse(/f[abc]/i.test('xa'));
 | |
| assertFalse(/<[abc]/i.test('x'));
 | |
| assertFalse(/<[abc]/i.test('xa'));
 | |
| 
 | |
| // Test that merging of quick test masks gets it right.
 | |
| assertFalse(/x([0-7]%%x|[0-6]%%y)/.test('x7%%y'), 'qt');
 | |
| assertFalse(/()x\1(y([0-7]%%%x|[0-6]%%%y)|dkjasldkas)/.test('xy7%%%y'), 'qt2');
 | |
| assertFalse(/()x\1(y([0-7]%%%x|[0-6]%%%y)|dkjasldkas)/.test('xy%%%y'), 'qt3');
 | |
| assertFalse(/()x\1y([0-7]%%%x|[0-6]%%%y)/.test('xy7%%%y'), 'qt4');
 | |
| assertFalse(/()x\1(y([0-7]%%%x|[0-6]%%%y)|dkjasldkas)/.test('xy%%%y'), 'qt5');
 | |
| assertFalse(/()x\1y([0-7]%%%x|[0-6]%%%y)/.test('xy7%%%y'), 'qt6');
 | |
| assertFalse(/xy([0-7]%%%x|[0-6]%%%y)/.test('xy7%%%y'), 'qt7');
 | |
| assertFalse(/x([0-7]%%%x|[0-6]%%%y)/.test('x7%%%y'), 'qt8');
 | |
| 
 | |
| 
 | |
| // Don't hang on this one.
 | |
| /[^\xfe-\xff]*/.test("");
 | |
| 
 | |
| 
 | |
| var long = "a";
 | |
| for (var i = 0; i < 100000; i++) {
 | |
|   long = "a?" + long;
 | |
| }
 | |
| // Don't crash on this one, but maybe throw an exception.
 | |
| try {
 | |
|   RegExp(long).exec("a");
 | |
| } catch (e) {
 | |
|   assertTrue(String(e).indexOf("Stack overflow") >= 0, "overflow");
 | |
| }
 | |
| 
 | |
| 
 | |
| // Test that compile works on modified objects
 | |
| var re = /re+/;
 | |
| assertEquals("re+", re.source);
 | |
| assertFalse(re.global);
 | |
| assertFalse(re.ignoreCase);
 | |
| assertFalse(re.multiline);
 | |
| assertEquals(0, re.lastIndex);
 | |
| 
 | |
| re.compile("ro+", "gim");
 | |
| assertEquals("ro+", re.source);
 | |
| assertTrue(re.global);
 | |
| assertTrue(re.ignoreCase);
 | |
| assertTrue(re.multiline);
 | |
| assertEquals(0, re.lastIndex);
 | |
| 
 | |
| re.lastIndex = 42;
 | |
| re.someOtherProperty = 42;
 | |
| re.someDeletableProperty = 42;
 | |
| re[37] = 37;
 | |
| re[42] = 42;
 | |
| 
 | |
| re.compile("ra+", "i");
 | |
| assertEquals("ra+", re.source);
 | |
| assertFalse(re.global);
 | |
| assertTrue(re.ignoreCase);
 | |
| assertFalse(re.multiline);
 | |
| assertEquals(0, re.lastIndex);
 | |
| 
 | |
| assertEquals(42, re.someOtherProperty);
 | |
| assertEquals(42, re.someDeletableProperty);
 | |
| assertEquals(37, re[37]);
 | |
| assertEquals(42, re[42]);
 | |
| 
 | |
| re.lastIndex = -1;
 | |
| re.someOtherProperty = 37;
 | |
| re[42] = 37;
 | |
| assertTrue(delete re[37]);
 | |
| assertTrue(delete re.someDeletableProperty);
 | |
| re.compile("ri+", "gm");
 | |
| 
 | |
| assertEquals("ri+", re.source);
 | |
| assertTrue(re.global);
 | |
| assertFalse(re.ignoreCase);
 | |
| assertTrue(re.multiline);
 | |
| assertEquals(0, re.lastIndex);
 | |
| assertEquals(37, re.someOtherProperty);
 | |
| assertEquals(37, re[42]);
 | |
| 
 | |
| // Test boundary-checks.
 | |
| function assertRegExpTest(re, input, test) {
 | |
|   assertEquals(test, re.test(input), "test:" + re + ":" + input);
 | |
| }
 | |
| 
 | |
| assertRegExpTest(/b\b/, "b", true);
 | |
| assertRegExpTest(/b\b$/, "b", true);
 | |
| assertRegExpTest(/\bb/, "b", true);
 | |
| assertRegExpTest(/^\bb/, "b", true);
 | |
| assertRegExpTest(/,\b/, ",", false);
 | |
| assertRegExpTest(/,\b$/, ",", false);
 | |
| assertRegExpTest(/\b,/, ",", false);
 | |
| assertRegExpTest(/^\b,/, ",", false);
 | |
| 
 | |
| assertRegExpTest(/b\B/, "b", false);
 | |
| assertRegExpTest(/b\B$/, "b", false);
 | |
| assertRegExpTest(/\Bb/, "b", false);
 | |
| assertRegExpTest(/^\Bb/, "b", false);
 | |
| assertRegExpTest(/,\B/, ",", true);
 | |
| assertRegExpTest(/,\B$/, ",", true);
 | |
| assertRegExpTest(/\B,/, ",", true);
 | |
| assertRegExpTest(/^\B,/, ",", true);
 | |
| 
 | |
| assertRegExpTest(/b\b/, "b,", true);
 | |
| assertRegExpTest(/b\b/, "ba", false);
 | |
| assertRegExpTest(/b\B/, "b,", false);
 | |
| assertRegExpTest(/b\B/, "ba", true);
 | |
| 
 | |
| assertRegExpTest(/b\Bb/, "bb", true);
 | |
| assertRegExpTest(/b\bb/, "bb", false);
 | |
| 
 | |
| assertRegExpTest(/b\b[,b]/, "bb", false);
 | |
| assertRegExpTest(/b\B[,b]/, "bb", true);
 | |
| assertRegExpTest(/b\b[,b]/, "b,", true);
 | |
| assertRegExpTest(/b\B[,b]/, "b,", false);
 | |
| 
 | |
| assertRegExpTest(/[,b]\bb/, "bb", false);
 | |
| assertRegExpTest(/[,b]\Bb/, "bb", true);
 | |
| assertRegExpTest(/[,b]\bb/, ",b", true);
 | |
| assertRegExpTest(/[,b]\Bb/, ",b", false);
 | |
| 
 | |
| assertRegExpTest(/[,b]\b[,b]/, "bb", false);
 | |
| assertRegExpTest(/[,b]\B[,b]/, "bb", true);
 | |
| assertRegExpTest(/[,b]\b[,b]/, ",b", true);
 | |
| assertRegExpTest(/[,b]\B[,b]/, ",b", false);
 | |
| assertRegExpTest(/[,b]\b[,b]/, "b,", true);
 | |
| assertRegExpTest(/[,b]\B[,b]/, "b,", false);
 | |
| 
 | |
| // Test that caching of result doesn't share result objects.
 | |
| // More iterations increases the chance of hitting a GC.
 | |
| for (var i = 0; i < 100; i++) {
 | |
|   var re = /x(y)z/;
 | |
|   var res = re.exec("axyzb");
 | |
|   assertTrue(!!res);
 | |
|   assertEquals(2, res.length);
 | |
|   assertEquals("xyz", res[0]);
 | |
|   assertEquals("y", res[1]);
 | |
|   assertEquals(1, res.index);
 | |
|   assertEquals("axyzb", res.input);
 | |
|   assertEquals(undefined, res.foobar);
 | |
| 
 | |
|   res.foobar = "Arglebargle";
 | |
|   res[3] = "Glopglyf";
 | |
|   assertEquals("Arglebargle", res.foobar);
 | |
| }
 | |
| 
 | |
| // Test that we perform the spec required conversions in the correct order.
 | |
| var log;
 | |
| var string = "the string";
 | |
| var fakeLastIndex = {
 | |
|       valueOf: function() {
 | |
|         log.push("li");
 | |
|         return 0;
 | |
|       }
 | |
|     };
 | |
| var fakeString = {
 | |
|       toString: function() {
 | |
|         log.push("ts");
 | |
|         return string;
 | |
|       },
 | |
|       length: 0
 | |
|     };
 | |
| 
 | |
| var re = /str/;
 | |
| log = [];
 | |
| re.lastIndex = fakeLastIndex;
 | |
| var result = re.exec(fakeString);
 | |
| assertEquals(["str"], result);
 | |
| assertEquals(["ts", "li"], log);
 | |
| 
 | |
| // Again, to check if caching interferes.
 | |
| log = [];
 | |
| re.lastIndex = fakeLastIndex;
 | |
| result = re.exec(fakeString);
 | |
| assertEquals(["str"], result);
 | |
| assertEquals(["ts", "li"], log);
 | |
| 
 | |
| // And one more time, just to be certain.
 | |
| log = [];
 | |
| re.lastIndex = fakeLastIndex;
 | |
| result = re.exec(fakeString);
 | |
| assertEquals(["str"], result);
 | |
| assertEquals(["ts", "li"], log);
 | |
| 
 | |
| // Now with a global regexp, where lastIndex is actually used.
 | |
| re = /str/g;
 | |
| log = [];
 | |
| re.lastIndex = fakeLastIndex;
 | |
| var result = re.exec(fakeString);
 | |
| assertEquals(["str"], result);
 | |
| assertEquals(["ts", "li"], log);
 | |
| 
 | |
| // Again, to check if caching interferes.
 | |
| log = [];
 | |
| re.lastIndex = fakeLastIndex;
 | |
| result = re.exec(fakeString);
 | |
| assertEquals(["str"], result);
 | |
| assertEquals(["ts", "li"], log);
 | |
| 
 | |
| // And one more time, just to be certain.
 | |
| log = [];
 | |
| re.lastIndex = fakeLastIndex;
 | |
| result = re.exec(fakeString);
 | |
| assertEquals(["str"], result);
 | |
| assertEquals(["ts", "li"], log);
 | |
| 
 | |
| 
 | |
| // Check that properties of RegExp have the correct permissions.
 | |
| var re = /x/g;
 | |
| var desc = Object.getOwnPropertyDescriptor(re.__proto__, "global");
 | |
| assertInstanceof(desc.get, Function);
 | |
| assertEquals(true, desc.configurable);
 | |
| assertEquals(false, desc.enumerable);
 | |
| 
 | |
| desc = Object.getOwnPropertyDescriptor(re.__proto__, "multiline");
 | |
| assertInstanceof(desc.get, Function);
 | |
| assertEquals(true, desc.configurable);
 | |
| assertEquals(false, desc.enumerable);
 | |
| 
 | |
| desc = Object.getOwnPropertyDescriptor(re.__proto__, "ignoreCase");
 | |
| assertInstanceof(desc.get, Function);
 | |
| assertEquals(true, desc.configurable);
 | |
| assertEquals(false, desc.enumerable);
 | |
| 
 | |
| desc = Object.getOwnPropertyDescriptor(re, "global");
 | |
| assertEquals(undefined, desc);
 | |
| 
 | |
| desc = Object.getOwnPropertyDescriptor(re, "multiline");
 | |
| assertEquals(undefined, desc);
 | |
| 
 | |
| desc = Object.getOwnPropertyDescriptor(re, "ignoreCase");
 | |
| assertEquals(undefined, desc);
 | |
| 
 | |
| desc = Object.getOwnPropertyDescriptor(re, "lastIndex");
 | |
| assertEquals(0, desc.value);
 | |
| assertEquals(false, desc.configurable);
 | |
| assertEquals(false, desc.enumerable);
 | |
| assertEquals(true, desc.writable);
 | |
| 
 | |
| 
 | |
| // Check that end-anchored regexps are optimized correctly.
 | |
| var re = /(?:a|bc)g$/;
 | |
| assertTrue(re.test("ag"));
 | |
| assertTrue(re.test("bcg"));
 | |
| assertTrue(re.test("abcg"));
 | |
| assertTrue(re.test("zimbag"));
 | |
| assertTrue(re.test("zimbcg"));
 | |
| 
 | |
| assertFalse(re.test("g"));
 | |
| assertFalse(re.test(""));
 | |
| 
 | |
| // Global regexp (non-zero start).
 | |
| var re = /(?:a|bc)g$/g;
 | |
| assertTrue(re.test("ag"));
 | |
| re.lastIndex = 1;  // Near start of string.
 | |
| assertTrue(re.test("zimbag"));
 | |
| re.lastIndex = 6;  // At end of string.
 | |
| assertFalse(re.test("zimbag"));
 | |
| re.lastIndex = 5;  // Near end of string.
 | |
| assertFalse(re.test("zimbag"));
 | |
| re.lastIndex = 4;
 | |
| assertTrue(re.test("zimbag"));
 | |
| 
 | |
| // Anchored at both ends.
 | |
| var re = /^(?:a|bc)g$/g;
 | |
| assertTrue(re.test("ag"));
 | |
| re.lastIndex = 1;
 | |
| assertFalse(re.test("ag"));
 | |
| re.lastIndex = 1;
 | |
| assertFalse(re.test("zag"));
 | |
| 
 | |
| // Long max_length of RegExp.
 | |
| var re = /VeryLongRegExp!{1,1000}$/;
 | |
| assertTrue(re.test("BahoolaVeryLongRegExp!!!!!!"));
 | |
| assertFalse(re.test("VeryLongRegExp"));
 | |
| assertFalse(re.test("!"));
 | |
| 
 | |
| // End anchor inside disjunction.
 | |
| var re = /(?:a$|bc$)/;
 | |
| assertTrue(re.test("a"));
 | |
| assertTrue(re.test("bc"));
 | |
| assertTrue(re.test("abc"));
 | |
| assertTrue(re.test("zimzamzumba"));
 | |
| assertTrue(re.test("zimzamzumbc"));
 | |
| assertFalse(re.test("c"));
 | |
| assertFalse(re.test(""));
 | |
| 
 | |
| // Only partially anchored.
 | |
| var re = /(?:a|bc$)/;
 | |
| assertTrue(re.test("a"));
 | |
| assertTrue(re.test("bc"));
 | |
| assertEquals(["a"], re.exec("abc"));
 | |
| assertEquals(4, re.exec("zimzamzumba").index);
 | |
| assertEquals(["bc"], re.exec("zimzomzumbc"));
 | |
| assertFalse(re.test("c"));
 | |
| assertFalse(re.test(""));
 | |
| 
 | |
| // Valid syntax in ES5.
 | |
| re = RegExp("(?:x)*");
 | |
| re = RegExp("(x)*");
 | |
| 
 | |
| // Syntax extension relative to ES5, for matching JSC (and ES3).
 | |
| // Shouldn't throw.
 | |
| re = RegExp("(?=x)*");
 | |
| re = RegExp("(?!x)*");
 | |
| 
 | |
| // Should throw. Shouldn't hit asserts in debug mode.
 | |
| assertThrows("RegExp('(*)')");
 | |
| assertThrows("RegExp('(?:*)')");
 | |
| assertThrows("RegExp('(?=*)')");
 | |
| assertThrows("RegExp('(?!*)')");
 | |
| 
 | |
| // Test trimmed regular expression for RegExp.test().
 | |
| assertTrue(/.*abc/.test("abc"));
 | |
| assertFalse(/.*\d+/.test("q"));
 | |
| 
 | |
| // Test that RegExp.prototype.toString() throws TypeError for
 | |
| // incompatible receivers (ES5 section 15.10.6 and 15.10.6.4).
 | |
| assertThrows("RegExp.prototype.toString.call(null)", TypeError);
 | |
| assertThrows("RegExp.prototype.toString.call(0)", TypeError);
 | |
| assertThrows("RegExp.prototype.toString.call('')", TypeError);
 | |
| assertThrows("RegExp.prototype.toString.call(false)", TypeError);
 | |
| assertThrows("RegExp.prototype.toString.call(true)", TypeError);
 | |
| 
 | |
| // Test mutually recursive capture and backreferences.
 | |
| assertEquals(["b", "", ""], /(\2)b(\1)/.exec("aba"));
 | |
| assertEquals(["a", "", ""], /(\2).(\1)/.exec("aba"));
 | |
| assertEquals(["aba", "a", "a"], /(.\2).(\1)/.exec("aba"));
 | |
| assertEquals(["acbc", "c", "c"], /a(.\2)b(\1)$/.exec("acbc"));
 | |
| assertEquals(["acbc", "c", "c"], /a(.\2)b(\1)/.exec("aabcacbc"));
 | |
| 
 | |
| // Test surrogate pair detection in split.
 | |
| // \u{daff}\u{e000} is not a surrogate pair, while \u{daff}\u{dfff} is.
 | |
| assertEquals(["\u{daff}", "\u{e000}"], "\u{daff}\u{e000}".split(/[a-z]{0,1}/u));
 | |
| assertEquals(["\u{daff}\u{dfff}"], "\u{daff}\u{dfff}".split(/[a-z]{0,1}/u));
 | |
| 
 | |
| // Test that changing a property on RegExp.prototype results in us taking the
 | |
| // slow path, which executes RegExp.prototype.exec instead of our
 | |
| // RegExpExecStub.
 | |
| const RegExpPrototypeExec = RegExp.prototype.exec;
 | |
| RegExp.prototype.exec = function() { throw new Error(); }
 | |
| assertThrows(() => "abc".replace(/./, ""));
 | |
| RegExp.prototype.exec = RegExpPrototypeExec;
 | |
| 
 | |
| // Test the code path in RE.proto[@@search] when previousLastIndex is a receiver
 | |
| // but can't be converted to a primitive. This exposed a crash in an older
 | |
| // C++ implementation of @@search which a) still relied on Object::Equals,
 | |
| // and b) incorrectly returned isolate->pending_exception() on error.
 | |
| 
 | |
| var re = /./;
 | |
| re.lastIndex = { [Symbol.toPrimitive]: 42 };
 | |
| try { "abc".search(re); } catch (_) {}  // Ensure we don't crash.
 | |
| 
 | |
| // Test lastIndex values of -0.0 and NaN (since @@search uses SameValue).
 | |
| 
 | |
| var re = /./;
 | |
| re.exec = function(str) { assertEquals(0, re.lastIndex); return []; }
 | |
| re.lastIndex = -0.0;
 | |
| assertEquals(-0, re.lastIndex);
 | |
| "abc".search(re);
 | |
| assertEquals(-0, re.lastIndex);
 | |
| 
 | |
| var re = /./;
 | |
| re.exec = function(str) { assertEquals(0, re.lastIndex); return []; }
 | |
| re.lastIndex = NaN;
 | |
| assertEquals(NaN, re.lastIndex);
 | |
| "abc".search(re);
 | |
| assertEquals(NaN, re.lastIndex);
 | |
| 
 | |
| // Annex B changes: https://github.com/tc39/ecma262/pull/303
 | |
| 
 | |
| assertThrows("/{1}/", SyntaxError);
 | |
| assertTrue(/^{*$/.test("{{{"));
 | |
| assertTrue(/^}*$/.test("}}}"));
 | |
| assertTrue(/]/.test("]"));
 | |
| assertTrue(/^\c%$/.test("\\c%"));   // We go into ExtendedPatternCharacter.
 | |
| assertTrue(/^\d%$/.test("2%"));     // ... CharacterClassEscape.
 | |
| assertTrue(/^\e%$/.test("e%"));     // ... IdentityEscape.
 | |
| assertTrue(/^\ca$/.test("\u{1}"));  // ... ControlLetter.
 | |
| assertTrue(/^\cA$/.test("\u{1}"));  // ... ControlLetter.
 | |
| assertTrue(/^\c9$/.test("\\c9"));   // ... ExtendedPatternCharacter.
 | |
| assertTrue(/^\c$/.test("\\c"));   // ... ExtendedPatternCharacter.
 | |
| assertTrue(/^[\c%]*$/.test("\\c%"));  // TODO(v8:6201): Not covered by the spec.
 | |
| assertTrue(/^[\c:]*$/.test("\\c:"));  // TODO(v8:6201): Not covered by the spec.
 | |
| assertTrue(/^[\c0]*$/.test("\u{10}"));  // ... ClassControlLetter.
 | |
| assertTrue(/^[\c1]*$/.test("\u{11}"));  // ('0' % 32 == 0x10)
 | |
| assertTrue(/^[\c2]*$/.test("\u{12}"));
 | |
| assertTrue(/^[\c3]*$/.test("\u{13}"));
 | |
| assertTrue(/^[\c4]*$/.test("\u{14}"));
 | |
| assertTrue(/^[\c5]*$/.test("\u{15}"));
 | |
| assertTrue(/^[\c6]*$/.test("\u{16}"));
 | |
| assertTrue(/^[\c7]*$/.test("\u{17}"));
 | |
| assertTrue(/^[\c8]*$/.test("\u{18}"));
 | |
| assertTrue(/^[\c9]*$/.test("\u{19}"));
 | |
| assertTrue(/^[\c_]*$/.test("\u{1F}"));
 | |
| assertTrue(/^[\c11]*$/.test("\u{11}1"));
 | |
| assertTrue(/^[\8]*$/.test("8"));  // ... ClassEscape ~~> IdentityEscape.
 | |
| assertTrue(/^[\7]*$/.test("\u{7}"));  // ... ClassEscape
 | |
|                                       // ~~> LegacyOctalEscapeSequence.
 | |
| assertTrue(/^[\11]*$/.test("\u{9}"));
 | |
| assertTrue(/^[\111]*$/.test("\u{49}"));
 | |
| assertTrue(/^[\222]*$/.test("\u{92}"));
 | |
| assertTrue(/^[\333]*$/.test("\u{DB}"));
 | |
| assertTrue(/^[\444]*$/.test("\u{24}4"));
 | |
| assertTrue(/^[\d-X]*$/.test("234-X-432"));  // CharacterRangeOrUnion.
 | |
| assertTrue(/^[\d-X-Z]*$/.test("234-XZ-432"));
 | |
| assertFalse(/^[\d-X-Z]*$/.test("234-XYZ-432"));
 | |
| 
 | |
| // Lone leading surrogates. Just here to exercise specific parsing code-paths.
 | |
| 
 | |
| assertFalse(/\uDB88|\uDBEC|aa/.test(""));
 | |
| assertFalse(/\uDB88|\uDBEC|aa/u.test(""));
 |