Possible scope leak in Nashorn

A. Sundararajan sundararajan.athijegannathan at oracle.com
Tue Dec 10 05:25:35 PST 2013


Hi Tim Fox,

Sorry for being terse in the bug update. I was about to write a detailed 
email response to nashorn dev anyway...

Nashorn supports javax.script API. Since there are few things are needed 
beyond javax.script, jdk.nashorn.api.scripting package contains 
(experimental) 'extensions'. Any other nashorn package contains 
(jdk.nashorn.internal.* and jdk.nashorn.tools.*) are considered internal 
implementation code.

Extensions to javax.script API are:

* script objects (objects, arrays, functions etc.) are exposed to java 
code as ScriptObjectMirror instances.

     Object obj = engine.eval("({ foo: 343 }");

     ScriptObjectMirror sobj = (ScriptObjectMirror) obj;

ScriptObjectMirror implements javax.script.Bindings (and hence 
java.util.Map) and jdk.nashorn.api.scripting.JSObject. It has hooks to 
access specific properties of a script object, set property, call 
method, check if it is an array or a function etc.

* jdk.nashorn.api.scripting.JSObject interface is a two way interface. 
If you implement it by your own class (or extend 
jdk.nashorn.api.scripting.AbstractJSObject), your objects will be 
treated like script objects from scripts. i.e., script access of 
property "obj.foo" will result in JSObject.getMemer being called and so 
on. Also, ScriptObjectMirror itself implements JSObject interface.

* Every Nashorn global scope is an object of 
jdk.nashorn.internal.objects.Global class. This too is ( like any other 
script object ) wrapped as an instance of ScriptObjectMirror.

* When a new ScriptEngine is created, it gets a ScriptContext as it's 
default context. This has an ENGINE_SCOPE and an GLOBAL_SCOPE script 
Bindings. ENGINE_SCOPE bindings is a ScriptObject that wraps a nashorn 
global.

* When you create a new Bindings by ScriptEngine.createBindings, it 
creates a new nashorn Global and wraps it as ScriptObjectMirror.

* Any java level variable exposed by Bindings like GLOBAL_SCOPE is 
read-only  - in the sense that any script assignment will not affect it 
- will only create new nashorn global variable shadowing variable 
exposed in java Bindings.

    Bindings b = 
engine.getContext().getBindings(ScriptContext.GLOBAL_SCOPE);
    b.put("foo", 34);
    b.put("bar", new Object());

    e.eval("print(foo)"); // prints 34
    e.eval("foo = 23"); // does not change "b"'s "foo" entry!! creates a 
new nashorn global var shadowing whatever is exposed in Bindings.

* ENGINE_SCOPE bindings of default context is a ScriptObjectMirror 
wrapping a nashorn global scope. So, any assignment in script is 
reflected there.

    Bindings b1 = 
engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE);
    e.eval("foo = 33");
    System.out.println(b1.get("foo")); // prints 33
    b1.put("foo", "hello");
     e.eval("print(foo)"); // prints hello

i.e., it is two way update/read.

* If you use your own ScriptContext with no ScriptObjectMirror/Global 
for ENGINE_SCOPE, nashorn script engine creates a nashorn global and 
ScriptObjectMirror wrapper and associates with it.

* ScriptEngine.createBindings creates a new nashorn Global and wraps it 
as ScriptObjectMirror. You can use it as ENGINE_SCOPE in another 
ScriptContext and evaluate scripts. The script will see different JS 
"Object", "Number" etc.

* With --global-per-engine flag set, ScriptEngine uses a single nashorn 
Global for all ENGINE_SCOPEs of all ScriptContexts. 
ScriptEngine.createBindings creates a javax.script.SimpleBindings. i.e., 
with this mode, there is only one Nashorn "Object", "Number", "RegExp" 
etc. for all ScriptContexts. If you call ScriptEngine.createBindings and 
set the Bindings as ENGINE_SCOPE of another ScriptContext, you get 
ENGINE_SCOPE to be a SimpleBindings. (and not ScriptObjectMirror 
wrapping a nashorn Global instance). Nashorn script engine uses same 
single Nashorn Global instance for all ScriptContext instances.

* $nashorn_repo/test/src/jdk/nashorn/api/scripting directory has many 
tests demonstrating/checking javax.script and jdk.nashorn.api.scripting 
classes.

PS. One of the near future task is to document all this and put it in 
nashorn wiki.

Hope this helps,
-Sundar


On Tuesday 10 December 2013 06:11 PM, Tim Fox wrote:
> Hi Sundar,
>
> I see you resolved this issue as a non issue here 
> https://bugs.openjdk.java.net/browse/JDK-8029604
>
> Unfortunately I am unable to add comments to the bug so I have a few 
> questions...
>
> >> In either mode, Java level Bindings instance backed Java objects as 
> script variables are "read only"
>
> Can you elaborate what this means? Do you mean the Java object 
> _reference_ is read only? I.e. JavaScript can't replace the global 
> variable with anything else. I imagine ensuring that the Java object 
> itself is read-only would be an extremely difficult thing to do.
>
> >> You can get ScriptObjectMirror wrapper of ScriptFunctions and 
> ScriptObjects and share/call/use it from anywhere (ScriptObjectMirror 
> takes care of setting right nashorn Global object in thread local 
> storage).
>
> It seems like this is what we need to do to implement CommonJS 
> require-like functionality as we need to export objects from scopes 
> such that they are callable from different scopes. This is essential 
> for implementing any "node-like" functionality using Nashorn.
>
> I can't see anything in the javax.script API that mentions 
> ScriptObjectMirror, ScriptFunctions or ScriptObjects, so I assume 
> these are in a different API. Is this API documented anywhere, and is 
> it a public API? If you could give a simple code example of how my 
> simple test program could be adapted using this other API, that would 
> be a great help :)
>
> I guess this also implies that what I want to do is impossible using 
> the javax.script API?
>
> On 05/12/13 13:55, A. Sundararajan wrote:
>> Hi,
>>
>> Please use "tools" category and include scripting/nashorn in your 
>> description or title or label somewhere.
>>
>> PS. we are working on getting appropriate category.
>>
>> Thanks
>> Sundar
>>
>> On Thursday 05 December 2013 06:43 PM, Tim Fox wrote:
>>> Thanks I am trying to submit a bug here:
>>>
>>> http://bugreport.sun.com/bugreport/submit_intro.do
>>>
>>> as directed in the link your provided, but I can't see any category 
>>> I can file it against that seems appropriate for Nashorn...
>>>
>>> On 05/12/13 13:00, A. Sundararajan wrote:
>>>> Please file a bug using web interface. Process explained in this 
>>>> post by Jim:
>>>>
>>>> http://mail.openjdk.java.net/pipermail/nashorn-dev/2013-December/002515.html 
>>>>
>>>>
>>>> Thanks
>>>> -Sundar
>>>>
>>>> On Thursday 05 December 2013 06:14 PM, Tim Fox wrote:
>>>>> Oops! Forgot to provide a link to the example...
>>>>>
>>>>> Here it is:
>>>>>
>>>>> https://gist.github.com/purplefox/7804105
>>>>>
>>>>> On 05/12/13 12:42, Tim Fox wrote:
>>>>>> Hello Nashorn folks,
>>>>>>
>>>>>> I've been playing around with Nashorn today, and in particular 
>>>>>> I've been trying to implement commonJS-like require() 
>>>>>> functionality, but having some problems. Most probably I am doing 
>>>>>> something stupid...
>>>>>>
>>>>>> I'm sure you all know, with commonJS modules you can "require" 
>>>>>> them from your JS scripts, e.g.
>>>>>>
>>>>>> var someObject = require("modulename");
>>>>>>
>>>>>> Then in modulename.js
>>>>>>
>>>>>> you do your stuff then export the object that ends up being 
>>>>>> returned from the require, e.g.
>>>>>>
>>>>>> var someObject = {
>>>>>>   foo: "bar"
>>>>>> }
>>>>>>
>>>>>> module.exports = someObject;
>>>>>>
>>>>>> One key thing is that modules have their own scope, so any 
>>>>>> globals defined in modulename.js shouldn't be visible in the 
>>>>>> script that requires them, and vice versa.
>>>>>>
>>>>>> While trying to implement this using Nashorn and running scripts 
>>>>>> in different scopes I've found it's possible for Nashorn to get 
>>>>>> confused about what scope it's using, and for scopes to leak from 
>>>>>> one to another.
>>>>>>
>>>>>> I created this simple runnable example to demonstrate the issue.
>>>>>>
>>>>>> I wonder if anyone can advise whether this is a bug, or perhaps I 
>>>>>> am using the API incorrectly. If the latter, could you advise on 
>>>>>> the proper way to use the API to implement this kind of 
>>>>>> functionality.
>>>>>>
>>>>>> Many thanks
>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>



More information about the nashorn-dev mailing list