diff --git a/src/org/mozilla/javascript/NativeString.java b/src/org/mozilla/javascript/NativeString.java
index 8ee76d7168..75c7e5b99e 100644
--- a/src/org/mozilla/javascript/NativeString.java
+++ b/src/org/mozilla/javascript/NativeString.java
@@ -98,6 +98,7 @@ protected void fillConstructorProperties(IdFunctionObject ctor) {
addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_match, "match", 2);
addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_search, "search", 2);
addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_replace, "replace", 2);
+ addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_replaceAll, "replaceAll", 2);
addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_localeCompare, "localeCompare", 2);
addIdFunctionProperty(
ctor, STRING_TAG, ConstructorId_toLocaleLowerCase, "toLocaleLowerCase", 1);
@@ -246,6 +247,10 @@ protected void initPrototypeId(int id) {
arity = 2;
s = "replace";
break;
+ case Id_replaceAll:
+ arity = 2;
+ s = "replaceAll";
+ break;
case Id_at:
arity = 1;
s = "at";
@@ -345,6 +350,7 @@ public Object execIdCall(
case ConstructorId_match:
case ConstructorId_search:
case ConstructorId_replace:
+ case ConstructorId_replaceAll:
case ConstructorId_localeCompare:
case ConstructorId_toLocaleLowerCase:
{
@@ -463,10 +469,15 @@ public Object execIdCall(
String thisString =
ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
if (args.length > 0 && args[0] instanceof NativeRegExp) {
- throw ScriptRuntime.typeErrorById(
- "msg.first.arg.not.regexp",
- String.class.getSimpleName(),
- f.getFunctionName());
+ if (ScriptableObject.isTrue(
+ ScriptableObject.getProperty(
+ ScriptableObject.ensureScriptable(args[0]),
+ SymbolKey.MATCH))) {
+ throw ScriptRuntime.typeErrorById(
+ "msg.first.arg.not.regexp",
+ String.class.getSimpleName(),
+ f.getFunctionName());
+ }
}
int idx = js_indexOf(id, thisString, args);
@@ -599,14 +610,17 @@ public Object execIdCall(
case Id_match:
case Id_search:
case Id_replace:
+ case Id_replaceAll:
{
int actionType;
if (id == Id_match) {
actionType = RegExpProxy.RA_MATCH;
} else if (id == Id_search) {
actionType = RegExpProxy.RA_SEARCH;
- } else {
+ } else if (id == Id_replace) {
actionType = RegExpProxy.RA_REPLACE;
+ } else {
+ actionType = RegExpProxy.RA_REPLACE_ALL;
}
requireObjectCoercible(cx, thisObj, f);
@@ -1286,6 +1300,9 @@ protected int findPrototypeId(String s) {
case "replace":
id = Id_replace;
break;
+ case "replaceAll":
+ id = Id_replaceAll;
+ break;
case "localeCompare":
id = Id_localeCompare;
break;
@@ -1380,24 +1397,25 @@ protected int findPrototypeId(String s) {
Id_match = 31,
Id_search = 32,
Id_replace = 33,
- Id_localeCompare = 34,
- Id_toLocaleLowerCase = 35,
- Id_toLocaleUpperCase = 36,
- Id_trim = 37,
- Id_trimLeft = 38,
- Id_trimRight = 39,
- Id_includes = 40,
- Id_startsWith = 41,
- Id_endsWith = 42,
- Id_normalize = 43,
- Id_repeat = 44,
- Id_codePointAt = 45,
- Id_padStart = 46,
- Id_padEnd = 47,
- SymbolId_iterator = 48,
- Id_trimStart = 49,
- Id_trimEnd = 50,
- Id_at = 51,
+ Id_replaceAll = 34,
+ Id_localeCompare = 35,
+ Id_toLocaleLowerCase = 36,
+ Id_toLocaleUpperCase = 37,
+ Id_trim = 38,
+ Id_trimLeft = 39,
+ Id_trimRight = 40,
+ Id_includes = 41,
+ Id_startsWith = 42,
+ Id_endsWith = 43,
+ Id_normalize = 44,
+ Id_repeat = 45,
+ Id_codePointAt = 46,
+ Id_padStart = 47,
+ Id_padEnd = 48,
+ SymbolId_iterator = 49,
+ Id_trimStart = 50,
+ Id_trimEnd = 51,
+ Id_at = 52,
MAX_PROTOTYPE_ID = Id_at;
private static final int ConstructorId_charAt = -Id_charAt,
ConstructorId_charCodeAt = -Id_charCodeAt,
@@ -1414,6 +1432,7 @@ protected int findPrototypeId(String s) {
ConstructorId_match = -Id_match,
ConstructorId_search = -Id_search,
ConstructorId_replace = -Id_replace,
+ ConstructorId_replaceAll = -Id_replaceAll,
ConstructorId_localeCompare = -Id_localeCompare,
ConstructorId_toLocaleLowerCase = -Id_toLocaleLowerCase;
diff --git a/src/org/mozilla/javascript/RegExpProxy.java b/src/org/mozilla/javascript/RegExpProxy.java
index b158b629f9..092a93f136 100644
--- a/src/org/mozilla/javascript/RegExpProxy.java
+++ b/src/org/mozilla/javascript/RegExpProxy.java
@@ -15,7 +15,8 @@ public interface RegExpProxy {
// Types of regexp actions
public static final int RA_MATCH = 1;
public static final int RA_REPLACE = 2;
- public static final int RA_SEARCH = 3;
+ public static final int RA_REPLACE_ALL = 3;
+ public static final int RA_SEARCH = 4;
public boolean isRegExp(Scriptable obj);
diff --git a/src/org/mozilla/javascript/TokenStream.java b/src/org/mozilla/javascript/TokenStream.java
index 2e5292d5ba..e84d1c9a07 100644
--- a/src/org/mozilla/javascript/TokenStream.java
+++ b/src/org/mozilla/javascript/TokenStream.java
@@ -1496,8 +1496,8 @@ void readRegExp(int startToken) throws IOException {
if (matchChar('g')) addToString('g');
else if (matchChar('i')) addToString('i');
else if (matchChar('m')) addToString('m');
- else if (matchChar('y')) // FireFox 3
- addToString('y');
+ else if (matchChar('s')) addToString('s');
+ else if (matchChar('y')) addToString('y');
else break;
}
tokenEnd = start + stringBufferTop + 2; // include slashes
diff --git a/src/org/mozilla/javascript/regexp/NativeRegExp.java b/src/org/mozilla/javascript/regexp/NativeRegExp.java
index 2b7cffdefe..d707679bf0 100644
--- a/src/org/mozilla/javascript/regexp/NativeRegExp.java
+++ b/src/org/mozilla/javascript/regexp/NativeRegExp.java
@@ -11,6 +11,7 @@
import org.mozilla.javascript.IdFunctionObject;
import org.mozilla.javascript.IdScriptableObject;
import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
@@ -37,7 +38,8 @@ public class NativeRegExp extends IdScriptableObject {
public static final int JSREG_GLOB = 0x1; // 'g' flag: global
public static final int JSREG_FOLD = 0x2; // 'i' flag: fold
public static final int JSREG_MULTILINE = 0x4; // 'm' flag: multiline
- public static final int JSREG_STICKY = 0x8; // 'y' flag: sticky
+ public static final int JSREG_DOTALL = 0x8; // 's' flag: dotAll
+ public static final int JSREG_STICKY = 0x10; // 'y' flag: sticky
// type of match to perform
public static final int TEST = 0;
@@ -196,6 +198,7 @@ private void appendFlags(StringBuilder buf) {
if ((re.flags & JSREG_GLOB) != 0) buf.append('g');
if ((re.flags & JSREG_FOLD) != 0) buf.append('i');
if ((re.flags & JSREG_MULTILINE) != 0) buf.append('m');
+ if ((re.flags & JSREG_DOTALL) != 0) buf.append('s');
if ((re.flags & JSREG_STICKY) != 0) buf.append('y');
}
@@ -278,6 +281,8 @@ static RECompiled compileRE(Context cx, String str, String global, boolean flat)
f = JSREG_FOLD;
} else if (c == 'm') {
f = JSREG_MULTILINE;
+ } else if (c == 's') {
+ f = JSREG_DOTALL;
} else if (c == 'y') {
f = JSREG_STICKY;
} else {
@@ -1709,7 +1714,9 @@ private static int simpleMatch(
^ ((gData.cp < end) && isWord(input.charAt(gData.cp))));
break;
case REOP_DOT:
- if (gData.cp != end && !isLineTerm(input.charAt(gData.cp))) {
+ if (gData.cp != end
+ && ((gData.regexp.flags & JSREG_DOTALL) != 0
+ || !isLineTerm(input.charAt(gData.cp)))) {
result = true;
gData.cp++;
}
@@ -2521,8 +2528,9 @@ private static void reportError(String messageId, String arg) {
Id_global = 4,
Id_ignoreCase = 5,
Id_multiline = 6,
- Id_sticky = 7,
- MAX_INSTANCE_ID = 7;
+ Id_dotAll = 7,
+ Id_sticky = 8,
+ MAX_INSTANCE_ID = 8;
@Override
protected int getMaxInstanceId() {
@@ -2551,6 +2559,9 @@ protected int findInstanceIdInfo(String s) {
case "multiline":
id = Id_multiline;
break;
+ case "dotAll":
+ id = Id_dotAll;
+ break;
case "sticky":
id = Id_sticky;
break;
@@ -2571,6 +2582,7 @@ protected int findInstanceIdInfo(String s) {
case Id_global:
case Id_ignoreCase:
case Id_multiline:
+ case Id_dotAll:
case Id_sticky:
attr = PERMANENT | READONLY | DONTENUM;
break;
@@ -2595,6 +2607,8 @@ protected String getInstanceIdName(int id) {
return "ignoreCase";
case Id_multiline:
return "multiline";
+ case Id_dotAll:
+ return "dotAll";
case Id_sticky:
return "sticky";
}
@@ -2620,6 +2634,8 @@ protected Object getInstanceIdValue(int id) {
return ScriptRuntime.wrapBoolean((re.flags & JSREG_FOLD) != 0);
case Id_multiline:
return ScriptRuntime.wrapBoolean((re.flags & JSREG_MULTILINE) != 0);
+ case Id_dotAll:
+ return ScriptRuntime.wrapBoolean((re.flags & JSREG_DOTALL) != 0);
case Id_sticky:
return ScriptRuntime.wrapBoolean((re.flags & JSREG_STICKY) != 0);
}
@@ -2644,6 +2660,7 @@ protected void setInstanceIdValue(int id, Object value) {
case Id_global:
case Id_ignoreCase:
case Id_multiline:
+ case Id_dotAll:
case Id_sticky:
return;
}
@@ -2716,6 +2733,18 @@ public Object execIdCall(
return realThis(thisObj, f).compile(cx, scope, args);
case Id_toString:
+ // thisObj != scope is a strange hack but i had no better idea for the moment
+ if (thisObj != scope && thisObj instanceof NativeObject) {
+ Object sourceObj = thisObj.get("source", thisObj);
+ String source =
+ sourceObj.equals(NOT_FOUND) ? "undefined" : escapeRegExp(sourceObj);
+ Object flagsObj = thisObj.get("flags", thisObj);
+ String flags = flagsObj.equals(NOT_FOUND) ? "undefined" : flagsObj.toString();
+
+ return "/" + source + "/" + flags;
+ }
+ return realThis(thisObj, f).toString();
+
case Id_toSource:
return realThis(thisObj, f).toString();
diff --git a/src/org/mozilla/javascript/regexp/RegExpImpl.java b/src/org/mozilla/javascript/regexp/RegExpImpl.java
index df778b6da7..a2c388a1dd 100644
--- a/src/org/mozilla/javascript/regexp/RegExpImpl.java
+++ b/src/org/mozilla/javascript/regexp/RegExpImpl.java
@@ -66,6 +66,7 @@ public Object action(
}
case RA_REPLACE:
+ case RA_REPLACE_ALL:
{
boolean useRE = args.length > 0 && args[0] instanceof NativeRegExp;
@@ -78,6 +79,11 @@ public Object action(
String search = null;
if (useRE) {
re = createRegExp(cx, scope, args, 2, true);
+ if (RA_REPLACE_ALL == actionType
+ && (re.getFlags() & NativeRegExp.JSREG_GLOB) == 0) {
+ throw ScriptRuntime.typeError(
+ "replaceAll must be called with a global RegExp");
+ }
} else {
Object arg0 = args.length < 1 ? Undefined.instance : args[0];
search = ScriptRuntime.toString(arg0);
@@ -102,32 +108,54 @@ public Object action(
Object val;
if (useRE) {
- val = matchOrReplace(cx, scope, thisObj, args, this, data, re);
+ Object result = matchOrReplace(cx, scope, thisObj, args, this, data, re);
+ if (data.charBuf == null) {
+ if (data.global || result == null || !Boolean.TRUE.equals(result)) {
+ /* Didn't match even once. */
+ return data.str;
+ }
+ SubString lc = this.leftContext;
+ replace_glob(data, cx, scope, this, lc.index, lc.length);
+ }
} else {
- String str = data.str;
- int index = str.indexOf(search);
- if (index >= 0) {
- int slen = search.length();
+ final String str = data.str;
+ final int strLen = str.length(), searchLen = search.length();
+ int index = -1, lastIndex = 0;
+ for (; ; ) {
+ if (search.isEmpty()) {
+ if (index == -1) {
+ index = 0;
+ } else {
+ index = (lastIndex < strLen) ? lastIndex + 1 : -1;
+ }
+ } else {
+ index = str.indexOf(search, lastIndex);
+ }
+
+ if (index == -1) {
+ if (data.charBuf == null) {
+ return str;
+ }
+ break;
+ }
+
this.parens = null;
this.lastParen = null;
this.leftContext = new SubString(str, 0, index);
- this.lastMatch = new SubString(str, index, slen);
+ this.lastMatch = new SubString(str, index, searchLen);
this.rightContext =
- new SubString(str, index + slen, str.length() - index - slen);
- val = Boolean.TRUE;
- } else {
- val = Boolean.FALSE;
- }
- }
+ new SubString(
+ str, index + searchLen, strLen - index - searchLen);
+
+ replace_glob(data, cx, scope, this, lastIndex, index - lastIndex);
+ lastIndex = index + searchLen;
- if (data.charBuf == null) {
- if (data.global || val == null || !val.equals(Boolean.TRUE)) {
- /* Didn't match even once. */
- return data.str;
+ if (actionType != RA_REPLACE_ALL) {
+ break;
+ }
}
- SubString lc = this.leftContext;
- replace_glob(data, cx, scope, this, lc.index, lc.length);
}
+
SubString rc = this.rightContext;
data.charBuf.append(rc.str, rc.index, rc.index + rc.length);
return data.charBuf.toString();
@@ -192,7 +220,7 @@ private static Object matchOrReplace(
if (data.mode == RA_MATCH) {
match_glob(data, cx, scope, count, reImpl);
} else {
- if (data.mode != RA_REPLACE) Kit.codeBug();
+ if (data.mode != RA_REPLACE && data.mode != RA_REPLACE_ALL) Kit.codeBug();
SubString lastMatch = reImpl.lastMatch;
int leftIndex = data.leftIndex;
int leftlen = lastMatch.index - leftIndex;
diff --git a/testsrc/org/mozilla/javascript/tests/NativeRegExpTest.java b/testsrc/org/mozilla/javascript/tests/NativeRegExpTest.java
index 944629438d..c9816fdb72 100644
--- a/testsrc/org/mozilla/javascript/tests/NativeRegExpTest.java
+++ b/testsrc/org/mozilla/javascript/tests/NativeRegExpTest.java
@@ -28,157 +28,223 @@ public void openBrace() {
/** @throws Exception if an error occurs */
@Test
public void globalCtor() throws Exception {
- testEvaluate("g-true-false-false-false", "new RegExp('foo', 'g');");
+ testEvaluate("g-true-false-false-false-false", "new RegExp('foo', 'g');");
}
/** @throws Exception if an error occurs */
@Test
public void global() throws Exception {
- testEvaluate("g-true-false-false-false", "/foo/g;");
+ testEvaluate("g-true-false-false-false-false", "/foo/g;");
}
/** @throws Exception if an error occurs */
@Test
public void ignoreCaseCtor() throws Exception {
- testEvaluate("i-false-true-false-false", "new RegExp('foo', 'i');");
+ testEvaluate("i-false-true-false-false-false", "new RegExp('foo', 'i');");
}
/** @throws Exception if an error occurs */
@Test
public void ignoreCase() throws Exception {
- testEvaluate("i-false-true-false-false", "/foo/i;");
+ testEvaluate("i-false-true-false-false-false", "/foo/i;");
}
/** @throws Exception if an error occurs */
@Test
public void multilineCtor() throws Exception {
- testEvaluate("m-false-false-true-false", "new RegExp('foo', 'm');");
+ testEvaluate("m-false-false-true-false-false", "new RegExp('foo', 'm');");
}
/** @throws Exception if an error occurs */
@Test
public void multiline() throws Exception {
- testEvaluate("m-false-false-true-false", "/foo/m;");
+ testEvaluate("m-false-false-true-false-false", "/foo/m;");
+ }
+
+ /** @throws Exception if an error occurs */
+ @Test
+ public void dotAllCtor() throws Exception {
+ testEvaluate("s-false-false-false-true-false", "new RegExp('foo', 's');");
+ }
+
+ /** @throws Exception if an error occurs */
+ @Test
+ public void dotAll() throws Exception {
+ testEvaluate("s-false-false-false-true-false", "/foo/s;");
}
/** @throws Exception if an error occurs */
@Test
public void stickyCtor() throws Exception {
- testEvaluate("y-false-false-false-true", "new RegExp('foo', 'y');");
+ testEvaluate("y-false-false-false-false-true", "new RegExp('foo', 'y');");
}
/** @throws Exception if an error occurs */
@Test
public void sticky() throws Exception {
- testEvaluate("y-false-false-false-true", "/foo/y;");
+ testEvaluate("y-false-false-false-false-true", "/foo/y;");
}
/** @throws Exception if an error occurs */
@Test
public void globalMultilineCtor() throws Exception {
- testEvaluate("gm-true-false-true-false", "new RegExp('foo', 'gm');");
+ testEvaluate("gm-true-false-true-false-false", "new RegExp('foo', 'gm');");
}
/** @throws Exception if an error occurs */
@Test
public void globalMultiline() throws Exception {
- testEvaluate("gm-true-false-true-false", "/foo/gm;");
+ testEvaluate("gm-true-false-true-false-false", "/foo/gm;");
+ }
+
+ /** @throws Exception if an error occurs */
+ @Test
+ public void globalDotAll() throws Exception {
+ testEvaluate("gs-true-false-false-true-false", "/foo/gs;");
}
/** @throws Exception if an error occurs */
@Test
public void globalIgnoreCaseCtor() throws Exception {
- testEvaluate("gi-true-true-false-false", "new RegExp('foo', 'ig');");
+ testEvaluate("gi-true-true-false-false-false", "new RegExp('foo', 'ig');");
}
/** @throws Exception if an error occurs */
@Test
public void globalIgnoreCase() throws Exception {
- testEvaluate("gi-true-true-false-false", "/foo/ig;");
+ testEvaluate("gi-true-true-false-false-false", "/foo/ig;");
}
/** @throws Exception if an error occurs */
@Test
public void globalStickyCtor() throws Exception {
- testEvaluate("gy-true-false-false-true", "new RegExp('foo', 'gy');");
+ testEvaluate("gy-true-false-false-false-true", "new RegExp('foo', 'gy');");
}
/** @throws Exception if an error occurs */
@Test
public void globalSticky() throws Exception {
- testEvaluate("gy-true-false-false-true", "/foo/gy;");
+ testEvaluate("gy-true-false-false-false-true", "/foo/gy;");
}
/** @throws Exception if an error occurs */
@Test
public void globalMultilineIgnoreCaseCtor() throws Exception {
- testEvaluate("gim-true-true-true-false", "new RegExp('foo', 'mig');");
+ testEvaluate("gim-true-true-true-false-false", "new RegExp('foo', 'mig');");
}
/** @throws Exception if an error occurs */
@Test
public void globalMultilineIgnoreCase() throws Exception {
- testEvaluate("gim-true-true-true-false", "/foo/gmi;");
+ testEvaluate("gim-true-true-true-false-false", "/foo/gmi;");
+ }
+
+ /** @throws Exception if an error occurs */
+ @Test
+ public void globalDotAllIgnoreCaseCtor() throws Exception {
+ testEvaluate("gis-true-true-false-true-false", "new RegExp('foo', 'gsi');");
+ }
+
+ /** @throws Exception if an error occurs */
+ @Test
+ public void globalDotAllIgnoreCase() throws Exception {
+ testEvaluate("gis-true-true-false-true-false", "/foo/gsi;");
}
/** @throws Exception if an error occurs */
@Test
public void globalIgnoreCaseStickyCtor() throws Exception {
- testEvaluate("giy-true-true-false-true", "new RegExp('foo', 'yig');");
+ testEvaluate("giy-true-true-false-false-true", "new RegExp('foo', 'yig');");
}
/** @throws Exception if an error occurs */
@Test
public void globalIgnoreCaseSticky() throws Exception {
- testEvaluate("giy-true-true-false-true", "/foo/ygi;");
+ testEvaluate("giy-true-true-false-false-true", "/foo/ygi;");
}
/** @throws Exception if an error occurs */
@Test
public void globalMultilineStickyCtor() throws Exception {
- testEvaluate("gmy-true-false-true-true", "new RegExp('foo', 'gmy');");
+ testEvaluate("gmy-true-false-true-false-true", "new RegExp('foo', 'gmy');");
}
/** @throws Exception if an error occurs */
@Test
public void globalMultilineSticky() throws Exception {
- testEvaluate("gmy-true-false-true-true", "/foo/gmy;");
+ testEvaluate("gmy-true-false-true-false-true", "/foo/gmy;");
+ }
+
+ /** @throws Exception if an error occurs */
+ @Test
+ public void globalDotAllStickyCtor() throws Exception {
+ testEvaluate("gsy-true-false-false-true-true", "new RegExp('foo', 'gys');");
+ }
+
+ /** @throws Exception if an error occurs */
+ @Test
+ public void globalDotAllSticky() throws Exception {
+ testEvaluate("gsy-true-false-false-true-true", "/foo/gys;");
}
/** @throws Exception if an error occurs */
@Test
public void ignoreCaseMultilineCtor() throws Exception {
- testEvaluate("im-false-true-true-false", "new RegExp('foo', 'im');");
+ testEvaluate("im-false-true-true-false-false", "new RegExp('foo', 'im');");
}
/** @throws Exception if an error occurs */
@Test
public void ignoreCaseMultiline() throws Exception {
- testEvaluate("im-false-true-true-false", "/foo/mi;");
+ testEvaluate("im-false-true-true-false-false", "/foo/mi;");
+ }
+
+ /** @throws Exception if an error occurs */
+ @Test
+ public void ignoreCaseDotAllCtor() throws Exception {
+ testEvaluate("is-false-true-false-true-false", "new RegExp('foo', 'si');");
+ }
+
+ /** @throws Exception if an error occurs */
+ @Test
+ public void ignoreCaseDotAll() throws Exception {
+ testEvaluate("is-false-true-false-true-false", "/foo/si;");
}
/** @throws Exception if an error occurs */
@Test
public void ignoreCaseStickyCtor() throws Exception {
- testEvaluate("iy-false-true-false-true", "new RegExp('foo', 'yi');");
+ testEvaluate("iy-false-true-false-false-true", "new RegExp('foo', 'yi');");
}
/** @throws Exception if an error occurs */
@Test
public void ignoreCaseSticky() throws Exception {
- testEvaluate("iy-false-true-false-true", "/foo/iy;");
+ testEvaluate("iy-false-true-false-false-true", "/foo/iy;");
}
/** @throws Exception if an error occurs */
@Test
public void multilineStickyCtor() throws Exception {
- testEvaluate("my-false-false-true-true", "new RegExp('foo', 'my');");
+ testEvaluate("my-false-false-true-false-true", "new RegExp('foo', 'my');");
}
/** @throws Exception if an error occurs */
@Test
public void multilineSticky() throws Exception {
- testEvaluate("my-false-false-true-true", "/foo/my;");
+ testEvaluate("my-false-false-true-false-true", "/foo/my;");
+ }
+
+ /** @throws Exception if an error occurs */
+ @Test
+ public void dotAllStickyCtor() throws Exception {
+ testEvaluate("sy-false-false-false-true-true", "new RegExp('foo', 'ys');");
+ }
+
+ /** @throws Exception if an error occurs */
+ @Test
+ public void dotAllSticky() throws Exception {
+ testEvaluate("sy-false-false-false-true-true", "/foo/ys;");
}
private static void testEvaluate(final String expected, final String regex) {
@@ -195,6 +261,8 @@ private static void testEvaluate(final String expected, final String regex) {
+ "res += '-';\n"
+ "res += regex.multiline;\n"
+ "res += '-';\n"
+ + "res += regex.dotAll;\n"
+ + "res += '-';\n"
+ "res += regex.sticky;\n"
+ "res";
@@ -268,6 +336,17 @@ public void matchGlobalSymbol() throws Exception {
test("3-a-a-a", script);
}
+ /** @throws Exception if an error occurs */
+ @Test
+ public void matchDotAll() throws Exception {
+ final String script =
+ "var result = 'bar\\nfoo'.match(/bar.foo/s);\n"
+ + "var res = '' + result.length;\n"
+ + "res = res + '-' + result[0];\n"
+ + "res;";
+ test("1-bar\nfoo", script);
+ }
+
/** @throws Exception if an error occurs */
@Test
public void matchSticky() throws Exception {
@@ -328,6 +407,26 @@ public void flagsPropery() throws Exception {
test("0-undefined-true-false-undefined", script);
}
+ /** @throws Exception if an error occurs */
+ @Test
+ public void objectToString() throws Exception {
+ test("/undefined/undefined", "RegExp.prototype.toString.call({})");
+ test("/Foo/undefined", "RegExp.prototype.toString.call({source: 'Foo'})");
+ test("/undefined/gy", "RegExp.prototype.toString.call({flags: 'gy'})");
+ test("/Foo/g", "RegExp.prototype.toString.call({source: 'Foo', flags: 'g'})");
+ test("/Foo/g", "RegExp.prototype.toString.call({source: 'Foo', flags: 'g', sticky: true})");
+
+ test(
+ "TypeError: Method \"toString\" called on incompatible object",
+ "try { RegExp.prototype.toString.call(''); } catch (e) { ('' + e).substr(0, 58) }");
+ test(
+ "TypeError: Method \"toString\" called on incompatible object",
+ "try { RegExp.prototype.toString.call(undefined); } catch (e) { ('' + e).substr(0, 58) }");
+ test(
+ "TypeError: Method \"toString\" called on incompatible object",
+ "var toString = RegExp.prototype.toString; try { toString(); } catch (e) { ('' + e).substr(0, 58) }");
+ }
+
private static void test(final String expected, final String script) {
Utils.runWithAllOptimizationLevels(
cx -> {
diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeString2Test.java b/testsrc/org/mozilla/javascript/tests/es6/NativeString2Test.java
index 97c64d4616..b31a16bf7f 100644
--- a/testsrc/org/mozilla/javascript/tests/es6/NativeString2Test.java
+++ b/testsrc/org/mozilla/javascript/tests/es6/NativeString2Test.java
@@ -8,11 +8,10 @@
package org.mozilla.javascript.tests.es6;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.mozilla.javascript.Context;
-import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.tests.Utils;
/** Test for handling const variables. */
@@ -20,378 +19,281 @@ public class NativeString2Test {
@Test
public void getOwnPropertyDescriptorWithIndex() {
- Utils.runWithAllOptimizationLevels(
- cx -> {
- cx.setLanguageVersion(Context.VERSION_ES6);
- ScriptableObject scope = cx.initStandardObjects();
-
- Object result =
- cx.evaluateString(
- scope,
- " var res = 'hello'.hasOwnProperty('0');"
- + " res += ';';"
- + " desc = Object.getOwnPropertyDescriptor('hello', '0');"
- + " res += desc.value;"
- + " res += ';';"
- + " res += desc.writable;"
- + " res += ';';"
- + " res += desc.enumerable;"
- + " res += ';';"
- + " res += desc.configurable;"
- + " res += ';';"
- + " res;",
- "test",
- 1,
- null);
- assertEquals("true;h;false;true;false;", result);
-
- return null;
- });
+ String js =
+ " var res = 'hello'.hasOwnProperty('0');"
+ + " res += ';';"
+ + " desc = Object.getOwnPropertyDescriptor('hello', '0');"
+ + " res += desc.value;"
+ + " res += ';';"
+ + " res += desc.writable;"
+ + " res += ';';"
+ + " res += desc.enumerable;"
+ + " res += ';';"
+ + " res += desc.configurable;"
+ + " res += ';';"
+ + " res;";
+ assertEvaluatesES6("true;h;false;true;false;", js);
}
@Test
public void normalizeNoParam() {
- Utils.runWithAllOptimizationLevels(
- cx -> {
- cx.setLanguageVersion(Context.VERSION_ES6);
- ScriptableObject scope = cx.initStandardObjects();
-
- Object result = cx.evaluateString(scope, "'123'.normalize()", "test", 1, null);
- assertEquals("123", result);
-
- return null;
- });
+ assertEvaluates("123", "'123'.normalize()");
}
@Test
public void normalizeNoUndefined() {
- Utils.runWithAllOptimizationLevels(
- cx -> {
- cx.setLanguageVersion(Context.VERSION_ES6);
- ScriptableObject scope = cx.initStandardObjects();
-
- Object result =
- cx.evaluateString(scope, "'123'.normalize(undefined)", "test", 1, null);
- assertEquals("123", result);
-
- return null;
- });
+ assertEvaluates("123", "'123'.normalize(undefined)");
}
@Test
public void normalizeNoNull() {
- Utils.runWithAllOptimizationLevels(
- cx -> {
- cx.setLanguageVersion(Context.VERSION_ES6);
- ScriptableObject scope = cx.initStandardObjects();
-
- Object result =
- cx.evaluateString(
- scope,
- "try { "
- + " '123'.normalize(null);"
- + "} catch (e) { e.message }",
- "test",
- 1,
- null);
- assertEquals(
- "The normalization form should be one of 'NFC', 'NFD', 'NFKC', 'NFKD'.",
- result);
-
- return null;
- });
+ String js = "try { " + " '123'.normalize(null);" + "} catch (e) { e.message }";
+ assertEvaluates(
+ "The normalization form should be one of 'NFC', 'NFD', 'NFKC', 'NFKD'.", js);
}
@Test
public void replaceReplacementAsString() {
- Utils.runWithAllOptimizationLevels(
- cx -> {
- cx.setLanguageVersion(Context.VERSION_ES6);
- ScriptableObject scope = cx.initStandardObjects();
-
- Object result =
- cx.evaluateString(scope, "'123'.replace('2', /x/);", "test", 1, null);
- assertEquals("1/x/3", result);
-
- return null;
- });
+ assertEvaluates("1null3", "'123'.replace('2', /x/);");
+ assertEvaluatesES6("1/x/3", "'123'.replace('2', /x/);");
}
@Test
public void indexOfEmpty() {
- Utils.runWithAllOptimizationLevels(
- cx -> {
- cx.setLanguageVersion(Context.VERSION_ES6);
- ScriptableObject scope = cx.initStandardObjects();
-
- Object result =
- cx.evaluateString(scope, "'1234'.indexOf('', 0);", "test", 1, null);
- assertEquals(0, result);
-
- result = cx.evaluateString(scope, "'1234'.indexOf('', 1);", "test", 1, null);
- assertEquals(1, result);
-
- result = cx.evaluateString(scope, "'1234'.indexOf('', 4);", "test", 1, null);
- assertEquals(4, result);
-
- result = cx.evaluateString(scope, "'1234'.indexOf('', 5);", "test", 1, null);
- assertEquals(4, result);
-
- result = cx.evaluateString(scope, "'1234'.indexOf('', 42);", "test", 1, null);
- assertEquals(4, result);
-
- return null;
- });
+ assertEvaluates(0, "'1234'.indexOf('', 0);");
+ assertEvaluates(1, "'1234'.indexOf('', 1);");
+ assertEvaluates(4, "'1234'.indexOf('', 4);");
+ assertEvaluates(4, "'1234'.indexOf('', 5);");
+ assertEvaluates(4, "'1234'.indexOf('', 42);");
}
@Test
public void includesEmpty() {
- Utils.runWithAllOptimizationLevels(
- cx -> {
- cx.setLanguageVersion(Context.VERSION_ES6);
- ScriptableObject scope = cx.initStandardObjects();
-
- Boolean result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.includes('');", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.includes('', 0);", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.includes('', 1);", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.includes('', 4);", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.includes('', 5);", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.includes('', 42);", "test", 1, null);
- assertTrue(result);
+ assertEvaluates(true, "'1234'.includes('');");
+ assertEvaluates(true, "'1234'.includes('', 0);");
+ assertEvaluates(true, "'1234'.includes('', 1);");
+ assertEvaluates(true, "'1234'.includes('', 4);");
+ assertEvaluates(true, "'1234'.includes('', 5);");
+ assertEvaluates(true, "'1234'.includes('', 42);");
+ }
- return null;
- });
+ @Test
+ public void includesRegExpMatch() {
+ String js =
+ "var regExp = /./;\n"
+ + "var res = '';\n"
+ + "try {\n"
+ + " res += '/./'.includes(regExp);\n"
+ + "} catch (e) {\n"
+ + " res += e;\n"
+ + "}\n"
+ + "regExp[Symbol.match] = false;\n"
+ + "res += ' # ' + '/./'.includes(regExp);\n"
+ + "res;";
+
+ assertEvaluatesES6(
+ "TypeError: First argument to String.prototype.includes must not be a regular expression # true",
+ js);
}
@Test
public void startsWithEmpty() {
- Utils.runWithAllOptimizationLevels(
- cx -> {
- cx.setLanguageVersion(Context.VERSION_ES6);
- ScriptableObject scope = cx.initStandardObjects();
-
- Boolean result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.startsWith('');", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.startsWith('', 0);", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.startsWith('', 1);", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.startsWith('', 4);", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.startsWith('', 5);", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.startsWith('', 42);", "test", 1, null);
- assertTrue(result);
+ assertEvaluates(true, "'1234'.startsWith('');");
+ assertEvaluates(true, "'1234'.startsWith('', 0);");
+ assertEvaluates(true, "'1234'.startsWith('', 1);");
+ assertEvaluates(true, "'1234'.startsWith('', 4);");
+ assertEvaluates(true, "'1234'.startsWith('', 5);");
+ assertEvaluates(true, "'1234'.startsWith('', 42);");
+ }
- return null;
- });
+ @Test
+ public void startsWithRegExpMatch() {
+ String js =
+ "var regExp = /./;\n"
+ + "var res = '';\n"
+ + "try {\n"
+ + " res += '/./'.startsWith(regExp);\n"
+ + "} catch (e) {\n"
+ + " res += e;\n"
+ + "}\n"
+ + "regExp[Symbol.match] = false;\n"
+ + "res += ' # ' + '/./'.includes(regExp);\n"
+ + "res;";
+
+ assertEvaluatesES6(
+ "TypeError: First argument to String.prototype.startsWith must not be a regular expression # true",
+ js);
}
@Test
public void endsWithEmpty() {
- Utils.runWithAllOptimizationLevels(
- cx -> {
- cx.setLanguageVersion(Context.VERSION_ES6);
- ScriptableObject scope = cx.initStandardObjects();
-
- Boolean result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.endsWith('');", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.endsWith('', 0);", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.endsWith('', 1);", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.endsWith('', 4);", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.endsWith('', 5);", "test", 1, null);
- assertTrue(result);
-
- result =
- (Boolean)
- cx.evaluateString(
- scope, "'1234'.endsWith('', 42);", "test", 1, null);
- assertTrue(result);
+ assertEvaluates(true, "'1234'.endsWith('');");
+ assertEvaluates(true, "'1234'.endsWith('', 0);");
+ assertEvaluates(true, "'1234'.endsWith('', 1);");
+ assertEvaluates(true, "'1234'.endsWith('', 4);");
+ assertEvaluates(true, "'1234'.endsWith('', 5);");
+ assertEvaluates(true, "'1234'.endsWith('', 42);");
+ }
- return null;
- });
+ @Test
+ public void endsWithRegExpMatch() {
+ String js =
+ "var regExp = /./;\n"
+ + "var res = '';\n"
+ + "try {\n"
+ + " res += '/./'.startsWith(regExp);\n"
+ + "} catch (e) {\n"
+ + " res += e;\n"
+ + "}\n"
+ + "regExp[Symbol.match] = false;\n"
+ + "res += ' # ' + '/./'.includes(regExp);\n"
+ + "res;";
+
+ assertEvaluatesES6(
+ "TypeError: First argument to String.prototype.startsWith must not be a regular expression # true",
+ js);
}
@Test
public void tagify() {
- Utils.runWithAllOptimizationLevels(
- cx -> {
- cx.setLanguageVersion(Context.VERSION_ES6);
- ScriptableObject scope = cx.initStandardObjects();
-
- Object result = cx.evaluateString(scope, "'tester'.big()", "test", 1, null);
- assertEquals("tester", result);
-
- result = cx.evaluateString(scope, "'\"tester\"'.big()", "test", 1, null);
- assertEquals("\"tester\"", result);
-
- result = cx.evaluateString(scope, "'\"tester\"'.big()", "test", 1, null);
- assertEquals("\"tester\"", result);
-
- result = cx.evaluateString(scope, "'tester'.fontsize()", "test", 1, null);
- assertEquals("tester", result);
-
- result = cx.evaluateString(scope, "'tester'.fontsize(null)", "test", 1, null);
- assertEquals("tester", result);
-
- result =
- cx.evaluateString(
- scope, "'tester'.fontsize(undefined)", "test", 1, null);
- assertEquals("tester", result);
+ assertEvaluates("tester", "'tester'.big()");
+ assertEvaluates("\"tester\"", "'\"tester\"'.big()");
+ assertEvaluates("tester", "'tester'.fontsize()");
+ assertEvaluates("tester", "'tester'.fontsize(null)");
+ assertEvaluates("tester", "'tester'.fontsize(undefined)");
+ assertEvaluates("tester", "'tester'.fontsize(123)");
+ assertEvaluates(
+ "tester", "'tester'.fontsize('\"123\"')");
+ }
- result = cx.evaluateString(scope, "'tester'.fontsize(123)", "test", 1, null);
- assertEquals("tester", result);
+ @Test
+ public void tagifyPrototypeNull() {
+ for (String call :
+ new String[] {
+ "big",
+ "blink",
+ "bold",
+ "fixed",
+ "fontcolor",
+ "fontsize",
+ "italics",
+ "link",
+ "small",
+ "strike",
+ "sub",
+ "sup"
+ }) {
+ String js = "try { String.prototype." + call + ".call(null);} catch (e) { e.message }";
+ String expected = "String.prototype." + call + " method called on null or undefined";
+
+ assertEvaluatesES6(expected, js);
+ }
+ }
- result =
- cx.evaluateString(
- scope, "'tester'.fontsize('\"123\"')", "test", 1, null);
- assertEquals("tester", result);
+ @Test
+ public void tagifyPrototypeUndefined() {
+ for (String call :
+ new String[] {
+ "big",
+ "blink",
+ "bold",
+ "fixed",
+ "fontcolor",
+ "fontsize",
+ "italics",
+ "link",
+ "small",
+ "strike",
+ "sub",
+ "sup"
+ }) {
+ String js =
+ "try { String.prototype." + call + ".call(undefined);} catch (e) { e.message }";
+ String expected = "String.prototype." + call + " method called on null or undefined";
+
+ assertEvaluatesES6(expected, js);
+ }
+ }
- return null;
- });
+ @Test
+ public void stringReplace() {
+ assertEvaluates("xyz", "''.replace('', 'xyz')");
+ assertEvaluates("1", "'121'.replace('21', '')");
+ assertEvaluates("xyz121", "'121'.replace('', 'xyz')");
+ assertEvaluates("a$c21", "'121'.replace('1', 'a$c')");
+ assertEvaluates("a121", "'121'.replace('1', 'a$&')");
+ assertEvaluates("a$c21", "'121'.replace('1', 'a$$c')");
+ assertEvaluates("abaabe", "'abcde'.replace('cd', 'a$`')");
+ assertEvaluates("a21", "'121'.replace('1', 'a$`')");
+ assertEvaluates("abaee", "'abcde'.replace('cd', \"a$'\")");
+ assertEvaluates("aba", "'abcd'.replace('cd', \"a$'\")");
+ assertEvaluates("aba$0", "'abcd'.replace('cd', 'a$0')");
+ assertEvaluates("aba$1", "'abcd'.replace('cd', 'a$1')");
+ assertEvaluates(
+ "abCD",
+ "'abcd'.replace('cd', function (matched) { return matched.toUpperCase() })");
+ assertEvaluates("", "'123456'.replace(/\\d+/, '')");
+ assertEvaluates(
+ "123ABCD321abcd",
+ "'123abcd321abcd'.replace(/[a-z]+/, function (matched) { return matched.toUpperCase() })");
}
@Test
- public void tagifyPrototypeNull() {
+ public void stringReplaceAll() {
+ assertEvaluates("xyz", "''.replaceAll('', 'xyz')");
+ assertEvaluates("1", "'12121'.replaceAll('21', '')");
+ assertEvaluates("xyz1xyz2xyz1xyz", "'121'.replaceAll('', 'xyz')");
+ assertEvaluates("a$c2a$c", "'121'.replaceAll('1', 'a$c')");
+ assertEvaluates("a12a1", "'121'.replaceAll('1', 'a$&')");
+ assertEvaluates("a$c2a$c", "'121'.replaceAll('1', 'a$$c')");
+ assertEvaluates("aaadaaabcda", "'abcdabc'.replaceAll('bc', 'a$`')");
+ assertEvaluates("a2a12", "'121'.replaceAll('1', 'a$`')");
+ assertEvaluates("aadabcdaa", "'abcdabc'.replaceAll('bc', \"a$'\")");
+ assertEvaluates("aadabcdaa", "'abcdabc'.replaceAll('bc', \"a$'\")");
+ assertEvaluates("aa$0daa$0", "'abcdabc'.replaceAll('bc', 'a$0')");
+ assertEvaluates("aa$1daa$1", "'abcdabc'.replaceAll('bc', 'a$1')");
+ assertEvaluates("", "'123456'.replaceAll(/\\d+/g, '')");
+ assertEvaluates("123456", "'123456'.replaceAll(undefined, '')");
+ assertEvaluates("afoobarb", "'afoob'.replaceAll(/(foo)/g, '$1bar')");
+ assertEvaluates("foobarb", "'foob'.replaceAll(/(foo)/gy, '$1bar')");
+ assertEvaluates("hllo", "'hello'.replaceAll(/(h)e/gy, '$1')");
+ assertEvaluates("$1llo", "'hello'.replaceAll(/he/g, '$1')");
+ assertEvaluates(
+ "I$want$these$periods$to$be$$s",
+ "'I.want.these.periods.to.be.$s'.replaceAll(/\\./g, '$')");
+ assertEvaluates("food bar", "'foo bar'.replaceAll(/foo/g, '$&d')");
+ assertEvaluates("foo foo ", "'foo bar'.replaceAll(/bar/g, '$`')");
+ assertEvaluates(" bar bar", "'foo bar'.replaceAll(/foo/g, '$\\'')");
+ assertEvaluates("$' bar", "'foo bar'.replaceAll(/foo/g, '$$\\'')");
+ assertEvaluates("ad$0db", "'afoob'.replaceAll(/(foo)/g, 'd$0d')");
+ assertEvaluates("ad$0db", "'afkxxxkob'.replace(/(f)k(.*)k(o)/g, 'd$0d')");
+ assertEvaluates("ad$0dbd$0dc", "'afoobfuoc'.replaceAll(/(f.o)/g, 'd$0d')");
+ assertEvaluates(
+ "123FOOBAR321BARFOO123",
+ "'123foobar321barfoo123'.replace(/[a-z]+/g, function (matched) { return matched.toUpperCase() })");
+
+ assertEvaluates(
+ "TypeError: replaceAll must be called with a global RegExp",
+ "try { 'hello'.replaceAll(/he/i, 'x'); } catch (e) { '' + e }");
+ }
+
+ private static void assertEvaluates(final Object expected, final String source) {
Utils.runWithAllOptimizationLevels(
cx -> {
- cx.setLanguageVersion(Context.VERSION_ES6);
- ScriptableObject scope = cx.initStandardObjects();
-
- for (String call :
- new String[] {
- "big",
- "blink",
- "bold",
- "fixed",
- "fontcolor",
- "fontsize",
- "italics",
- "link",
- "small",
- "strike",
- "sub",
- "sup"
- }) {
- String code =
- "try { String.prototype."
- + call
- + ".call(null);} catch (e) { e.message }";
- Object result = cx.evaluateString(scope, code, "test", 1, null);
- assertEquals(
- "String.prototype." + call + " method called on null or undefined",
- result);
- }
-
+ final Scriptable scope = cx.initStandardObjects();
+ final Object rep = cx.evaluateString(scope, source, "test.js", 0, null);
+ assertEquals(expected, rep);
return null;
});
}
- @Test
- public void tagifyPrototypeUndefined() {
+ private static void assertEvaluatesES6(final Object expected, final String source) {
Utils.runWithAllOptimizationLevels(
cx -> {
cx.setLanguageVersion(Context.VERSION_ES6);
- ScriptableObject scope = cx.initStandardObjects();
-
- for (String call :
- new String[] {
- "big",
- "blink",
- "bold",
- "fixed",
- "fontcolor",
- "fontsize",
- "italics",
- "link",
- "small",
- "strike",
- "sub",
- "sup"
- }) {
- String code =
- "try { String.prototype."
- + call
- + ".call(undefined);} catch (e) { e.message }";
- Object result = cx.evaluateString(scope, code, "test", 1, null);
- assertEquals(
- "String.prototype." + call + " method called on null or undefined",
- result);
- }
-
+ final Scriptable scope = cx.initStandardObjects();
+ final Object rep = cx.evaluateString(scope, source, "test.js", 0, null);
+ assertEquals(expected, rep);
return null;
});
}
diff --git a/testsrc/test262.properties b/testsrc/test262.properties
index 626371927d..332b9c6711 100644
--- a/testsrc/test262.properties
+++ b/testsrc/test262.properties
@@ -1552,7 +1552,7 @@ built-ins/SetIteratorPrototype 0/11 (0.0%)
~built-ins/SharedArrayBuffer
-built-ins/String 120/1114 (10.77%)
+built-ins/String 99/1114 (8.89%)
prototype/endsWith/return-abrupt-from-searchstring-regexp-test.js
prototype/includes/return-abrupt-from-searchstring-regexp-test.js
prototype/indexOf/position-tointeger-bigint.js {unsupported: [computed-property-names]}
@@ -1567,7 +1567,25 @@ built-ins/String 120/1114 (10.77%)
prototype/match/cstm-matcher-get-err.js
prototype/match/cstm-matcher-invocation.js
prototype/match/invoke-builtin-match.js
- prototype/replaceAll 40/40 (100.0%)
+ prototype/replaceAll/getSubstitution-0x0024-0x003C.js
+ prototype/replaceAll/getSubstitution-0x0024N.js
+ prototype/replaceAll/getSubstitution-0x0024NN.js
+ prototype/replaceAll/replaceValue-call-each-match-position.js
+ prototype/replaceAll/replaceValue-call-matching-empty.js
+ prototype/replaceAll/replaceValue-value-tostring.js
+ prototype/replaceAll/searchValue-flags-no-g-throws.js
+ prototype/replaceAll/searchValue-flags-null-undefined-throws.js
+ prototype/replaceAll/searchValue-flags-toString-abrupt.js
+ prototype/replaceAll/searchValue-get-flags-abrupt.js
+ prototype/replaceAll/searchValue-isRegExp-abrupt.js
+ prototype/replaceAll/searchValue-replacer-before-tostring.js
+ prototype/replaceAll/searchValue-replacer-call.js
+ prototype/replaceAll/searchValue-replacer-call-abrupt.js
+ prototype/replaceAll/searchValue-replacer-method-abrupt.js
+ prototype/replaceAll/searchValue-replacer-RegExp-call.js {unsupported: [class]}
+ prototype/replaceAll/searchValue-replacer-RegExp-call-fn.js {unsupported: [class]}
+ prototype/replaceAll/searchValue-tostring-regexp.js
+ prototype/replaceAll/this-tostring.js
prototype/replace/cstm-replace-get-err.js
prototype/replace/cstm-replace-invocation.js
prototype/replace/S15.5.4.11_A12.js non-strict