More fun with scopes and ScriptObjectMirror
timvolpe at gmail.com
Wed Dec 11 04:13:41 PST 2013
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>),
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
> 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
> semantics with a set of highly mutable built-ins (Function, String,
> Array, etc.) they currently seem like a necessity when crossing Global
> I'd suggest you try to work with an approach that avoids creation of
> mirrors - where each ScriptEngine instance is an isolated execution
> 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.
> On Dec 10, 2013, at 9:16 PM, Tim Fox <timvolpe at gmail.com
> <mailto:timvolpe at gmail.com>> wrote:
>> 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:
>> 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
>>> 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 jdk.nashorn.internal.scripts.Script$\^eval\_._L2(<eval>:3)
>>> at jdk.nashorn.internal.scripts.Script$\^eval\_.runScript(<eval>:1)
>>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>>> at java.lang.reflect.Method.invoke(Method.java:483)
More information about the nashorn-dev