diff --git a/src/org/mozilla/javascript/TokenStream.java b/src/org/mozilla/javascript/TokenStream.java index 2e5292d5ba..bd520905e5 100644 --- a/src/org/mozilla/javascript/TokenStream.java +++ b/src/org/mozilla/javascript/TokenStream.java @@ -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; diff --git a/src/org/mozilla/javascript/regexp/NativeRegExp.java b/src/org/mozilla/javascript/regexp/NativeRegExp.java index 50d7f55760..4661ef1c58 100644 --- a/src/org/mozilla/javascript/regexp/NativeRegExp.java +++ b/src/org/mozilla/javascript/regexp/NativeRegExp.java @@ -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; @@ -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'); } @@ -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 { @@ -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++; } @@ -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() { @@ -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; @@ -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; @@ -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"; } @@ -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); } @@ -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; } diff --git a/testsrc/org/mozilla/javascript/tests/NativeRegExpTest.java b/testsrc/org/mozilla/javascript/tests/NativeRegExpTest.java index 47d8da1481..408614ca5b 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 {