8021252: invokeMethod throws NoSuchMethodException when script object is from different script context

Reviewed-by: lagergren, hannesw
This commit is contained in:
Athijegannathan Sundararajan 2013-07-25 14:05:03 +05:30
parent 97f5b61172
commit 90fc477ac3
3 changed files with 177 additions and 40 deletions

View file

@ -357,50 +357,39 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
private Object invokeImpl(final Object selfObject, final String name, final Object... args) throws ScriptException, NoSuchMethodException {
name.getClass(); // null check
ScriptObjectMirror selfMirror = null;
if (selfObject instanceof ScriptObjectMirror) {
selfMirror = (ScriptObjectMirror)selfObject;
} else if (selfObject instanceof ScriptObject) {
// invokeMethod called from script code - in which case we may get 'naked' ScriptObject
// Wrap it with oldGlobal to make a ScriptObjectMirror for the same.
final ScriptObject oldGlobal = Context.getGlobal();
if (oldGlobal != null) {
selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(selfObject, oldGlobal);
}
} else if (selfObject == null) {
// selfObject is null => global function call
final ScriptObject ctxtGlobal = getNashornGlobalFrom(context);
final boolean globalChanged = (oldGlobal != ctxtGlobal);
Object self = globalChanged? ScriptObjectMirror.wrap(selfObject, oldGlobal) : selfObject;
try {
if (globalChanged) {
Context.setGlobal(ctxtGlobal);
selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(ctxtGlobal, ctxtGlobal);
}
ScriptObject sobj;
Object value = null;
self = ScriptObjectMirror.unwrap(self, ctxtGlobal);
// FIXME: should convert when self is not ScriptObject
if (self instanceof ScriptObject) {
sobj = (ScriptObject)self;
value = sobj.get(name);
} else if (self == null) {
self = ctxtGlobal;
sobj = ctxtGlobal;
value = sobj.get(name);
}
if (value instanceof ScriptFunction) {
final Object res;
if (selfMirror != null) {
try {
final Object[] modArgs = globalChanged? ScriptObjectMirror.wrapArray(args, oldGlobal) : args;
res = ScriptRuntime.checkAndApply((ScriptFunction)value, self, ScriptObjectMirror.unwrapArray(modArgs, ctxtGlobal));
return ScriptObjectMirror.translateUndefined(selfMirror.call(name, args));
} catch (final Exception e) {
final Throwable cause = e.getCause();
if (cause instanceof NoSuchMethodException) {
throw (NoSuchMethodException)cause;
}
throwAsScriptException(e);
throw new AssertionError("should not reach here");
}
return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, ctxtGlobal));
}
throw new NoSuchMethodException(name);
} finally {
if (globalChanged) {
Context.setGlobal(oldGlobal);
}
}
// Non-script object passed as selfObject
throw new IllegalArgumentException("can not call invokeMethod on non-script objects");
}
private Object evalImpl(final char[] buf, final ScriptContext ctxt) throws ScriptException {

View file

@ -89,7 +89,7 @@ public final class ScriptObjectMirror extends JSObject implements Bindings {
final Object val = functionName == null? sobj : sobj.get(functionName);
if (! (val instanceof ScriptFunction)) {
throw new RuntimeException("No such function " + ((functionName != null)? functionName : ""));
throw new NoSuchMethodException("No such function " + ((functionName != null)? functionName : ""));
}
final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
@ -630,5 +630,4 @@ public final class ScriptObjectMirror extends JSObject implements Bindings {
}
}
}
}

View file

@ -648,6 +648,91 @@ public class ScriptEngineTest {
}
}
@Test
/**
* Check that we can call invokeMethod on an object that we got by evaluating
* script with different Context set.
*/
public void invokeMethodDifferentContextTest() {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
try {
// define an object with method on it
Object obj = e.eval("({ hello: function() { return 'Hello World!'; } })");
final ScriptContext ctxt = new SimpleScriptContext();
ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
e.setContext(ctxt);
// invoke 'func' on obj - but with current script context changed
final Object res = ((Invocable)e).invokeMethod(obj, "hello");
assertEquals(res, "Hello World!");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
@Test
/**
* Check that invokeMethod throws NPE on null method name.
*/
public void invokeMethodNullNameTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object obj = e.eval("({})");
final Object res = ((Invocable)e).invokeMethod(obj, null);
fail("should have thrown NPE");
} catch (final Exception exp) {
if (! (exp instanceof NullPointerException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that invokeMethod throws NoSuchMethodException on missing method.
*/
public void invokeMethodMissingTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object obj = e.eval("({})");
final Object res = ((Invocable)e).invokeMethod(obj, "nonExistentMethod");
fail("should have thrown NoSuchMethodException");
} catch (final Exception exp) {
if (! (exp instanceof NoSuchMethodException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that calling method on non-script object 'thiz' results in IllegalArgumentException.
*/
public void invokeMethodNonScriptObjectThizTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable)e).invokeMethod(new Object(), "toString");
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (! (exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
public void noEnumerablePropertiesTest() {
final ScriptEngineManager m = new ScriptEngineManager();
@ -766,6 +851,70 @@ public class ScriptEngineTest {
}
}
@Test
/**
* check that null function name results in NPE.
*/
public void invokeFunctionNullNameTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object res = ((Invocable)e).invokeFunction(null);
fail("should have thrown NPE");
} catch (final Exception exp) {
if (! (exp instanceof NullPointerException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that attempt to call missing function results in NoSuchMethodException.
*/
public void invokeFunctionMissingTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object res = ((Invocable)e).invokeFunction("NonExistentFunc");
fail("should have thrown NoSuchMethodException");
} catch (final Exception exp) {
if (! (exp instanceof NoSuchMethodException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that invokeFunction calls functions only from current context's Bindings.
*/
public void invokeFunctionDifferentContextTest() {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
try {
// define an object with method on it
Object obj = e.eval("function hello() { return 'Hello World!'; }");
final ScriptContext ctxt = new SimpleScriptContext();
ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
// change engine's current context
e.setContext(ctxt);
((Invocable)e).invokeFunction("hello"); // no 'hello' in new context!
fail("should have thrown NoSuchMethodException");
} catch (final Exception exp) {
if (! (exp instanceof NoSuchMethodException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
public void invokeFunctionExceptionTest() {
final ScriptEngineManager m = new ScriptEngineManager();