Did this while I was figuring out how to share global objects across isolated contexts. Kinda works, but not really. Prototypes are broken because the internal Global
object maintains the original instance. So even after overwriting, something like Object.getPrototypeOf({}) === Object.prototype
returns false
... which was what I was trying to get around originally, anyway. sigh
Map<String, Object> nativeJavaScriptObjects = new HashMap<>();
try {
engine.getBindings(ScriptContext.ENGINE_SCOPE).put("globals", (BiConsumer<String, Object>) (s, o) ->
nativeJavaScriptObjects.put(s, ScriptUtils.unwrap(o))
);
engine.eval("Object.getOwnPropertyNames(this).forEach(function(n) { if(Object.getOwnPropertyDescriptor(this, n).writable && n !== 'globals' && n !== 'javax.script.filename') { globals(n, this[n]); } }, this)");
engine.getBindings(ScriptContext.ENGINE_SCOPE).remove("globals");
} catch(Exception e) {
throw new RuntimeException(e);
}
//later:
nativeJavaScriptObjects.forEach((name, nativeObject) -> {
try {
newContext.setAttribute("_" + name, nativeObject, ScriptContext.ENGINE_SCOPE);
this.engine.eval("this." + name + " = _" + name + ";", newContext);
newContext.removeAttribute("_" + name, ScriptContext.ENGINE_SCOPE);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
The reason this works is that calling a Java method that takes in parameters of type Object
from JavaScript supposedly doesn't wrap the object in ScriptObjectMirror
. I didn't find this to be the case, but I did notice that calling ScriptUtils.unwrap
actually did unwrap the object.