8006736: nashorn script engine should support the usage multiple global objects with same engine instance
Reviewed-by: lagergren, jlaskey, hannesw
This commit is contained in:
parent
7014b0cced
commit
4135674f92
@ -47,7 +47,6 @@ import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptException;
|
||||
import javax.script.SimpleBindings;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.ErrorManager;
|
||||
import jdk.nashorn.internal.runtime.GlobalObject;
|
||||
@ -112,29 +111,9 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
});
|
||||
|
||||
// create new global object
|
||||
this.global = AccessController.doPrivileged(new PrivilegedAction<ScriptObject>() {
|
||||
@Override
|
||||
public ScriptObject run() {
|
||||
try {
|
||||
return nashornContext.createGlobal();
|
||||
} catch (final RuntimeException e) {
|
||||
if (Context.DEBUG) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// current ScriptContext exposed as "context"
|
||||
global.addOwnProperty("context", Property.NOT_ENUMERABLE, context);
|
||||
// current ScriptEngine instance exposed as "engine". We added @SuppressWarnings("LeakingThisInConstructor") as
|
||||
// NetBeans identifies this assignment as such a leak - this is a false positive as we're setting this property
|
||||
// in the Global of a Context we just created - both the Context and the Global were just created and can not be
|
||||
// seen from another thread outside of this constructor.
|
||||
global.addOwnProperty("engine", Property.NOT_ENUMERABLE, this);
|
||||
// global script arguments
|
||||
global.addOwnProperty("arguments", Property.NOT_ENUMERABLE, UNDEFINED);
|
||||
this.global = createNashornGlobal();
|
||||
// set the default engine scope for the default context
|
||||
context.setBindings(new ScriptObjectMirror(global, global), ScriptContext.ENGINE_SCOPE);
|
||||
|
||||
// evaluate engine initial script
|
||||
try {
|
||||
@ -145,8 +124,6 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
}
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
context.setBindings(new ScriptObjectMirror(global, global), ScriptContext.ENGINE_SCOPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -170,7 +147,8 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
|
||||
@Override
|
||||
public Bindings createBindings() {
|
||||
return new SimpleBindings();
|
||||
final ScriptObject newGlobal = createNashornGlobal();
|
||||
return new ScriptObjectMirror(newGlobal, newGlobal);
|
||||
}
|
||||
|
||||
// Compilable methods
|
||||
@ -208,22 +186,23 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
|
||||
private <T> T getInterfaceInner(final Object self, final Class<T> clazz) {
|
||||
final Object realSelf;
|
||||
final ScriptObject ctxtGlobal = getNashornGlobalFrom(context);
|
||||
if(self == null) {
|
||||
realSelf = global;
|
||||
realSelf = ctxtGlobal;
|
||||
} else if (!(self instanceof ScriptObject)) {
|
||||
realSelf = ScriptObjectMirror.unwrap(self, global);
|
||||
realSelf = ScriptObjectMirror.unwrap(self, ctxtGlobal);
|
||||
} else {
|
||||
realSelf = self;
|
||||
}
|
||||
try {
|
||||
final ScriptObject oldGlobal = getNashornGlobal();
|
||||
try {
|
||||
if(oldGlobal != global) {
|
||||
setNashornGlobal(global);
|
||||
if(oldGlobal != ctxtGlobal) {
|
||||
setNashornGlobal(ctxtGlobal);
|
||||
}
|
||||
return clazz.cast(JavaAdapterFactory.getConstructor(realSelf.getClass(), clazz).invoke(realSelf));
|
||||
} finally {
|
||||
if(oldGlobal != global) {
|
||||
if(oldGlobal != ctxtGlobal) {
|
||||
setNashornGlobal(oldGlobal);
|
||||
}
|
||||
}
|
||||
@ -259,13 +238,14 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
*/
|
||||
public Object __noSuchProperty__(final Object self, final ScriptContext ctxt, final String name) {
|
||||
final int scope = ctxt.getAttributesScope(name);
|
||||
final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt);
|
||||
if (scope != -1) {
|
||||
return ScriptObjectMirror.unwrap(ctxt.getAttribute(name, scope), global);
|
||||
return ScriptObjectMirror.unwrap(ctxt.getAttribute(name, scope), ctxtGlobal);
|
||||
}
|
||||
|
||||
if (self == UNDEFINED) {
|
||||
// scope access and so throw ReferenceError
|
||||
referenceError(global, "not.defined", name);
|
||||
referenceError(ctxtGlobal, "not.defined", name);
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
@ -282,29 +262,72 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
*/
|
||||
public Object __noSuchMethod__(final Object self, final ScriptContext ctxt, final String name, final Object args) {
|
||||
final int scope = ctxt.getAttributesScope(name);
|
||||
final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt);
|
||||
Object value;
|
||||
|
||||
if (scope != -1) {
|
||||
value = ctxt.getAttribute(name, scope);
|
||||
} else {
|
||||
if (self == UNDEFINED) {
|
||||
referenceError(global, "not.defined", name);
|
||||
referenceError(ctxtGlobal, "not.defined", name);
|
||||
} else {
|
||||
typeError(global, "no.such.function", name, ScriptRuntime.safeToString(global));
|
||||
typeError(ctxtGlobal, "no.such.function", name, ScriptRuntime.safeToString(ctxtGlobal));
|
||||
}
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
value = ScriptObjectMirror.unwrap(value, global);
|
||||
value = ScriptObjectMirror.unwrap(value, ctxtGlobal);
|
||||
if (value instanceof ScriptFunction) {
|
||||
return ScriptObjectMirror.unwrap(ScriptRuntime.apply((ScriptFunction)value, global, args), global);
|
||||
return ScriptObjectMirror.unwrap(ScriptRuntime.apply((ScriptFunction)value, ctxtGlobal, args), ctxtGlobal);
|
||||
}
|
||||
|
||||
typeError(global, "not.a.function", ScriptRuntime.safeToString(name));
|
||||
typeError(ctxtGlobal, "not.a.function", ScriptRuntime.safeToString(name));
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
private ScriptObject getNashornGlobalFrom(final ScriptContext ctxt) {
|
||||
final Bindings bindings = ctxt.getBindings(ScriptContext.ENGINE_SCOPE);
|
||||
if (bindings instanceof ScriptObjectMirror) {
|
||||
ScriptObject sobj = ((ScriptObjectMirror)bindings).getScriptObject();
|
||||
if (sobj instanceof GlobalObject) {
|
||||
return sobj;
|
||||
}
|
||||
}
|
||||
|
||||
// didn't find global object from context given - return the engine-wide global
|
||||
return global;
|
||||
}
|
||||
|
||||
private ScriptObject createNashornGlobal() {
|
||||
final ScriptObject newGlobal = AccessController.doPrivileged(new PrivilegedAction<ScriptObject>() {
|
||||
@Override
|
||||
public ScriptObject run() {
|
||||
try {
|
||||
return nashornContext.createGlobal();
|
||||
} catch (final RuntimeException e) {
|
||||
if (Context.DEBUG) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// current ScriptContext exposed as "context"
|
||||
newGlobal.addOwnProperty("context", Property.NOT_ENUMERABLE, UNDEFINED);
|
||||
// current ScriptEngine instance exposed as "engine". We added @SuppressWarnings("LeakingThisInConstructor") as
|
||||
// NetBeans identifies this assignment as such a leak - this is a false positive as we're setting this property
|
||||
// in the Global of a Context we just created - both the Context and the Global were just created and can not be
|
||||
// seen from another thread outside of this constructor.
|
||||
newGlobal.addOwnProperty("engine", Property.NOT_ENUMERABLE, this);
|
||||
// global script arguments with undefined value
|
||||
newGlobal.addOwnProperty("arguments", Property.NOT_ENUMERABLE, UNDEFINED);
|
||||
// file name default is null
|
||||
newGlobal.addOwnProperty(ScriptEngine.FILENAME, Property.NOT_ENUMERABLE, null);
|
||||
return newGlobal;
|
||||
}
|
||||
|
||||
private void evalEngineScript() throws ScriptException {
|
||||
evalSupportScript("resources/engine.js");
|
||||
}
|
||||
@ -332,51 +355,53 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
// scripts should see "context" and "engine" as variables
|
||||
private void setContextVariables(final ScriptContext ctxt) {
|
||||
ctxt.setAttribute("context", ctxt, ScriptContext.ENGINE_SCOPE);
|
||||
global.set("context", ctxt, false);
|
||||
Object args = ScriptObjectMirror.unwrap(ctxt.getAttribute("arguments"), global);
|
||||
final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt);
|
||||
ctxtGlobal.set("context", ctxt, false);
|
||||
Object args = ScriptObjectMirror.unwrap(ctxt.getAttribute("arguments"), ctxtGlobal);
|
||||
if (args == null || args == UNDEFINED) {
|
||||
args = ScriptRuntime.EMPTY_ARRAY;
|
||||
}
|
||||
// if no arguments passed, expose it
|
||||
args = ((GlobalObject)global).wrapAsObject(args);
|
||||
global.set("arguments", args, false);
|
||||
args = ((GlobalObject)ctxtGlobal).wrapAsObject(args);
|
||||
ctxtGlobal.set("arguments", args, false);
|
||||
}
|
||||
|
||||
private Object invokeImpl(final Object selfObject, final String name, final Object... args) throws ScriptException, NoSuchMethodException {
|
||||
final ScriptObject oldGlobal = getNashornGlobal();
|
||||
final boolean globalChanged = (oldGlobal != global);
|
||||
final ScriptObject ctxtGlobal = getNashornGlobalFrom(context);
|
||||
final boolean globalChanged = (oldGlobal != ctxtGlobal);
|
||||
|
||||
Object self = selfObject;
|
||||
|
||||
try {
|
||||
if (globalChanged) {
|
||||
setNashornGlobal(global);
|
||||
setNashornGlobal(ctxtGlobal);
|
||||
}
|
||||
|
||||
ScriptObject sobj;
|
||||
Object value = null;
|
||||
|
||||
self = ScriptObjectMirror.unwrap(self, global);
|
||||
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 = global;
|
||||
sobj = global;
|
||||
self = ctxtGlobal;
|
||||
sobj = ctxtGlobal;
|
||||
value = sobj.get(name);
|
||||
}
|
||||
|
||||
if (value instanceof ScriptFunction) {
|
||||
final Object res;
|
||||
try {
|
||||
res = ScriptRuntime.apply((ScriptFunction)value, self, ScriptObjectMirror.unwrapArray(args, global));
|
||||
res = ScriptRuntime.apply((ScriptFunction)value, self, ScriptObjectMirror.unwrapArray(args, ctxtGlobal));
|
||||
} catch (final Exception e) {
|
||||
throwAsScriptException(e);
|
||||
throw new AssertionError("should not reach here");
|
||||
}
|
||||
return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, global));
|
||||
return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, ctxtGlobal));
|
||||
}
|
||||
|
||||
throw new NoSuchMethodException(name);
|
||||
@ -396,10 +421,11 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
return null;
|
||||
}
|
||||
final ScriptObject oldGlobal = getNashornGlobal();
|
||||
final boolean globalChanged = (oldGlobal != global);
|
||||
final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt);
|
||||
final boolean globalChanged = (oldGlobal != ctxtGlobal);
|
||||
try {
|
||||
if (globalChanged) {
|
||||
setNashornGlobal(global);
|
||||
setNashornGlobal(ctxtGlobal);
|
||||
}
|
||||
|
||||
setContextVariables(ctxt);
|
||||
@ -413,8 +439,8 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
return null;
|
||||
}
|
||||
|
||||
Object res = ScriptRuntime.apply(script, global);
|
||||
return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, global));
|
||||
Object res = ScriptRuntime.apply(script, ctxtGlobal);
|
||||
return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, ctxtGlobal));
|
||||
} catch (final Exception e) {
|
||||
throwAsScriptException(e);
|
||||
throw new AssertionError("should not reach here");
|
||||
@ -458,17 +484,18 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
|
||||
private ScriptFunction compileImpl(final char[] buf, final ScriptContext ctxt) throws ScriptException {
|
||||
final ScriptObject oldGlobal = getNashornGlobal();
|
||||
final boolean globalChanged = (oldGlobal != global);
|
||||
final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt);
|
||||
final boolean globalChanged = (oldGlobal != ctxtGlobal);
|
||||
try {
|
||||
final Object val = ctxt.getAttribute(ScriptEngine.FILENAME);
|
||||
final String fileName = (val != null) ? val.toString() : "<eval>";
|
||||
|
||||
final Source source = new Source(fileName, buf);
|
||||
if (globalChanged) {
|
||||
setNashornGlobal(global);
|
||||
setNashornGlobal(ctxtGlobal);
|
||||
}
|
||||
|
||||
return nashornContext.compileScript(source, global, nashornContext._strict);
|
||||
return nashornContext.compileScript(source, ctxtGlobal, nashornContext._strict);
|
||||
} catch (final Exception e) {
|
||||
throwAsScriptException(e);
|
||||
throw new AssertionError("should not reach here");
|
||||
@ -479,17 +506,17 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
}
|
||||
}
|
||||
|
||||
// don't make these public!!
|
||||
// don't make this public!!
|
||||
static ScriptObject getNashornGlobal() {
|
||||
return Context.getGlobal();
|
||||
return Context.getGlobal();
|
||||
}
|
||||
|
||||
static void setNashornGlobal(final ScriptObject global) {
|
||||
static void setNashornGlobal(final ScriptObject newGlobal) {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
Context.setGlobal(global);
|
||||
return null;
|
||||
Context.setGlobal(newGlobal);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -329,6 +329,11 @@ final class ScriptObjectMirror extends JSObject implements Bindings {
|
||||
});
|
||||
}
|
||||
|
||||
// package-privates below this.
|
||||
ScriptObject getScriptObject() {
|
||||
return sobj;
|
||||
}
|
||||
|
||||
static Object translateUndefined(Object obj) {
|
||||
return (obj == ScriptRuntime.UNDEFINED)? null : obj;
|
||||
}
|
||||
|
@ -46,8 +46,10 @@ import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
import javax.script.SimpleScriptContext;
|
||||
import jdk.nashorn.internal.runtime.Version;
|
||||
import netscape.javascript.JSObject;
|
||||
import org.testng.Assert;
|
||||
import org.testng.TestNG;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@ -922,4 +924,51 @@ public class ScriptEngineTest {
|
||||
assertEquals(engineScope.get("myVar"), "nashorn");
|
||||
assertEquals(e.get("myVar"), "nashorn");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiGlobalTest() {
|
||||
final ScriptEngineManager m = new ScriptEngineManager();
|
||||
final ScriptEngine e = m.getEngineByName("nashorn");
|
||||
final Bindings b = e.createBindings();
|
||||
final ScriptContext newCtxt = new SimpleScriptContext();
|
||||
newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
|
||||
|
||||
try {
|
||||
Object obj1 = e.eval("Object");
|
||||
Object obj2 = e.eval("Object", newCtxt);
|
||||
Assert.assertNotEquals(obj1, obj2);
|
||||
Assert.assertNotNull(obj1);
|
||||
Assert.assertNotNull(obj2);
|
||||
Assert.assertEquals(obj1.toString(), obj2.toString());
|
||||
|
||||
e.eval("x = 'hello'");
|
||||
e.eval("x = 'world'", newCtxt);
|
||||
Object x1 = e.getContext().getAttribute("x");
|
||||
Object x2 = newCtxt.getAttribute("x");
|
||||
Assert.assertNotEquals(x1, x2);
|
||||
Assert.assertEquals(x1, "hello");
|
||||
Assert.assertEquals(x2, "world");
|
||||
|
||||
x1 = e.eval("x");
|
||||
x2 = e.eval("x", newCtxt);
|
||||
Assert.assertNotEquals(x1, x2);
|
||||
Assert.assertEquals(x1, "hello");
|
||||
Assert.assertEquals(x2, "world");
|
||||
|
||||
final ScriptContext origCtxt = e.getContext();
|
||||
e.setContext(newCtxt);
|
||||
e.eval("y = new Object()");
|
||||
e.eval("y = new Object()", origCtxt);
|
||||
|
||||
Object y1 = origCtxt.getAttribute("y");
|
||||
Object y2 = newCtxt.getAttribute("y");
|
||||
Assert.assertNotEquals(y1, y2);
|
||||
Assert.assertNotEquals(e.eval("y"), e.eval("y", origCtxt));
|
||||
Assert.assertEquals("[object Object]", y1.toString());
|
||||
Assert.assertEquals("[object Object]", y2.toString());
|
||||
} catch (final ScriptException se) {
|
||||
se.printStackTrace();
|
||||
fail(se.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user