More fun with scopes and ScriptObjectMirror

Tim Fox timvolpe at gmail.com
Wed Dec 11 04:13:41 PST 2013


Attila,

On 11/12/13 11:30, Attila Szegedi wrote:
> Admittedly, I've not been working a lot with the scoping/multiple 
> globals/javax.script API side of the project; Sundar is probably the 
> one with best insight here (I've mostly worked on dynamic linking and 
> Java platform APIs, and am lately focused on bytecode compiler and 
> performance improvements targetted for the first post-8.0 update).
>
> Having said that, I think it should be fairly easy to implement a 
> CommonJS require() function within a single ScriptEngine instance, 
> using separate ENGINE_SCOPE level bindings for loading each module and 
> having them use a single global (setting the "--global-per-engine" 
> flag that you can pass to one of NashornScriptEngine.getEngine() 
> overloads that takes a String[] args).
>
> As far as I know, when multiple engine scopes in a single ScriptEngine 
> are sharing a single Global (effectively recreating the "MGN" model 
> from <http://wiki.commonjs.org/wiki/Modules/ProposalForNativeExtension>),

Confused...

I assumed that if two scripts where run with their own script context, 
then they would already have separate globals, i.e. if I do

myglobal = 1

in module 1 that won't be visible in module 2.

So I'm not sure really what --global-per-engine really means, if the 
modules have their own globals anyway. I guess my understanding must be 
wrong somewhere.

> those scopes can accept each others' objects without 
> ScriptObjectMirror wrapping. (ScriptObject-> ScriptObjectMirror 
> wrapping only happens for scopes that don't share the Global object). 
> You could execute every module with separate engine scope bindings 
> (setting those bindings up with "require", "export", "module" 
> variables for that module.) thus achieving isolation of their 
> namespaces except for the built-in globals.
>
> The obvious limitation of the approach is that execution within a 
> single ScriptEngine is not threadsafe. In a multithreaded system you'd 
> have to make sure they're either thread local, or access to them is 
> synchronized. And you'd have your modules be loaded separately into 
> every ScriptEngine. I'm not familiar with Vert.x architecture; I know 
> folks working on Avatar.js didn't have problems with implementing 
> require() in their system, but their system is single-threaded.
> ScriptObjectMirror is obviously not as capable as ScriptObject. Mind 
> you, I'm not using "obviously" meaning "it should've been obvious to 
> everyone" but rather in the sense of "obvious now that you have 
> stumbled across it". The reality of product development scheduling is 
> that we can't address it in the current phase (see 
> <http://openjdk.java.net/projects/jdk8/milestones>) even if we decided 
> that it should be addressed. In ideal world, a mirror wouldn't be 
> needed, but due to JavaScript's inherent entanglement of execution 
> semantics with a set of highly mutable built-ins (Function, String, 
> Array, etc.) they currently seem like a necessity when crossing Global 
> boundaries.
>
> I'd suggest you try to work with an approach that avoids creation of 
> mirrors - where each ScriptEngine instance is an isolated execution 
> context organized around a Global (this is true for JavaScript 
> runtimes in general). Mirrors are an attempt on our part to provide 
> cross-Global usage of JS objects, but as you have found out, they 
> aren't complete first-class citizens when they end up in a different 
> Global environments.
>
> The abovementioned approach with "--global-per-engine", 
> single-threaded usage of every engine instance, and use of separate 
> ENGINE_SCOPE bindings in the engine using ScriptEngine.setBindings() 
> is the best I can recommend at the moment.
>
> Attila.
>
> On Dec 10, 2013, at 9:16 PM, Tim Fox <timvolpe at gmail.com 
> <mailto:timvolpe at gmail.com>> wrote:
>
>> Attila-
>>
>> Perhaps a more fundamental question is, is what I am trying to do 
>> (implement commonJS require) really possible in Vanilla Nashorn?
>>
>> I did notice this post by you in December 2012:
>>
>> http://mail.openjdk.java.net/pipermail/nashorn-dev/2012-December/000014.html
>>
>> I understand there is no intention to implement require() directly in 
>> Nashorn, but my previous understanding was that it should be possible 
>> to implement it myself on top of Nashorn given that I can create 
>> different scopes and pass JS objects between them. After my recent 
>> experiments I'm not sure that understanding is really correct!
>>
>> What's your view on this?
>>
>>
>>
>>
>> On 10/12/13 19:49, Tim Fox wrote:
>>> Sorry for the deluge of posts, but I think I've found another issue 
>>> with scopes/ScriptObjectMirror.
>>>
>>> Consider this case: https://gist.github.com/purplefox/7896892
>>>
>>> I have a simple JS object which contains a function 'setCallback' 
>>> which simply delegates to a Java object which also has a setCallback 
>>> method.
>>>
>>> Calling this method works fine from within the same scope that it 
>>> was created in.
>>>
>>> However, if I export the object to another scope - wrapping in 
>>> ScriptObjectMirror as advised (and this is exactly the kind of thing 
>>> I would need to do to implement CommonJS require in Nashorn), and 
>>> then try to call the setCallback method from there, it yields the 
>>> following exception:
>>>
>>> java.lang.ClassCastException: Cannot cast 
>>> jdk.nashorn.api.scripting.ScriptObjectMirror to java.lang.Runnable
>>>    at 
>>> sun.invoke.util.ValueConversions.newClassCastException(ValueConversions.java:461)
>>>    at 
>>> sun.invoke.util.ValueConversions.castReference(ValueConversions.java:456)
>>>    at jdk.nashorn.internal.scripts.Script$\^eval\_._L2(<eval>:3)
>>>    at 
>>> jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:500)
>>>    at 
>>> jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:207)
>>>    at 
>>> jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:378)
>>>    at 
>>> jdk.nashorn.api.scripting.ScriptObjectMirror.call(ScriptObjectMirror.java:107)
>>>    at jdk.nashorn.internal.scripts.Script$\^eval\_.runScript(<eval>:1)
>>>    at 
>>> jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:498)
>>>    at 
>>> jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:207)
>>>    at 
>>> jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:378)
>>>    at 
>>> jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:544)
>>>    at 
>>> jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:526)
>>>    at 
>>> jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:522)
>>>    at 
>>> jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:193)
>>>    at 
>>> org.vertx.java.platform.impl.HandlerScopeTest.run(HandlerScopeTest.java:48)
>>>    at 
>>> org.vertx.java.platform.impl.HandlerScopeTest.main(HandlerScopeTest.java:9)
>>>    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>>>    at 
>>> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
>>>    at 
>>> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
>>>    at java.lang.reflect.Method.invoke(Method.java:483)
>>>    at 
>>> com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
>>>
>>
>



More information about the nashorn-dev mailing list