// Copyright (c) 2014, the Dart project authors. All rights reserved. // 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. import "package:expect/expect.dart"; void testEscape(str, regexp) { assertEquals("foo:bar:baz", str.split(regexp).join(":")); } void assertEquals(actual, expected, [String message = ""]) => Expect.equals(actual, expected, message); void assertTrue(actual, [String message = ""]) => Expect.isTrue(actual, message); void assertFalse(actual, [String message = ""]) => Expect.isFalse(actual, message); void assertThrows(fn) => Expect.throws(fn); void main() { testEscape("foo\nbar\nbaz", new RegExp(r"\n")); testEscape("foo bar baz", new RegExp(r"\s")); testEscape("foo\tbar\tbaz", new RegExp(r"\s")); testEscape("foo-bar-baz", new RegExp(r"\u002D")); // Test containing null char in regexp. var s = '[' + new String.fromCharCode(0) + ']'; var regexp = new RegExp(s); assertEquals(regexp.allMatches(s).length, 1); assertEquals(regexp.stringMatch(s), new String.fromCharCode(0)); final _vmFrame = new RegExp(r'^#\d+\s+(\S.*) \((.+?):(\d+)(?::(\d+))?\)$'); final _traceLine = "#0 Trace.Trace.parse (package:stack_trace/src/trace.dart:130:7)"; Expect.equals(_vmFrame.firstMatch(_traceLine)!.group(0), _traceLine); // Test the UTF16 case insensitive comparison. regexp = new RegExp(r"x(a)\1x", caseSensitive: false); Expect.equals(regexp.firstMatch("xaAx\u1234")!.group(0), "xaAx"); // Test strings containing all line separators s = 'aA\nbB\rcC\r\ndD\u2028eE\u2029fF'; // any non-newline character at the beginning of a line regexp = new RegExp(r"^.", multiLine: true); var result = regexp.allMatches(s).toList(); assertEquals(result.length, 6); assertEquals(result[0][0], 'a'); assertEquals(result[1][0], 'b'); assertEquals(result[2][0], 'c'); assertEquals(result[3][0], 'd'); assertEquals(result[4][0], 'e'); assertEquals(result[5][0], 'f'); // any non-newline character at the end of a line regexp = new RegExp(r".$", multiLine: true); result = regexp.allMatches(s).toList(); assertEquals(result.length, 6); assertEquals(result[0][0], 'A'); assertEquals(result[1][0], 'B'); assertEquals(result[2][0], 'C'); assertEquals(result[3][0], 'D'); assertEquals(result[4][0], 'E'); assertEquals(result[5][0], 'F'); // *any* character at the beginning of a line regexp = new RegExp(r"^[^]", multiLine: true); result = regexp.allMatches(s).toList(); assertEquals(result.length, 7); assertEquals(result[0][0], 'a'); assertEquals(result[1][0], 'b'); assertEquals(result[2][0], 'c'); assertEquals(result[3][0], '\n'); assertEquals(result[4][0], 'd'); assertEquals(result[5][0], 'e'); assertEquals(result[6][0], 'f'); // *any* character at the end of a line regexp = new RegExp(r"[^]$", multiLine: true); result = regexp.allMatches(s).toList(); assertEquals(result.length, 7); assertEquals(result[0][0], 'A'); assertEquals(result[1][0], 'B'); assertEquals(result[2][0], 'C'); assertEquals(result[3][0], '\r'); assertEquals(result[4][0], 'D'); assertEquals(result[5][0], 'E'); assertEquals(result[6][0], 'F'); // Some tests from the Mozilla tests, where our behavior used to differ // from SpiderMonkey. // From ecma_3/RegExp/regress-334158.js assertTrue("\x01".contains(new RegExp(r"\ca"))); assertFalse("\\ca".contains(new RegExp(r"\ca"))); assertFalse("ca".contains(new RegExp(r"\ca"))); assertTrue("\\ca".contains(new RegExp(r"\c[a/]"))); assertTrue("\\c/".contains(new RegExp(r"\c[a/]"))); // Test \c in character class var re = r"^[\cM]$"; assertTrue("\r".contains(new RegExp(re))); assertFalse("M".contains(new RegExp(re))); assertFalse("c".contains(new RegExp(re))); assertFalse("\\".contains(new RegExp(re))); assertFalse("\x03".contains(new RegExp(re))); // I.e., read as \cc re = r"^[\c]]$"; assertTrue("c]".contains(new RegExp(re))); assertTrue("\\]".contains(new RegExp(re))); assertFalse("\x1d".contains(new RegExp(re))); // ']' & 0x1f assertFalse("\x03]".contains(new RegExp(re))); // I.e., read as \cc // Digit control characters are masked in character classes. re = r"^[\c1]$"; assertTrue("\x11".contains(new RegExp(re))); assertFalse("\\".contains(new RegExp(re))); assertFalse("c".contains(new RegExp(re))); assertFalse("1".contains(new RegExp(re))); // Underscore control character is masked in character classes. re = r"^[\c_]$"; assertTrue("\x1f".contains(new RegExp(re))); assertFalse("\\".contains(new RegExp(re))); assertFalse("c".contains(new RegExp(re))); assertFalse("_".contains(new RegExp(re))); re = r"^[\c$]$"; // Other characters are interpreted literally. assertFalse("\x04".contains(new RegExp(re))); assertTrue("\\".contains(new RegExp(re))); assertTrue("c".contains(new RegExp(re))); assertTrue(r"$".contains(new RegExp(re))); assertTrue("Z[\\cde".contains(new RegExp(r"^[Z-\c-e]*$"))); // Test that we handle \s and \S correctly on special Unicode characters. re = r"\s"; assertTrue("\u2028".contains(new RegExp(re))); assertTrue("\u2029".contains(new RegExp(re))); assertTrue("\uFEFF".contains(new RegExp(re))); re = r"\S"; assertFalse("\u2028".contains(new RegExp(re))); assertFalse("\u2029".contains(new RegExp(re))); assertFalse("\uFEFF".contains(new RegExp(re))); // Test that we handle \s and \S correctly inside some bizarre // character classes. re = r"[\s-:]"; assertTrue('-'.contains(new RegExp(re))); assertTrue(':'.contains(new RegExp(re))); assertTrue(' '.contains(new RegExp(re))); assertTrue('\t'.contains(new RegExp(re))); assertTrue('\n'.contains(new RegExp(re))); assertFalse('a'.contains(new RegExp(re))); assertFalse('Z'.contains(new RegExp(re))); re = r"[\S-:]"; assertTrue('-'.contains(new RegExp(re))); assertTrue(':'.contains(new RegExp(re))); assertFalse(' '.contains(new RegExp(re))); assertFalse('\t'.contains(new RegExp(re))); assertFalse('\n'.contains(new RegExp(re))); assertTrue('a'.contains(new RegExp(re))); assertTrue('Z'.contains(new RegExp(re))); re = r"[^\s-:]"; assertFalse('-'.contains(new RegExp(re))); assertFalse(':'.contains(new RegExp(re))); assertFalse(' '.contains(new RegExp(re))); assertFalse('\t'.contains(new RegExp(re))); assertFalse('\n'.contains(new RegExp(re))); assertTrue('a'.contains(new RegExp(re))); assertTrue('Z'.contains(new RegExp(re))); re = r"[^\S-:]"; assertFalse('-'.contains(new RegExp(re))); assertFalse(':'.contains(new RegExp(re))); assertTrue(' '.contains(new RegExp(re))); assertTrue('\t'.contains(new RegExp(re))); assertTrue('\n'.contains(new RegExp(re))); assertFalse('a'.contains(new RegExp(re))); assertFalse('Z'.contains(new RegExp(re))); re = r"[\s]"; assertFalse('-'.contains(new RegExp(re))); assertFalse(':'.contains(new RegExp(re))); assertTrue(' '.contains(new RegExp(re))); assertTrue('\t'.contains(new RegExp(re))); assertTrue('\n'.contains(new RegExp(re))); assertFalse('a'.contains(new RegExp(re))); assertFalse('Z'.contains(new RegExp(re))); re = r"[^\s]"; assertTrue('-'.contains(new RegExp(re))); assertTrue(':'.contains(new RegExp(re))); assertFalse(' '.contains(new RegExp(re))); assertFalse('\t'.contains(new RegExp(re))); assertFalse('\n'.contains(new RegExp(re))); assertTrue('a'.contains(new RegExp(re))); assertTrue('Z'.contains(new RegExp(re))); re = r"[\S]"; assertTrue('-'.contains(new RegExp(re))); assertTrue(':'.contains(new RegExp(re))); assertFalse(' '.contains(new RegExp(re))); assertFalse('\t'.contains(new RegExp(re))); assertFalse('\n'.contains(new RegExp(re))); assertTrue('a'.contains(new RegExp(re))); assertTrue('Z'.contains(new RegExp(re))); re = r"[^\S]"; assertFalse('-'.contains(new RegExp(re))); assertFalse(':'.contains(new RegExp(re))); assertTrue(' '.contains(new RegExp(re))); assertTrue('\t'.contains(new RegExp(re))); assertTrue('\n'.contains(new RegExp(re))); assertFalse('a'.contains(new RegExp(re))); assertFalse('Z'.contains(new RegExp(re))); re = r"[\s\S]"; assertTrue('-'.contains(new RegExp(re))); assertTrue(':'.contains(new RegExp(re))); assertTrue(' '.contains(new RegExp(re))); assertTrue('\t'.contains(new RegExp(re))); assertTrue('\n'.contains(new RegExp(re))); assertTrue('a'.contains(new RegExp(re))); assertTrue('Z'.contains(new RegExp(re))); re = r"[^\s\S]"; assertFalse('-'.contains(new RegExp(re))); assertFalse(':'.contains(new RegExp(re))); assertFalse(' '.contains(new RegExp(re))); assertFalse('\t'.contains(new RegExp(re))); assertFalse('\n'.contains(new RegExp(re))); assertFalse('a'.contains(new RegExp(re))); assertFalse('Z'.contains(new RegExp(re))); // 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 = r"[\s-0-9]"; assertTrue(' '.contains(new RegExp(re))); assertTrue('\xA0'.contains(new RegExp(re))); assertTrue('-'.contains(new RegExp(re))); assertTrue('0'.contains(new RegExp(re))); assertTrue('9'.contains(new RegExp(re))); assertFalse('1'.contains(new RegExp(re))); // Test beginning and end of line assertions with or without the // multiline flag. re = r"^\d+"; assertFalse("asdf\n123".contains(new RegExp(re))); regexp = new RegExp(r"^\d+", multiLine: true); assertTrue("asdf\n123".contains(regexp)); re = r"\d+$"; assertFalse("123\nasdf".contains(new RegExp(re))); regexp = new RegExp(r"\d+$", multiLine: true); assertTrue("123\nasdf".contains(regexp)); // Test that empty matches are handled correctly for multiline global // regexps. regexp = new RegExp(r"^(.*)", multiLine: true); assertEquals(3, regexp.allMatches("a\n\rb").length); assertEquals( "*a\n*b\r*c\n*\r*d\r*\n*e", "a\nb\rc\n\rd\r\ne" .replaceAllMapped(regexp, (Match m) => "*${m.group(1)}")); // Test that empty matches advance one character regexp = new RegExp(""); assertEquals("xAx", "A".replaceAll(regexp, "x")); assertEquals(3, new String.fromCharCode(161).replaceAll(regexp, "x").length); // Check for lazy RegExp literal cregexpation lazyLiteral(doit) { if (doit) return "".replaceAll(new RegExp(r"foo(", caseSensitive: false), ""); return true; } assertTrue(lazyLiteral(false)); assertThrows(() => lazyLiteral(true)); // Check $01 and $10 regexp = new RegExp("(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)"); assertEquals( "t", "123456789t".replaceAllMapped(regexp, (Match m) => m.group(10)!)); assertEquals("15", "123456789t".replaceAllMapped(regexp, (Match m) => "${m.group(1)}5")); assertEquals( "1", "123456789t".replaceAllMapped(regexp, (Match m) => m.group(1)!)); assertFalse("football".contains(new RegExp(r"()foo$\1")), "football1"); assertFalse("football".contains(new RegExp(r"foo$(?=ball)")), "football2"); assertFalse("football".contains(new RegExp(r"foo$(?!bar)")), "football3"); assertTrue("foo".contains(new RegExp(r"()foo$\1")), "football4"); assertTrue("foo".contains(new RegExp(r"foo$(?=(ball)?)")), "football5"); assertTrue("foo".contains(new RegExp(r"()foo$(?!bar)")), "football6"); assertFalse("football".contains(new RegExp(r"(x?)foo$\1")), "football7"); assertFalse("football".contains(new RegExp(r"foo$(?=ball)")), "football8"); assertFalse("football".contains(new RegExp(r"foo$(?!bar)")), "football9"); assertTrue("foo".contains(new RegExp(r"(x?)foo$\1")), "football10"); assertTrue("foo".contains(new RegExp(r"foo$(?=(ball)?)")), "football11"); assertTrue("foo".contains(new RegExp(r"foo$(?!bar)")), "football12"); // Check that the back reference has two successors. See // BackReferenceNode::PropagateForward. assertFalse('foo'.contains(new RegExp(r"f(o)\b\1"))); assertTrue('foo'.contains(new RegExp(r"f(o)\B\1"))); // Back-reference, ignore case: // ASCII assertEquals( "a", new RegExp(r"x(a)\1x", caseSensitive: false).firstMatch("xaAx")!.group(1), "backref-ASCII"); assertFalse("xaaaaa".contains(new RegExp(r"x(...)\1", caseSensitive: false)), "backref-ASCII-short"); assertTrue("xx".contains(new RegExp(r"x((?:))\1\1x", caseSensitive: false)), "backref-ASCII-empty"); assertTrue( "xabcx".contains(new RegExp(r"x(?:...|(...))\1x", caseSensitive: false)), "backref-ASCII-uncaptured"); assertTrue( "xabcABCx" .contains(new RegExp(r"x(?:...|(...))\1x", caseSensitive: false)), "backref-ASCII-backtrack"); assertEquals( "aBc", new RegExp(r"x(...)\1\1x", caseSensitive: false) .firstMatch("xaBcAbCABCx")! .group(1), "backref-ASCII-twice"); for (var i = 0; i < 128; i++) { var testName = "backref-ASCII-char-$i,,${i^0x20}"; var test = new String.fromCharCodes([i, i ^ 0x20]) .contains(new RegExp(r"^(.)\1$", caseSensitive: false)); if (('A'.codeUnitAt(0) <= i && i <= 'Z'.codeUnitAt(0)) || ('a'.codeUnitAt(0) <= i && i <= 'z'.codeUnitAt(0))) { assertTrue(test, testName); } else { assertFalse(test, testName); } } assertFalse('foo'.contains(new RegExp(r"f(o)$\1")), "backref detects at_end"); // Check decimal escapes doesn't overflow. // (Note: \214 is interpreted as octal). assertEquals( "\x8c7483648", new RegExp(r"\2147483648").firstMatch("\x8c7483648")!.group(0), "Overflow decimal escape"); // Check numbers in quantifiers doesn't overflow and doesn't throw on // too large numbers. assertFalse( 'b'.contains( new RegExp(r"a{111111111111111111111111111111111111111111111}")), "overlarge1"); assertFalse( 'b'.contains( new RegExp(r"a{999999999999999999999999999999999999999999999}")), "overlarge2"); assertFalse( 'b'.contains( new RegExp(r"a{1,111111111111111111111111111111111111111111111}")), "overlarge3"); assertFalse( 'b'.contains( new RegExp(r"a{1,999999999999999999999999999999999999999999999}")), "overlarge4"); assertFalse('b'.contains(new RegExp(r"a{2147483648}")), "overlarge5"); assertFalse('b'.contains(new RegExp(r"a{21474836471}")), "overlarge6"); assertFalse('b'.contains(new RegExp(r"a{1,2147483648}")), "overlarge7"); assertFalse('b'.contains(new RegExp(r"a{1,21474836471}")), "overlarge8"); assertFalse( 'b'.contains(new RegExp(r"a{2147483648,2147483648}")), "overlarge9"); assertFalse( 'b'.contains(new RegExp(r"a{21474836471,21474836471}")), "overlarge10"); assertFalse('b'.contains(new RegExp(r"a{2147483647}")), "overlarge11"); assertFalse('b'.contains(new RegExp(r"a{1,2147483647}")), "overlarge12"); assertTrue('a'.contains(new RegExp(r"a{1,2147483647}")), "overlarge13"); assertFalse( 'a'.contains(new RegExp(r"a{2147483647,2147483647}")), "overlarge14"); // Check that we don't repad past the end of the string. assertFalse('b'.contains(new RegExp(r"f"))); assertFalse('x'.contains(new RegExp(r"[abc]f"))); assertFalse('xa'.contains(new RegExp(r"[abc]f"))); assertFalse('x'.contains(new RegExp(r"[abc]<"))); assertFalse('xa'.contains(new RegExp(r"[abc]<"))); assertFalse('b'.contains(new RegExp(r"f", caseSensitive: false))); assertFalse('x'.contains(new RegExp(r"[abc]f", caseSensitive: false))); assertFalse('xa'.contains(new RegExp(r"[abc]f", caseSensitive: false))); assertFalse('x'.contains(new RegExp(r"[abc]<", caseSensitive: false))); assertFalse('xa'.contains(new RegExp(r"[abc]<", caseSensitive: false))); assertFalse('x'.contains(new RegExp(r"f[abc]"))); assertFalse('xa'.contains(new RegExp(r"f[abc]"))); assertFalse('x'.contains(new RegExp(r"<[abc]"))); assertFalse('xa'.contains(new RegExp(r"<[abc]"))); assertFalse('x'.contains(new RegExp(r"f[abc]", caseSensitive: false))); assertFalse('xa'.contains(new RegExp(r"f[abc]", caseSensitive: false))); assertFalse('x'.contains(new RegExp(r"<[abc]", caseSensitive: false))); assertFalse('xa'.contains(new RegExp(r"<[abc]", caseSensitive: false))); // Test that merging of quick test masks gets it right. assertFalse('x7%%y'.contains(new RegExp(r"x([0-7]%%x|[0-6]%%y)")), 'qt'); assertFalse( 'xy7%%%y' .contains(new RegExp(r"()x\1(y([0-7]%%%x|[0-6]%%%y)|dkjasldkas)")), 'qt2'); assertFalse( 'xy%%%y' .contains(new RegExp(r"()x\1(y([0-7]%%%x|[0-6]%%%y)|dkjasldkas)")), 'qt3'); assertFalse( 'xy7%%%y'.contains(new RegExp(r"()x\1y([0-7]%%%x|[0-6]%%%y)")), 'qt4'); assertFalse( 'xy%%%y' .contains(new RegExp(r"()x\1(y([0-7]%%%x|[0-6]%%%y)|dkjasldkas)")), 'qt5'); assertFalse( 'xy7%%%y'.contains(new RegExp(r"()x\1y([0-7]%%%x|[0-6]%%%y)")), 'qt6'); assertFalse( 'xy7%%%y'.contains(new RegExp(r"xy([0-7]%%%x|[0-6]%%%y)")), 'qt7'); assertFalse('x7%%%y'.contains(new RegExp(r"x([0-7]%%%x|[0-6]%%%y)")), 'qt8'); // Don't hang on this one. "".contains(new RegExp(r"[^\xfe-\xff]*")); var longbuffer = new StringBuffer("a"); for (var i = 0; i < 100000; i++) { longbuffer.write("a?"); } var long = longbuffer.toString(); // Don't crash on this one, but maybe throw an exception. try { new RegExp(long).allMatches("a"); } catch (e) { assertTrue(e.toString().indexOf("Stack overflow") >= 0, "overflow"); } // Test boundary-checks. void assertRegExpTest(re, input, test) { assertEquals( test, input.contains(new RegExp(re)), "test:" + re + ":" + input); } assertRegExpTest(r"b\b", "b", true); assertRegExpTest(r"b\b$", "b", true); assertRegExpTest(r"\bb", "b", true); assertRegExpTest(r"^\bb", "b", true); assertRegExpTest(r",\b", ",", false); assertRegExpTest(r",\b$", ",", false); assertRegExpTest(r"\b,", ",", false); assertRegExpTest(r"^\b,", ",", false); assertRegExpTest(r"b\B", "b", false); assertRegExpTest(r"b\B$", "b", false); assertRegExpTest(r"\Bb", "b", false); assertRegExpTest(r"^\Bb", "b", false); assertRegExpTest(r",\B", ",", true); assertRegExpTest(r",\B$", ",", true); assertRegExpTest(r"\B,", ",", true); assertRegExpTest(r"^\B,", ",", true); assertRegExpTest(r"b\b", "b,", true); assertRegExpTest(r"b\b", "ba", false); assertRegExpTest(r"b\B", "b,", false); assertRegExpTest(r"b\B", "ba", true); assertRegExpTest(r"b\Bb", "bb", true); assertRegExpTest(r"b\bb", "bb", false); assertRegExpTest(r"b\b[,b]", "bb", false); assertRegExpTest(r"b\B[,b]", "bb", true); assertRegExpTest(r"b\b[,b]", "b,", true); assertRegExpTest(r"b\B[,b]", "b,", false); assertRegExpTest(r"[,b]\bb", "bb", false); assertRegExpTest(r"[,b]\Bb", "bb", true); assertRegExpTest(r"[,b]\bb", ",b", true); assertRegExpTest(r"[,b]\Bb", ",b", false); assertRegExpTest(r"[,b]\b[,b]", "bb", false); assertRegExpTest(r"[,b]\B[,b]", "bb", true); assertRegExpTest(r"[,b]\b[,b]", ",b", true); assertRegExpTest(r"[,b]\B[,b]", ",b", false); assertRegExpTest(r"[,b]\b[,b]", "b,", true); assertRegExpTest(r"[,b]\B[,b]", "b,", false); // Skipped tests from V8: // Test that caching of result doesn't share result objects. // More iterations increases the chance of hitting a GC. // Test that we perform the spec required conversions in the correct order. // Check that properties of RegExp have the correct permissions. // Check that end-anchored regexps are optimized correctly. re = r"(?:a|bc)g$"; assertTrue("ag".contains(new RegExp(re))); assertTrue("bcg".contains(new RegExp(re))); assertTrue("abcg".contains(new RegExp(re))); assertTrue("zimbag".contains(new RegExp(re))); assertTrue("zimbcg".contains(new RegExp(re))); assertFalse("g".contains(new RegExp(re))); assertFalse("".contains(new RegExp(re))); // Global regexp (non-zero start). re = r"(?:a|bc)g$"; assertTrue("ag".contains(new RegExp(re))); // Near start of string. assertTrue(new RegExp(re).allMatches("zimbag", 1).isNotEmpty); // At end of string. assertTrue(new RegExp(re).allMatches("zimbag", 6).isEmpty); // Near end of string. assertTrue(new RegExp(re).allMatches("zimbag", 5).isEmpty); assertTrue(new RegExp(re).allMatches("zimbag", 4).isNotEmpty); // Anchored at both ends. re = r"^(?:a|bc)g$"; assertTrue("ag".contains(new RegExp(re))); assertTrue(new RegExp(re).allMatches("ag", 1).isEmpty); assertTrue(new RegExp(re).allMatches("zag", 1).isEmpty); // Long max_length of RegExp. re = r"VeryLongRegExp!{1,1000}$"; assertTrue("BahoolaVeryLongRegExp!!!!!!".contains(new RegExp(re))); assertFalse("VeryLongRegExp".contains(new RegExp(re))); assertFalse("!".contains(new RegExp(re))); // End anchor inside disjunction. re = r"(?:a$|bc$)"; assertTrue("a".contains(new RegExp(re))); assertTrue("bc".contains(new RegExp(re))); assertTrue("abc".contains(new RegExp(re))); assertTrue("zimzamzumba".contains(new RegExp(re))); assertTrue("zimzamzumbc".contains(new RegExp(re))); assertFalse("c".contains(new RegExp(re))); assertFalse("".contains(new RegExp(re))); // Only partially anchored. re = r"(?:a|bc$)"; assertTrue("a".contains(new RegExp(re))); assertTrue("bc".contains(new RegExp(re))); assertEquals("a", new RegExp(re).firstMatch("abc")!.group(0)); assertEquals(4, new RegExp(re).firstMatch("zimzamzumba")!.start); assertEquals("bc", new RegExp(re).firstMatch("zimzomzumbc")!.group(0)); assertFalse("c".contains(new RegExp(re))); assertFalse("".contains(new RegExp(re))); // Valid syntax in ES5. regexp = new RegExp("(?:x)*"); regexp = new RegExp("(x)*"); // Syntax extension relative to ES5, for matching JSC (and ES3). // Shouldn't throw. regexp = new RegExp("(?=x)*"); regexp = new RegExp("(?!x)*"); // Should throw. Shouldn't hit asserts in debug mode. assertThrows(() => new RegExp('(*)')); assertThrows(() => new RegExp('(?:*)')); assertThrows(() => new RegExp('(?=*)')); assertThrows(() => new RegExp('(?!*)')); // Test trimmed regular expression for RegExp.test(). assertTrue("abc".contains(new RegExp(r".*abc"))); assertFalse("q".contains(new RegExp(r".*\d+"))); // Tests skipped from V8: // Test that RegExp.prototype.toString() throws TypeError for // incompatible receivers (ES5 section 15.10.6 and 15.10.6.4). testSticky(); } testSticky() { var regexp = new RegExp(r"foo.bar"); Expect.isNotNull(regexp.matchAsPrefix("foo_bar", 0)); Expect.isNull(regexp.matchAsPrefix("..foo_bar", 0)); Expect.isNotNull(regexp.matchAsPrefix("..foo_bar", 2)); regexp = new RegExp(r"^foo"); Expect.isNotNull(regexp.matchAsPrefix("foobar", 0)); Expect.isNull(regexp.matchAsPrefix("..foo", 0)); Expect.isNull(regexp.matchAsPrefix("..foo", 2)); regexp = new RegExp(r"^foo", multiLine: true); Expect.isNotNull(regexp.matchAsPrefix("foobar", 0)); Expect.isNull(regexp.matchAsPrefix("..\nfoo", 0)); Expect.isNotNull(regexp.matchAsPrefix("..\nfoo", 3)); Expect.isNull(regexp.matchAsPrefix("..\nfoofoo", 6)); regexp = new RegExp(r"bar$"); Expect.isNull(regexp.matchAsPrefix("foobar", 0)); Expect.isNotNull(regexp.matchAsPrefix("foobar", 3)); }