JDK-8138616 : invokeFunction fails if function calls a function defined in GLOBAL_SCOPE
  • Type: Bug
  • Component: core-libs
  • Sub-Component: jdk.nashorn
  • Affected Version: 8u60,9
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2015-09-22
  • Updated: 2016-01-14
  • Resolved: 2015-10-01
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u72Fixed 9 b85Fixed
Description
FULL PRODUCT VERSION :
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Darwin colibri2.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64

A DESCRIPTION OF THE PROBLEM :
Works in u51, fails in u60:

        NashornScriptEngine engine = (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn");
        ScriptContext otherCtxt = new SimpleScriptContext();
        engine.setContext(otherCtxt);
        otherCtxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
        engine.eval("aglobal = function(){return 12;};");
        // move code-to-reuse to global scope
        otherCtxt.setBindings(otherCtxt.getBindings(ScriptContext.ENGINE_SCOPE), ScriptContext.GLOBAL_SCOPE);
        // create throw-away engine scope
        otherCtxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
        engine.eval("alocal = function() {return aglobal();}");
        
        //////////////////////////// this fails suddenly
        engine.invokeFunction("alocal");
        //////////////////////////////////////////////////////////


/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/bin/java ���
Exception in thread "main" javax.script.ScriptException: ReferenceError: "aglobal" is not defined in <eval> at line number 1
	at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:467)
	at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:389)
	at jdk.nashorn.api.scripting.NashornScriptEngine.invokeFunction(NashornScriptEngine.java:190)
	at blah.TestMain.main(TestMain.java:33)
	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:497)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: <eval>:1 ReferenceError: "aglobal" is not defined
	at jdk.nashorn.internal.runtime.ECMAErrors.error(ECMAErrors.java:57)
	at jdk.nashorn.internal.runtime.ECMAErrors.referenceError(ECMAErrors.java:319)
	at jdk.nashorn.internal.runtime.ECMAErrors.referenceError(ECMAErrors.java:291)
	at jdk.nashorn.internal.objects.Global.__noSuchProperty__(Global.java:1432)
	at jdk.nashorn.internal.scripts.Script$Recompilation$2$20$\^eval\_.alocal(<eval>:1)
	at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:640)
	at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:228)
	at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393)
	at jdk.nashorn.api.scripting.ScriptObjectMirror.callMember(ScriptObjectMirror.java:199)
	at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:383)
	... 7 more

REGRESSION.  Last worked in version 8u51

ADDITIONAL REGRESSION INFORMATION: 
java version "1.8.0_51"
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
With u60:
        NashornScriptEngine engine = (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn");
        ScriptContext otherCtxt = new SimpleScriptContext();
        engine.setContext(otherCtxt);
        otherCtxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
        engine.eval("aglobal = function(){return 12;};");
        // move code-to-reuse to global scope
        otherCtxt.setBindings(otherCtxt.getBindings(ScriptContext.ENGINE_SCOPE), ScriptContext.GLOBAL_SCOPE);
        // create throw-away engine scope
        otherCtxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
        engine.eval("alocal = function() {return aglobal();}");
        
        //////////////////////////// this fails suddenly
        engine.invokeFunction("alocal");
        //////////////////////////////////////////////////////////

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No exception.
ACTUAL -
/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/bin/java ���
Exception in thread "main" javax.script.ScriptException: ReferenceError: "aglobal" is not defined in <eval> at line number 1
	at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:467)
	at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:389)
	at jdk.nashorn.api.scripting.NashornScriptEngine.invokeFunction(NashornScriptEngine.java:190)
	at blah.TestMain.main(TestMain.java:33)
	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:497)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: <eval>:1 ReferenceError: "aglobal" is not defined
	at jdk.nashorn.internal.runtime.ECMAErrors.error(ECMAErrors.java:57)
	at jdk.nashorn.internal.runtime.ECMAErrors.referenceError(ECMAErrors.java:319)
	at jdk.nashorn.internal.runtime.ECMAErrors.referenceError(ECMAErrors.java:291)
	at jdk.nashorn.internal.objects.Global.__noSuchProperty__(Global.java:1432)
	at jdk.nashorn.internal.scripts.Script$Recompilation$2$20$\^eval\_.alocal(<eval>:1)
	at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:640)
	at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:228)
	at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393)
	at jdk.nashorn.api.scripting.ScriptObjectMirror.callMember(ScriptObjectMirror.java:199)
	at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:383)
	... 7 more

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
@Test
public void testInvokeBug() {
NashornScriptEngine engine = (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn");
        ScriptContext otherCtxt = new SimpleScriptContext();
        engine.setContext(otherCtxt);
        otherCtxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
        engine.eval("aglobal = function(){return 12;};");
        // move code-to-reuse to global scope
        otherCtxt.setBindings(otherCtxt.getBindings(ScriptContext.ENGINE_SCOPE), ScriptContext.GLOBAL_SCOPE);
        // create throw-away engine scope
        otherCtxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
        engine.eval("alocal = function() {return aglobal();}");
        
        //////////////////////////// this fails suddenly
        engine.invokeFunction("alocal");
        //////////////////////////////////////////////////////////
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use engine.eval("alocal()"); 
or
engine.compile(...)

But: cannot pass args to function, slower in my tests.


Comments
Nashorn's Global class has __noSuchProperty__ method in which it searches for missing variables from the ScriptContext. There two ways in which the ScriptContext instance to use that can be found: 1) Reading from the thread local "scontext" - this is set/reset around every ScriptEngine.eval call. This is the most preferred ScriptContext instance -- as this one is the "currently used" ScriptContext for the ScriptEngine eval call. If no such ScriptContext is found, [ this situation is possible when calling invokeFunction/invokeMethod or using methods of ScriptObjectMirror ] then 2) Global's "initscontext" is used. This is the "initial" ScriptContext set when Global object is initialized. The current bug happens in scenario (2) with a Global that was created with initscontext set to null! That happens because Global is created from ScriptEngine.createBindings call - in which we can't associate any initial ScriptContext. Solution: When there is no thread-local ScriptContext, global should always use the ScriptEngine instance's 'default' ScriptContext - which can be retrieved using ScriptEngine.getContext() method. The default context of the script engine is always available. And this choice is consistent with expectation of invokeFunction/invokeMethod etc. [i.e., engine's default ScriptContext is expected to be used in these cases anyway]. Also, this choice makes sense for ScriptObjectMirror's method calls as well. i.e., use associated ScriptEngine's default ScriptContext rather than not using any ScriptContext or using the one associated with Global during initialization.
01-10-2015

Attached Test case was executed on Windows 7 64 bit and issue is reproducible , here are the results: JDK 8u51 - Pass JDK 8u60 - Fail JDK 9 -Fail Moving across to dev-team for action.
30-09-2015