More fun with scopes and ScriptObjectMirror

Tim Fox timvolpe at
Wed Dec 11 23:00:08 PST 2013

On 11/12/13 12:53, Attila Szegedi wrote:
> On Dec 11, 2013, at 1:13 PM, Tim Fox <timvolpe at> wrote:
>> 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.
> That's true, but then you also end up with the need for ScriptObjectMirrors between them, and that was what I suggested you try to avoid.
>> 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.
> Well, it will mean that modules won't have their own globals… --global-per-engine will make it so that the Global object is specific to the ScriptEngine, and not to the ScriptContext, e.g. even if you replace the ScriptContext of the engine, when scripts are run, they'll still see the same global object as before the replacement. The gist of it is:
> a) without --global-per-engine, the Global object is specific to a ScriptContext, each ScriptContext has its own. ENGINE_SCOPE Bindings object of the context is actually a mirror of its Global.
> b) with --global-per-engine, the Global object lives in the ScriptEngine. ENGINE_SCOPE Bindings object of the context is just a vanilla SimpleBindings (or whatever you set it to), and Global will delegate property getters for non-existent properties to it (but it'll still receive property setters, so new global variables created by one script will be visible by another; no isolation there).

One more question on this. With --global-per-engine, and multiple 
ScriptContext instances - if all the ScriptContext instances share a 
single a single global, how is this different from just having a single 
ScriptContext in which you execute all JavaScript?

> What I was suggesting is that your module loading code would look something like:
> // The engine that you use
> ScriptEngine engine = new NashornScriptEngine().getEngine(new String[] { "--global-per-engine" });
> ...
> // when loading a module
> Bindings moduleVars = new SimpleBindings();
> moduleVars.put("require", requireFn);
> moduleVars.put("module", moduleDescriptorObj);
> moduleVars.put("exports", exportsObj);
> Bindings prevBindings = engine.getBindings(ENGINE_SCOPE);
> engine.setBindings(moduleVars, ENGINE_SCOPE);
> try {
>      engine.eval(moduleSource);
> } finally {
>     engine.setBindings(prevBindings, ENGINE_SCOPE);
> }
> return exportsObj;
> NB: your modules would _not_ run in isolated globals. I thought they do, but I just spoke to Sundar and he explained the mechanism to me so now I see they won't -- see above the case b).
> They could pollute each other's global namespace (since it's shared). Hopefully they'd adhere to Modules/Secure recommendation and refrain from doing so, but you can't really enforce it.
> My require() implementation in Rhino could provide real isolation, but this is unfortunately impossible in Nashorn. Nashorn makes an assumption that the global object during execution of a script is an instance of jdk.nashorn.internals.objects.Global; in Rhino, it could have been anything so there I was able to run a module in a new scope that had the actual Global object as its prototype, so it could catch all variable assignments in itself and essentially make the Global read only (albeit objects in it mutable - e.g. a module could still extend Array prototype etc.).
> In Nashorn, it's the other way round with "--global-per-engine" - Global object is the one immediately visible to scripts, and ENGINE_SCOPE Bindings object is used as source of properties that aren't found in the Global. Here, Global catches variable assignments and ENGINE_SCOPE Bindings object ends up being immutable (although objects in it are obviously still mutable, so the module can build up its "exports" object).
> Basically, you have a choice between having shared globals (no isolation) without mirrors (and then you won't run into any issues with mirrors), or separate globals (with real isolation), but then also mirrors, and then you might run into limitations of mirrors (e.g. they can't be automatically used as Runnable etc. callbacks from Java and so on.)
> I'm not trying to justify or otherwise qualify any of the design decisions here, just trying to help you understand its constraints.
> Attila.

More information about the nashorn-dev mailing list