1// Copyright 2012 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28function testEscape(str, regex) { 29 assertEquals("foo:bar:baz", str.split(regex).join(":")); 30} 31 32testEscape("foo\nbar\nbaz", /\n/); 33testEscape("foo bar baz", /\s/); 34testEscape("foo\tbar\tbaz", /\s/); 35testEscape("foo-bar-baz", /\u002D/); 36 37// Test containing null char in regexp. 38var s = '[' + String.fromCharCode(0) + ']'; 39var re = new RegExp(s); 40assertEquals(s.match(re).length, 1); 41assertEquals(s.match(re)[0], String.fromCharCode(0)); 42 43// Test strings containing all line separators 44s = 'aA\nbB\rcC\r\ndD\u2028eE\u2029fF'; 45re = /^./gm; // any non-newline character at the beginning of a line 46var result = s.match(re); 47assertEquals(result.length, 6); 48assertEquals(result[0], 'a'); 49assertEquals(result[1], 'b'); 50assertEquals(result[2], 'c'); 51assertEquals(result[3], 'd'); 52assertEquals(result[4], 'e'); 53assertEquals(result[5], 'f'); 54 55re = /.$/gm; // any non-newline character at the end of a line 56result = s.match(re); 57assertEquals(result.length, 6); 58assertEquals(result[0], 'A'); 59assertEquals(result[1], 'B'); 60assertEquals(result[2], 'C'); 61assertEquals(result[3], 'D'); 62assertEquals(result[4], 'E'); 63assertEquals(result[5], 'F'); 64 65re = /^[^]/gm; // *any* character at the beginning of a line 66result = s.match(re); 67assertEquals(result.length, 7); 68assertEquals(result[0], 'a'); 69assertEquals(result[1], 'b'); 70assertEquals(result[2], 'c'); 71assertEquals(result[3], '\n'); 72assertEquals(result[4], 'd'); 73assertEquals(result[5], 'e'); 74assertEquals(result[6], 'f'); 75 76re = /[^]$/gm; // *any* character at the end of a line 77result = s.match(re); 78assertEquals(result.length, 7); 79assertEquals(result[0], 'A'); 80assertEquals(result[1], 'B'); 81assertEquals(result[2], 'C'); 82assertEquals(result[3], '\r'); 83assertEquals(result[4], 'D'); 84assertEquals(result[5], 'E'); 85assertEquals(result[6], 'F'); 86 87// Some tests from the Mozilla tests, where our behavior used to differ from 88// SpiderMonkey. 89// From ecma_3/RegExp/regress-334158.js 90assertTrue(/\ca/.test( "\x01" )); 91assertFalse(/\ca/.test( "\\ca" )); 92assertFalse(/\ca/.test( "ca" )); 93assertTrue(/\c[a/]/.test( "\\ca" )); 94assertTrue(/\c[a/]/.test( "\\c/" )); 95 96// Test \c in character class 97re = /^[\cM]$/; 98assertTrue(re.test("\r")); 99assertFalse(re.test("M")); 100assertFalse(re.test("c")); 101assertFalse(re.test("\\")); 102assertFalse(re.test("\x03")); // I.e., read as \cc 103 104re = /^[\c]]$/; 105assertTrue(re.test("c]")); 106assertTrue(re.test("\\]")); 107assertFalse(re.test("\x1d")); // ']' & 0x1f 108assertFalse(re.test("\x03]")); // I.e., read as \cc 109 110re = /^[\c1]$/; // Digit control characters are masked in character classes. 111assertTrue(re.test("\x11")); 112assertFalse(re.test("\\")); 113assertFalse(re.test("c")); 114assertFalse(re.test("1")); 115 116re = /^[\c_]$/; // Underscore control character is masked in character classes. 117assertTrue(re.test("\x1f")); 118assertFalse(re.test("\\")); 119assertFalse(re.test("c")); 120assertFalse(re.test("_")); 121 122re = /^[\c$]$/; // Other characters are interpreted literally. 123assertFalse(re.test("\x04")); 124assertTrue(re.test("\\")); 125assertTrue(re.test("c")); 126assertTrue(re.test("$")); 127 128assertTrue(/^[Z-\c-e]*$/.test("Z[\\cde")); 129 130// Test that we handle \s and \S correctly on special Unicode characters. 131re = /\s/; 132assertTrue(re.test("\u2028")); 133assertTrue(re.test("\u2029")); 134assertTrue(re.test("\uFEFF")); 135 136re = /\S/; 137assertFalse(re.test("\u2028")); 138assertFalse(re.test("\u2029")); 139assertFalse(re.test("\uFEFF")); 140 141// Test that we handle \s and \S correctly inside some bizarre 142// character classes. 143re = /[\s-:]/; 144assertTrue(re.test('-')); 145assertTrue(re.test(':')); 146assertTrue(re.test(' ')); 147assertTrue(re.test('\t')); 148assertTrue(re.test('\n')); 149assertFalse(re.test('a')); 150assertFalse(re.test('Z')); 151 152re = /[\S-:]/; 153assertTrue(re.test('-')); 154assertTrue(re.test(':')); 155assertFalse(re.test(' ')); 156assertFalse(re.test('\t')); 157assertFalse(re.test('\n')); 158assertTrue(re.test('a')); 159assertTrue(re.test('Z')); 160 161re = /[^\s-:]/; 162assertFalse(re.test('-')); 163assertFalse(re.test(':')); 164assertFalse(re.test(' ')); 165assertFalse(re.test('\t')); 166assertFalse(re.test('\n')); 167assertTrue(re.test('a')); 168assertTrue(re.test('Z')); 169 170re = /[^\S-:]/; 171assertFalse(re.test('-')); 172assertFalse(re.test(':')); 173assertTrue(re.test(' ')); 174assertTrue(re.test('\t')); 175assertTrue(re.test('\n')); 176assertFalse(re.test('a')); 177assertFalse(re.test('Z')); 178 179re = /[\s]/; 180assertFalse(re.test('-')); 181assertFalse(re.test(':')); 182assertTrue(re.test(' ')); 183assertTrue(re.test('\t')); 184assertTrue(re.test('\n')); 185assertFalse(re.test('a')); 186assertFalse(re.test('Z')); 187 188re = /[^\s]/; 189assertTrue(re.test('-')); 190assertTrue(re.test(':')); 191assertFalse(re.test(' ')); 192assertFalse(re.test('\t')); 193assertFalse(re.test('\n')); 194assertTrue(re.test('a')); 195assertTrue(re.test('Z')); 196 197re = /[\S]/; 198assertTrue(re.test('-')); 199assertTrue(re.test(':')); 200assertFalse(re.test(' ')); 201assertFalse(re.test('\t')); 202assertFalse(re.test('\n')); 203assertTrue(re.test('a')); 204assertTrue(re.test('Z')); 205 206re = /[^\S]/; 207assertFalse(re.test('-')); 208assertFalse(re.test(':')); 209assertTrue(re.test(' ')); 210assertTrue(re.test('\t')); 211assertTrue(re.test('\n')); 212assertFalse(re.test('a')); 213assertFalse(re.test('Z')); 214 215re = /[\s\S]/; 216assertTrue(re.test('-')); 217assertTrue(re.test(':')); 218assertTrue(re.test(' ')); 219assertTrue(re.test('\t')); 220assertTrue(re.test('\n')); 221assertTrue(re.test('a')); 222assertTrue(re.test('Z')); 223 224re = /[^\s\S]/; 225assertFalse(re.test('-')); 226assertFalse(re.test(':')); 227assertFalse(re.test(' ')); 228assertFalse(re.test('\t')); 229assertFalse(re.test('\n')); 230assertFalse(re.test('a')); 231assertFalse(re.test('Z')); 232 233// First - is treated as range operator, second as literal minus. 234// This follows the specification in parsing, but doesn't throw on 235// the \s at the beginning of the range. 236re = /[\s-0-9]/; 237assertTrue(re.test(' ')); 238assertTrue(re.test('\xA0')); 239assertTrue(re.test('-')); 240assertTrue(re.test('0')); 241assertTrue(re.test('9')); 242assertFalse(re.test('1')); 243 244// Test beginning and end of line assertions with or without the 245// multiline flag. 246re = /^\d+/; 247assertFalse(re.test("asdf\n123")); 248re = /^\d+/m; 249assertTrue(re.test("asdf\n123")); 250 251re = /\d+$/; 252assertFalse(re.test("123\nasdf")); 253re = /\d+$/m; 254assertTrue(re.test("123\nasdf")); 255 256// Test that empty matches are handled correctly for multiline global 257// regexps. 258re = /^(.*)/mg; 259assertEquals(3, "a\n\rb".match(re).length); 260assertEquals("*a\n*b\r*c\n*\r*d\r*\n*e", "a\nb\rc\n\rd\r\ne".replace(re, "*$1")); 261 262// Test that empty matches advance one character 263re = new RegExp("", "g"); 264assertEquals("xAx", "A".replace(re, "x")); 265assertEquals(3, String.fromCharCode(161).replace(re, "x").length); 266 267// Test that we match the KJS behavior with regard to undefined constructor 268// arguments: 269re = new RegExp(); 270// KJS actually shows this as '//'. Here we match the Firefox behavior (ie, 271// giving a syntactically legal regexp literal). 272assertEquals('/(?:)/', re.toString()); 273re = new RegExp(void 0); 274assertEquals('/(?:)/', re.toString()); 275re.compile(); 276assertEquals('/(?:)/', re.toString()); 277re.compile(void 0); 278assertEquals('/undefined/', re.toString()); 279 280 281// Check for lazy RegExp literal creation 282function lazyLiteral(doit) { 283 if (doit) return "".replace(/foo(/gi, ""); 284 return true; 285} 286 287assertTrue(lazyLiteral(false)); 288assertThrows("lazyLiteral(true)"); 289 290// Check $01 and $10 291re = new RegExp("(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)"); 292assertEquals("t", "123456789t".replace(re, "$10"), "$10"); 293assertEquals("15", "123456789t".replace(re, "$15"), "$10"); 294assertEquals("1", "123456789t".replace(re, "$01"), "$01"); 295assertEquals("$001", "123456789t".replace(re, "$001"), "$001"); 296re = new RegExp("foo(.)"); 297assertEquals("bar$0", "foox".replace(re, "bar$0"), "$0"); 298assertEquals("bar$00", "foox".replace(re, "bar$00"), "$00"); 299assertEquals("bar$000", "foox".replace(re, "bar$000"), "$000"); 300assertEquals("barx", "foox".replace(re, "bar$01"), "$01 2"); 301assertEquals("barx5", "foox".replace(re, "bar$15"), "$15"); 302 303assertFalse(/()foo$\1/.test("football"), "football1"); 304assertFalse(/foo$(?=ball)/.test("football"), "football2"); 305assertFalse(/foo$(?!bar)/.test("football"), "football3"); 306assertTrue(/()foo$\1/.test("foo"), "football4"); 307assertTrue(/foo$(?=(ball)?)/.test("foo"), "football5"); 308assertTrue(/()foo$(?!bar)/.test("foo"), "football6"); 309assertFalse(/(x?)foo$\1/.test("football"), "football7"); 310assertFalse(/foo$(?=ball)/.test("football"), "football8"); 311assertFalse(/foo$(?!bar)/.test("football"), "football9"); 312assertTrue(/(x?)foo$\1/.test("foo"), "football10"); 313assertTrue(/foo$(?=(ball)?)/.test("foo"), "football11"); 314assertTrue(/foo$(?!bar)/.test("foo"), "football12"); 315 316// Check that the back reference has two successors. See 317// BackReferenceNode::PropagateForward. 318assertFalse(/f(o)\b\1/.test('foo')); 319assertTrue(/f(o)\B\1/.test('foo')); 320 321// Back-reference, ignore case: 322// ASCII 323assertEquals("xaAx,a", String(/x(a)\1x/i.exec("xaAx")), "backref-ASCII"); 324assertFalse(/x(...)\1/i.test("xaaaaa"), "backref-ASCII-short"); 325assertTrue(/x((?:))\1\1x/i.test("xx"), "backref-ASCII-empty"); 326assertTrue(/x(?:...|(...))\1x/i.test("xabcx"), "backref-ASCII-uncaptured"); 327assertTrue(/x(?:...|(...))\1x/i.test("xabcABCx"), "backref-ASCII-backtrack"); 328assertEquals("xaBcAbCABCx,aBc", 329 String(/x(...)\1\1x/i.exec("xaBcAbCABCx")), 330 "backref-ASCII-twice"); 331 332for (var i = 0; i < 128; i++) { 333 var testName = "backref-ASCII-char-" + i + "," + (i^0x20); 334 var test = /^(.)\1$/i.test(String.fromCharCode(i, i ^ 0x20)) 335 var c = String.fromCharCode(i); 336 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) { 337 assertTrue(test, testName); 338 } else { 339 assertFalse(test, testName); 340 } 341} 342 343assertFalse(/f(o)$\1/.test('foo'), "backref detects at_end"); 344 345// Check decimal escapes doesn't overflow. 346// (Note: \214 is interpreted as octal). 347assertArrayEquals(["\x8c7483648"], 348 /\2147483648/.exec("\x8c7483648"), 349 "Overflow decimal escape"); 350 351 352// Check numbers in quantifiers doesn't overflow and doesn't throw on 353// too large numbers. 354assertFalse(/a{111111111111111111111111111111111111111111111}/.test('b'), 355 "overlarge1"); 356assertFalse(/a{999999999999999999999999999999999999999999999}/.test('b'), 357 "overlarge2"); 358assertFalse(/a{1,111111111111111111111111111111111111111111111}/.test('b'), 359 "overlarge3"); 360assertFalse(/a{1,999999999999999999999999999999999999999999999}/.test('b'), 361 "overlarge4"); 362assertFalse(/a{2147483648}/.test('b'), 363 "overlarge5"); 364assertFalse(/a{21474836471}/.test('b'), 365 "overlarge6"); 366assertFalse(/a{1,2147483648}/.test('b'), 367 "overlarge7"); 368assertFalse(/a{1,21474836471}/.test('b'), 369 "overlarge8"); 370assertFalse(/a{2147483648,2147483648}/.test('b'), 371 "overlarge9"); 372assertFalse(/a{21474836471,21474836471}/.test('b'), 373 "overlarge10"); 374assertFalse(/a{2147483647}/.test('b'), 375 "overlarge11"); 376assertFalse(/a{1,2147483647}/.test('b'), 377 "overlarge12"); 378assertTrue(/a{1,2147483647}/.test('a'), 379 "overlarge13"); 380assertFalse(/a{2147483647,2147483647}/.test('a'), 381 "overlarge14"); 382 383 384// Check that we don't read past the end of the string. 385assertFalse(/f/.test('b')); 386assertFalse(/[abc]f/.test('x')); 387assertFalse(/[abc]f/.test('xa')); 388assertFalse(/[abc]</.test('x')); 389assertFalse(/[abc]</.test('xa')); 390assertFalse(/f/i.test('b')); 391assertFalse(/[abc]f/i.test('x')); 392assertFalse(/[abc]f/i.test('xa')); 393assertFalse(/[abc]</i.test('x')); 394assertFalse(/[abc]</i.test('xa')); 395assertFalse(/f[abc]/.test('x')); 396assertFalse(/f[abc]/.test('xa')); 397assertFalse(/<[abc]/.test('x')); 398assertFalse(/<[abc]/.test('xa')); 399assertFalse(/f[abc]/i.test('x')); 400assertFalse(/f[abc]/i.test('xa')); 401assertFalse(/<[abc]/i.test('x')); 402assertFalse(/<[abc]/i.test('xa')); 403 404// Test that merging of quick test masks gets it right. 405assertFalse(/x([0-7]%%x|[0-6]%%y)/.test('x7%%y'), 'qt'); 406assertFalse(/()x\1(y([0-7]%%%x|[0-6]%%%y)|dkjasldkas)/.test('xy7%%%y'), 'qt2'); 407assertFalse(/()x\1(y([0-7]%%%x|[0-6]%%%y)|dkjasldkas)/.test('xy%%%y'), 'qt3'); 408assertFalse(/()x\1y([0-7]%%%x|[0-6]%%%y)/.test('xy7%%%y'), 'qt4'); 409assertFalse(/()x\1(y([0-7]%%%x|[0-6]%%%y)|dkjasldkas)/.test('xy%%%y'), 'qt5'); 410assertFalse(/()x\1y([0-7]%%%x|[0-6]%%%y)/.test('xy7%%%y'), 'qt6'); 411assertFalse(/xy([0-7]%%%x|[0-6]%%%y)/.test('xy7%%%y'), 'qt7'); 412assertFalse(/x([0-7]%%%x|[0-6]%%%y)/.test('x7%%%y'), 'qt8'); 413 414 415// Don't hang on this one. 416/[^\xfe-\xff]*/.test(""); 417 418 419var long = "a"; 420for (var i = 0; i < 100000; i++) { 421 long = "a?" + long; 422} 423// Don't crash on this one, but maybe throw an exception. 424try { 425 RegExp(long).exec("a"); 426} catch (e) { 427 assertTrue(String(e).indexOf("Stack overflow") >= 0, "overflow"); 428} 429 430 431// Test that compile works on modified objects 432var re = /re+/; 433assertEquals("re+", re.source); 434assertFalse(re.global); 435assertFalse(re.ignoreCase); 436assertFalse(re.multiline); 437assertEquals(0, re.lastIndex); 438 439re.compile("ro+", "gim"); 440assertEquals("ro+", re.source); 441assertTrue(re.global); 442assertTrue(re.ignoreCase); 443assertTrue(re.multiline); 444assertEquals(0, re.lastIndex); 445 446re.lastIndex = 42; 447re.someOtherProperty = 42; 448re.someDeletableProperty = 42; 449re[37] = 37; 450re[42] = 42; 451 452re.compile("ra+", "i"); 453assertEquals("ra+", re.source); 454assertFalse(re.global); 455assertTrue(re.ignoreCase); 456assertFalse(re.multiline); 457assertEquals(0, re.lastIndex); 458 459assertEquals(42, re.someOtherProperty); 460assertEquals(42, re.someDeletableProperty); 461assertEquals(37, re[37]); 462assertEquals(42, re[42]); 463 464re.lastIndex = -1; 465re.someOtherProperty = 37; 466re[42] = 37; 467assertTrue(delete re[37]); 468assertTrue(delete re.someDeletableProperty); 469re.compile("ri+", "gm"); 470 471assertEquals("ri+", re.source); 472assertTrue(re.global); 473assertFalse(re.ignoreCase); 474assertTrue(re.multiline); 475assertEquals(0, re.lastIndex); 476assertEquals(37, re.someOtherProperty); 477assertEquals(37, re[42]); 478 479// Test boundary-checks. 480function assertRegExpTest(re, input, test) { 481 assertEquals(test, re.test(input), "test:" + re + ":" + input); 482} 483 484assertRegExpTest(/b\b/, "b", true); 485assertRegExpTest(/b\b$/, "b", true); 486assertRegExpTest(/\bb/, "b", true); 487assertRegExpTest(/^\bb/, "b", true); 488assertRegExpTest(/,\b/, ",", false); 489assertRegExpTest(/,\b$/, ",", false); 490assertRegExpTest(/\b,/, ",", false); 491assertRegExpTest(/^\b,/, ",", false); 492 493assertRegExpTest(/b\B/, "b", false); 494assertRegExpTest(/b\B$/, "b", false); 495assertRegExpTest(/\Bb/, "b", false); 496assertRegExpTest(/^\Bb/, "b", false); 497assertRegExpTest(/,\B/, ",", true); 498assertRegExpTest(/,\B$/, ",", true); 499assertRegExpTest(/\B,/, ",", true); 500assertRegExpTest(/^\B,/, ",", true); 501 502assertRegExpTest(/b\b/, "b,", true); 503assertRegExpTest(/b\b/, "ba", false); 504assertRegExpTest(/b\B/, "b,", false); 505assertRegExpTest(/b\B/, "ba", true); 506 507assertRegExpTest(/b\Bb/, "bb", true); 508assertRegExpTest(/b\bb/, "bb", false); 509 510assertRegExpTest(/b\b[,b]/, "bb", false); 511assertRegExpTest(/b\B[,b]/, "bb", true); 512assertRegExpTest(/b\b[,b]/, "b,", true); 513assertRegExpTest(/b\B[,b]/, "b,", false); 514 515assertRegExpTest(/[,b]\bb/, "bb", false); 516assertRegExpTest(/[,b]\Bb/, "bb", true); 517assertRegExpTest(/[,b]\bb/, ",b", true); 518assertRegExpTest(/[,b]\Bb/, ",b", false); 519 520assertRegExpTest(/[,b]\b[,b]/, "bb", false); 521assertRegExpTest(/[,b]\B[,b]/, "bb", true); 522assertRegExpTest(/[,b]\b[,b]/, ",b", true); 523assertRegExpTest(/[,b]\B[,b]/, ",b", false); 524assertRegExpTest(/[,b]\b[,b]/, "b,", true); 525assertRegExpTest(/[,b]\B[,b]/, "b,", false); 526 527// Test that caching of result doesn't share result objects. 528// More iterations increases the chance of hitting a GC. 529for (var i = 0; i < 100; i++) { 530 var re = /x(y)z/; 531 var res = re.exec("axyzb"); 532 assertTrue(!!res); 533 assertEquals(2, res.length); 534 assertEquals("xyz", res[0]); 535 assertEquals("y", res[1]); 536 assertEquals(1, res.index); 537 assertEquals("axyzb", res.input); 538 assertEquals(undefined, res.foobar); 539 540 res.foobar = "Arglebargle"; 541 res[3] = "Glopglyf"; 542 assertEquals("Arglebargle", res.foobar); 543} 544 545// Test that we perform the spec required conversions in the correct order. 546var log; 547var string = "the string"; 548var fakeLastIndex = { 549 valueOf: function() { 550 log.push("li"); 551 return 0; 552 } 553 }; 554var fakeString = { 555 toString: function() { 556 log.push("ts"); 557 return string; 558 }, 559 length: 0 560 }; 561 562var re = /str/; 563log = []; 564re.lastIndex = fakeLastIndex; 565var result = re.exec(fakeString); 566assertEquals(["str"], result); 567assertEquals(["ts", "li"], log); 568 569// Again, to check if caching interferes. 570log = []; 571re.lastIndex = fakeLastIndex; 572result = re.exec(fakeString); 573assertEquals(["str"], result); 574assertEquals(["ts", "li"], log); 575 576// And one more time, just to be certain. 577log = []; 578re.lastIndex = fakeLastIndex; 579result = re.exec(fakeString); 580assertEquals(["str"], result); 581assertEquals(["ts", "li"], log); 582 583// Now with a global regexp, where lastIndex is actually used. 584re = /str/g; 585log = []; 586re.lastIndex = fakeLastIndex; 587var result = re.exec(fakeString); 588assertEquals(["str"], result); 589assertEquals(["ts", "li"], log); 590 591// Again, to check if caching interferes. 592log = []; 593re.lastIndex = fakeLastIndex; 594result = re.exec(fakeString); 595assertEquals(["str"], result); 596assertEquals(["ts", "li"], log); 597 598// And one more time, just to be certain. 599log = []; 600re.lastIndex = fakeLastIndex; 601result = re.exec(fakeString); 602assertEquals(["str"], result); 603assertEquals(["ts", "li"], log); 604 605 606// Check that properties of RegExp have the correct permissions. 607var re = /x/g; 608var desc = Object.getOwnPropertyDescriptor(re, "global"); 609assertEquals(true, desc.value); 610assertEquals(false, desc.configurable); 611assertEquals(false, desc.enumerable); 612assertEquals(false, desc.writable); 613 614desc = Object.getOwnPropertyDescriptor(re, "multiline"); 615assertEquals(false, desc.value); 616assertEquals(false, desc.configurable); 617assertEquals(false, desc.enumerable); 618assertEquals(false, desc.writable); 619 620desc = Object.getOwnPropertyDescriptor(re, "ignoreCase"); 621assertEquals(false, desc.value); 622assertEquals(false, desc.configurable); 623assertEquals(false, desc.enumerable); 624assertEquals(false, desc.writable); 625 626desc = Object.getOwnPropertyDescriptor(re, "lastIndex"); 627assertEquals(0, desc.value); 628assertEquals(false, desc.configurable); 629assertEquals(false, desc.enumerable); 630assertEquals(true, desc.writable); 631 632 633// Check that end-anchored regexps are optimized correctly. 634var re = /(?:a|bc)g$/; 635assertTrue(re.test("ag")); 636assertTrue(re.test("bcg")); 637assertTrue(re.test("abcg")); 638assertTrue(re.test("zimbag")); 639assertTrue(re.test("zimbcg")); 640 641assertFalse(re.test("g")); 642assertFalse(re.test("")); 643 644// Global regexp (non-zero start). 645var re = /(?:a|bc)g$/g; 646assertTrue(re.test("ag")); 647re.lastIndex = 1; // Near start of string. 648assertTrue(re.test("zimbag")); 649re.lastIndex = 6; // At end of string. 650assertFalse(re.test("zimbag")); 651re.lastIndex = 5; // Near end of string. 652assertFalse(re.test("zimbag")); 653re.lastIndex = 4; 654assertTrue(re.test("zimbag")); 655 656// Anchored at both ends. 657var re = /^(?:a|bc)g$/g; 658assertTrue(re.test("ag")); 659re.lastIndex = 1; 660assertFalse(re.test("ag")); 661re.lastIndex = 1; 662assertFalse(re.test("zag")); 663 664// Long max_length of RegExp. 665var re = /VeryLongRegExp!{1,1000}$/; 666assertTrue(re.test("BahoolaVeryLongRegExp!!!!!!")); 667assertFalse(re.test("VeryLongRegExp")); 668assertFalse(re.test("!")); 669 670// End anchor inside disjunction. 671var re = /(?:a$|bc$)/; 672assertTrue(re.test("a")); 673assertTrue(re.test("bc")); 674assertTrue(re.test("abc")); 675assertTrue(re.test("zimzamzumba")); 676assertTrue(re.test("zimzamzumbc")); 677assertFalse(re.test("c")); 678assertFalse(re.test("")); 679 680// Only partially anchored. 681var re = /(?:a|bc$)/; 682assertTrue(re.test("a")); 683assertTrue(re.test("bc")); 684assertEquals(["a"], re.exec("abc")); 685assertEquals(4, re.exec("zimzamzumba").index); 686assertEquals(["bc"], re.exec("zimzomzumbc")); 687assertFalse(re.test("c")); 688assertFalse(re.test("")); 689 690// Valid syntax in ES5. 691re = RegExp("(?:x)*"); 692re = RegExp("(x)*"); 693 694// Syntax extension relative to ES5, for matching JSC (and ES3). 695// Shouldn't throw. 696re = RegExp("(?=x)*"); 697re = RegExp("(?!x)*"); 698 699// Should throw. Shouldn't hit asserts in debug mode. 700assertThrows("RegExp('(*)')"); 701assertThrows("RegExp('(?:*)')"); 702assertThrows("RegExp('(?=*)')"); 703assertThrows("RegExp('(?!*)')"); 704 705// Test trimmed regular expression for RegExp.test(). 706assertTrue(/.*abc/.test("abc")); 707assertFalse(/.*\d+/.test("q")); 708