More fun with scopes and ScriptObjectMirror

Tim Fox timvolpe at
Wed Dec 11 09:43:26 PST 2013

So probably I will take the same path as you guys and just do a pure JS 
require and wrap in anon. function. Nice and easy

Thanks for all your help!

On 11/12/13 14:27, A. Sundararajan wrote:
> avatar/js project passes the relevant node.js tests (tests that check 
> no leak of vars from module files). Apparently, none of the modules 
> have "var" less declarations.
> -Sundar
> On Wednesday 11 December 2013 07:13 PM, Tim Fox wrote:
>> On 11/12/13 13:28, A. Sundararajan wrote:
>>> Our emails crossed (again!).
>> Hehe, we really must stop doing that! ;)
>>> I suggested that option based on avatar/js code..
>> fwiw, this is also the approach used in rhino-require 
>> It's actually the first thing I considered because it's so simple, 
>> and it's pure JS so portable between engines. However this approach 
>> is flawed as it doesn't prevent the leakage of globals not declared 
>> using var, i.e.
>> someglobal = 3;
>> Which is what led me down the more complex route with explicitly 
>> manipulating scopes at the engine level...
>> Do you guys not consider the leakage of non var globals not a big 
>> issue? Personally I ruled out this approach because of that, but 
>> maybe I should reconsider it (?)
>> In the long term though, I think it would be nice if Nashorn provided 
>> the mechanism to implement require() with real isolation.
>>> Sundar
>>> On Wednesday 11 December 2013 06:40 PM, Tim Fox wrote:
>>>> 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).
>>>>> 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 also considered a third option - executing all modules in a 
>>>> single global, but wrapping the module code in a function to hide 
>>>> any top level globals declared as vars, e.g. if module is:
>>>> var someglobal = "hello";
>>>> module.exports = someglobal;
>>>> after wrapping it becomes:
>>>> (function(module) {
>>>>   var someglobal = "hello");
>>>>   module.exports = someglobal;
>>>> })();
>>>> which hides someglobal.
>>>> However this doesn't work with modules that use globals by omitting 
>>>> var, e.g.
>>>> someglobal = "hello";
>>>> module.exports = someglobal;
>>>> Now, there are far fewer CommonJS modules  which use globals 
>>>> without var (as it's bad practice) but still enough to make this 
>>>> not a good option either :(
>>>>> 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