regexp.js revision 5d4cdbf7a67d3662fa0bee4efdb7edd8daec9b0b
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