Skip to content

Commit

Permalink
NativeRegExp: implement dotAll flag
Browse files Browse the repository at this point in the history
  • Loading branch information
duonglaiquang committed Jan 4, 2024
1 parent a9ccd92 commit afb1962
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 30 deletions.
1 change: 1 addition & 0 deletions src/org/mozilla/javascript/TokenStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -1496,6 +1496,7 @@ 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('s')) addToString('s');
else if (matchChar('y')) // FireFox 3
addToString('y');
else break;
Expand Down
22 changes: 18 additions & 4 deletions src/org/mozilla/javascript/regexp/NativeRegExp.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,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;
Expand Down Expand Up @@ -197,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');
}

Expand Down Expand Up @@ -279,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 {
Expand Down Expand Up @@ -1710,7 +1714,7 @@ 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++;
}
Expand Down Expand Up @@ -2522,8 +2526,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() {
Expand Down Expand Up @@ -2552,6 +2557,9 @@ protected int findInstanceIdInfo(String s) {
case "multiline":
id = Id_multiline;
break;
case "dotAll":
id = Id_dotAll;
break;
case "sticky":
id = Id_sticky;
break;
Expand All @@ -2572,6 +2580,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;
Expand All @@ -2596,6 +2605,8 @@ protected String getInstanceIdName(int id) {
return "ignoreCase";
case Id_multiline:
return "multiline";
case Id_dotAll:
return "dotAll";
case Id_sticky:
return "sticky";
}
Expand All @@ -2621,6 +2632,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);
}
Expand All @@ -2645,6 +2658,7 @@ protected void setInstanceIdValue(int id, Object value) {
case Id_global:
case Id_ignoreCase:
case Id_multiline:
case Id_dotAll:
case Id_sticky:
return;
}
Expand Down
131 changes: 105 additions & 26 deletions testsrc/org/mozilla/javascript/tests/NativeRegExpTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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";

Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit afb1962

Please sign in to comment.