dart-sdk/tests/corelib/regexp/regexp_test.dart
Robert Nystrom d9bb487ca8 Treat the expect and async_helper packages as opted in to NNBD.
This should enable strong mode tests to be fully NNBD opted in. At the
same time, legacy tests when run with the experiment off should
hopefully be able to still consume these libraries because they don't
actually use any NNBD features.

To do this, I changed the script that generates the SDK repo's package
config to not put in a language version for any package whose pubspec
has no SDK version. According to language versioning, that means the
package should be considered to be at the "current" version of the SDK
running the code.

In NNBD, that's opted in. With the experiment off, that is (presumably)
"opted out" in the sense that NNBD doesn't exist.

In order to *not* opt in some of the other packages that currently
lack SDK constraints, I put those in and pinned them to "^2.7.0" which
was what the package config script used to implicitly fill in for them.

I see a bunch of other changes in the generated package config too. The
update script probably hasn't been run in a while.

Change-Id: I55193d42eac0696a6b0105546551efa45a1f3252
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/144305
Auto-Submit: Bob Nystrom <rnystrom@google.com>
Reviewed-by: Leaf Petersen <leafp@google.com>
Commit-Queue: Bob Nystrom <rnystrom@google.com>
2020-04-27 21:04:56 +00:00

627 lines
24 KiB
Dart

// 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));
}