Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix NPE when regexp search has no result #927

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/org/mozilla/javascript/regexp/NativeRegExp.java
Expand Up @@ -2690,7 +2690,7 @@ public Object execIdCall(
case SymbolId_search:
Scriptable scriptable =
(Scriptable) realThis(thisObj, f).execSub(cx, scope, args, MATCH);
return scriptable.get("index", scriptable);
return scriptable == null ? -1 : scriptable.get("index", scriptable);
}
throw new IllegalArgumentException(String.valueOf(id));
}
Expand Down
228 changes: 116 additions & 112 deletions testsrc/org/mozilla/javascript/tests/es6/NativeRegExpTest.java
Expand Up @@ -15,149 +15,153 @@
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.tests.Utils;

/**
* @author Ronald Brill
*/
/** @author Ronald Brill */
public class NativeRegExpTest {

@Test
public void regExIsCallableForBackwardCompatibility() {
Context cx = Context.enter();
cx.setLanguageVersion(Context.VERSION_1_8);
ScriptableObject scope = cx.initStandardObjects();
try (Context cx = Context.enter()) {
cx.setLanguageVersion(Context.VERSION_1_8);
ScriptableObject scope = cx.initStandardObjects();

String source = "var a = new RegExp('1'); a(1).toString();";
assertEquals("1", cx.evaluateString(scope, source, "test", 0, null));
String source = "var a = new RegExp('1'); a(1).toString();";
assertEquals("1", cx.evaluateString(scope, source, "test", 0, null));

source = "/^\\{(.*)\\}$/('{1234}').toString();";
assertEquals("{1234},1234", cx.evaluateString(scope, source, "test", 0, null));
source = "/^\\{(.*)\\}$/('{1234}').toString();";
assertEquals("{1234},1234", cx.evaluateString(scope, source, "test", 0, null));

source = "RegExp('a|b','g')()";
assertNull(cx.evaluateString(scope, source, "test", 0, null));
source = "RegExp('a|b','g')()";
assertNull(cx.evaluateString(scope, source, "test", 0, null));

source = "new /z/();";
assertNull(cx.evaluateString(scope, source, "test", 0, null));
source = "new /z/();";
assertNull(cx.evaluateString(scope, source, "test", 0, null));

source = "(new new RegExp).toString()";
assertEquals("", cx.evaluateString(scope, source, "test", 0, null));

Context.exit();
source = "(new new RegExp).toString()";
assertEquals("", cx.evaluateString(scope, source, "test", 0, null));
}
}


@Test
public void regExMinusInRangeBorderCases() {
Context cx = Context.enter();
cx.setLanguageVersion(Context.VERSION_1_8);
ScriptableObject scope = cx.initStandardObjects();
try (Context cx = Context.enter()) {
cx.setLanguageVersion(Context.VERSION_1_8);
ScriptableObject scope = cx.initStandardObjects();

String source = "var r = 'a-b_c d efg 1 23';\n"
+ "r.replace(/[_-]+/g, 'x');";
assertEquals("axbxc d efg 1 23", cx.evaluateString(scope, source, "test", 0, null));
String source = "var r = 'a-b_c d efg 1 23';\n" + "r.replace(/[_-]+/g, 'x');";
assertEquals("axbxc d efg 1 23", cx.evaluateString(scope, source, "test", 0, null));

source = "var r = 'a-b_c d efg 1 23';\n"
+ "r.replace(/[_-\\s]+/g, 'x');";
assertEquals("axbxcxdxefgx1x23", cx.evaluateString(scope, source, "test", 0, null));
source = "var r = 'a-b_c d efg 1 23';\n" + "r.replace(/[_-\\s]+/g, 'x');";
assertEquals("axbxcxdxefgx1x23", cx.evaluateString(scope, source, "test", 0, null));

source = "var r = 'a-b_c d efg 1 23';\n"
+ "r.replace(/[_-\\S]+/g, 'x');";
assertEquals("x x x x x", cx.evaluateString(scope, source, "test", 0, null));
source = "var r = 'a-b_c d efg 1 23';\n" + "r.replace(/[_-\\S]+/g, 'x');";
assertEquals("x x x x x", cx.evaluateString(scope, source, "test", 0, null));

source = "var r = 'a-b_c d efg 1 23';\n"
+ "r.replace(/[_-\\w]+/g, 'x');";
assertEquals("x x x x x", cx.evaluateString(scope, source, "test", 0, null));
source = "var r = 'a-b_c d efg 1 23';\n" + "r.replace(/[_-\\w]+/g, 'x');";
assertEquals("x x x x x", cx.evaluateString(scope, source, "test", 0, null));

source = "var r = 'a-b_c d efg 1 23';\n"
+ "r.replace(/[_-\\W]+/g, 'x');";
assertEquals("axbxcxdxefgx1x23", cx.evaluateString(scope, source, "test", 0, null));
source = "var r = 'a-b_c d efg 1 23';\n" + "r.replace(/[_-\\W]+/g, 'x');";
assertEquals("axbxcxdxefgx1x23", cx.evaluateString(scope, source, "test", 0, null));

source = "var r = 'a-b_c d efg 1 23';\n"
+ "r.replace(/[_-\\d]+/g, 'x');";
assertEquals("axbxc d efg x x", cx.evaluateString(scope, source, "test", 0, null));
source = "var r = 'a-b_c d efg 1 23';\n" + "r.replace(/[_-\\d]+/g, 'x');";
assertEquals("axbxc d efg x x", cx.evaluateString(scope, source, "test", 0, null));

source = "var r = 'a-b_c d efg 1 23';\n"
+ "r.replace(/[_-\\D]+/g, 'x');";
assertEquals("x1x23", cx.evaluateString(scope, source, "test", 0, null));
source = "var r = 'a-b_c d efg 1 23';\n" + "r.replace(/[_-\\D]+/g, 'x');";
assertEquals("x1x23", cx.evaluateString(scope, source, "test", 0, null));

source = "var r = 'a-b_c d efg 1 23';\n"
+ "r.replace(/[_-\\a]+/g, 'x');";
assertEquals("x-bxc d efg 1 23", cx.evaluateString(scope, source, "test", 0, null));

Context.exit();
source = "var r = 'a-b_c d efg 1 23';\n" + "r.replace(/[_-\\a]+/g, 'x');";
assertEquals("x-bxc d efg 1 23", cx.evaluateString(scope, source, "test", 0, null));
}
}

@Test
public void regExIsNotCallable() {
Context cx = Context.enter();
cx.setLanguageVersion(Context.VERSION_ES6);
ScriptableObject scope = cx.initStandardObjects();

String source = "var a = new RegExp('1'); a(1);";
try {
cx.evaluateString(scope, source, "test", 0, null);
fail();
}
catch (EcmaError e) {
// expected
assertTrue(e.getMessage(), e.getMessage().startsWith("TypeError: "));
}

source = "/^\\{(.*)\\}$/('{1234}');";
try {
cx.evaluateString(scope, source, "test", 0, null);
fail();
}
catch (EcmaError e) {
// expected
assertTrue(e.getMessage(), e.getMessage().startsWith("TypeError: "));
try (Context cx = Context.enter()) {
cx.setLanguageVersion(Context.VERSION_ES6);
ScriptableObject scope = cx.initStandardObjects();

String source = "var a = new RegExp('1'); a(1);";
try {
cx.evaluateString(scope, source, "test", 0, null);
fail();
} catch (EcmaError e) {
// expected
assertTrue(e.getMessage(), e.getMessage().startsWith("TypeError: "));
}

source = "/^\\{(.*)\\}$/('{1234}');";
try {
cx.evaluateString(scope, source, "test", 0, null);
fail();
} catch (EcmaError e) {
// expected
assertTrue(e.getMessage(), e.getMessage().startsWith("TypeError: "));
}

source = "RegExp('a|b','g')();";
try {
cx.evaluateString(scope, source, "test", 0, null);
fail();
} catch (EcmaError e) {
// expected
assertTrue(e.getMessage(), e.getMessage().startsWith("TypeError: "));
}

source = "new /z/();";
try {
cx.evaluateString(scope, source, "test", 0, null);
fail();
} catch (EcmaError e) {
// expected
assertTrue(e.getMessage(), e.getMessage().startsWith("TypeError: "));
}

source = "new new RegExp";
try {
cx.evaluateString(scope, source, "test", 0, null);
fail();
} catch (EcmaError e) {
// expected
assertTrue(e.getMessage(), e.getMessage().startsWith("TypeError: "));
}
}
}

source = "RegExp('a|b','g')();";
try {
cx.evaluateString(scope, source, "test", 0, null);
fail();
}
catch (EcmaError e) {
// expected
assertTrue(e.getMessage(), e.getMessage().startsWith("TypeError: "));
}
@Test
public void lastIndexReadonly() {
final String script =
"try { "
+ " var r = /c/g;"
+ " Object.defineProperty(r, 'lastIndex', { writable: false });"
+ " r.exec('abc');"
+ "} catch (e) { e.message }";
Utils.runWithAllOptimizationLevels(
_cx -> {
final ScriptableObject scope = _cx.initStandardObjects();
final Object result = _cx.evaluateString(scope, script, "test script", 0, null);
assertEquals(
"Cannot modify readonly property: lastIndex.",
Context.toString(result));
return null;
});
}

source = "new /z/();";
try {
cx.evaluateString(scope, source, "test", 0, null);
fail();
}
catch (EcmaError e) {
// expected
assertTrue(e.getMessage(), e.getMessage().startsWith("TypeError: "));
}
@Test
public void search() {
try (Context cx = Context.enter()) {
cx.setLanguageVersion(Context.VERSION_ES6);
ScriptableObject scope = cx.initStandardObjects();

String source = "'abc'.search(/b/);";
assertEquals(1, cx.evaluateString(scope, source, "test", 0, null));

source = "new new RegExp";
try {
cx.evaluateString(scope, source, "test", 0, null);
fail();
}
catch (EcmaError e) {
// expected
assertTrue(e.getMessage(), e.getMessage().startsWith("TypeError: "));
}
source = "/b/[Symbol.search]('abc');";
assertEquals(1, cx.evaluateString(scope, source, "test", 0, null));

Context.exit();
}
source = "'abc'.search(/d/);";
assertEquals(-1, cx.evaluateString(scope, source, "test", 0, null));

@Test
public void lastIndexReadonly() {
final String script = "try { "
+ " var r = /c/g;"
+ " Object.defineProperty(r, 'lastIndex', { writable: false });"
+ " r.exec('abc');"
+ "} catch (e) { e.message }";
Utils.runWithAllOptimizationLevels(_cx -> {
final ScriptableObject scope = _cx.initStandardObjects();
final Object result = _cx.evaluateString(scope, script, "test script", 0, null);
assertEquals("Cannot modify readonly property: lastIndex.", Context.toString(result));
return null;
});
source = "/d/[Symbol.search]('abc');";
assertEquals(-1, cx.evaluateString(scope, source, "test", 0, null));
}
}
}