JDK-8050977 : Java8 Javascript Nashorn exception: no current Global instance for nashorn
  • Type: Bug
  • Component: core-libs
  • Sub-Component: jdk.nashorn
  • Affected Version: 8
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2014-07-17
  • Updated: 2015-06-04
  • Resolved: 2014-10-14
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
8u40 b12Fixed 9Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_11"
Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
Initially i added this here:
http://stackoverflow.com/questions/24784792/java8-javascript-nashorn-exception-no-current-global-instance-for-nashorn

I want to retrieve object generated in JS store them in Java and later call methods on them.
This worked with Java 7, now with Java 8 i get an exception:

    Exception in thread "main" java.lang.IllegalArgumentException: no current Global instance for nashorn
    	at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:492)
	    at jdk.nashorn.api.scripting.NashornScriptEngine.invokeMethod(NashornScriptEngine.java:238)

I have modified the official example from here http://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/api.html a bit.

Now i created a minimal example to produce this exception.
It seems like, if a JS object is passed to Java via return value, it is different to the case JS calls a Java object's method and passes the object.

    public class InvokeScriptMethod {
	
	    static Object o1;
	
	    public static class Context {
	    	public void add( Object o ){
		    	InvokeScriptMethod.o1 = o;
    		}
	    }
    
    	public static void main(String[] args) throws Exception {
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("nashorn");

            engine.put("ctx", new Context());

            engine.eval("function bla(){\n"
            		+ "var obj = new Object();\n"
        	    	+ "obj.var1 = 3;\n"
        		    + "obj.hello = function(name) { print('Hello, ' + this.var1 + ' ' + name); this.var1++; };\n"
            		+ "ctx.add(obj);\n"
            		+ "return obj;\n"
            		+ "}");

            Invocable inv = (Invocable) engine;
        
            Object obj = inv.invokeFunction("bla");
        
            System.out.printf("retrieved as return value        : %s %s\n", obj.getClass(), obj);
            System.out.printf("retrieved via call to java object: %s %s\n", o1.getClass(), o1);

            inv.invokeMethod(obj, "hello", "Script Method!");
            inv.invokeMethod(o1, "hello", "Script Method!"); // <-- exception
        }
    }

program output:

    retrieved as return value        : class jdk.nashorn.api.scripting.ScriptObjectMirror [object Object]
    retrieved via call to java object: class jdk.nashorn.internal.scripts.JO jdk.nashorn.internal.scripts.JO@105fece7
    Hello, 3 Script Method!
    Exception in thread "main" java.lang.IllegalArgumentException: no current Global instance for nashorn

obj is a ScriptObjectMirror as it is expected, o1 is an internal object.
http://cr.openjdk.java.net/~sundar/8023631/webrev.00/src/jdk/nashorn/api/scripting/NashornScriptEngine.java.html line 481 shows how this exception is thrown.
So I think, there is something wrong by wrapping the "naked" JS object into a ScriptObjectMirror when passing as argument to Java.



REGRESSION.  Last worked in version 7u10

ADDITIONAL REGRESSION INFORMATION: 
java version "1.7.0_06"
Java(TM) SE Runtime Environment (build 1.7.0_06-b24)
Java HotSpot(TM) 64-Bit Server VM (build 23.2-b09, mixed mode)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
run example code

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
no exception
ACTUAL -
exception thrown

ERROR MESSAGES/STACK TRACES THAT OCCUR :
no current Global instance for nashorn

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
   public class InvokeScriptMethod {
	
	    static Object o1;
	
	    public static class Context {
	    	public void add( Object o ){
		    	InvokeScriptMethod.o1 = o;
    		}
	    }
    
    	public static void main(String[] args) throws Exception {
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("nashorn");

            engine.put("ctx", new Context());

            engine.eval("function bla(){\n"
            		+ "var obj = new Object();\n"
        	    	+ "obj.var1 = 3;\n"
        		    + "obj.hello = function(name) { print('Hello, ' + this.var1 + ' ' + name); this.var1++; };\n"
            		+ "ctx.add(obj);\n"
            		+ "return obj;\n"
            		+ "}");

            Invocable inv = (Invocable) engine;
        
            Object obj = inv.invokeFunction("bla");
        
            System.out.printf("retrieved as return value        : %s %s\n", obj.getClass(), obj);
            System.out.printf("retrieved via call to java object: %s %s\n", o1.getClass(), o1);

            inv.invokeMethod(obj, "hello", "Script Method!");
            inv.invokeMethod(o1, "hello", "Script Method!"); // <-- exception
        }
    }

---------- END SOURCE ----------


Comments
Change in MethodEmitter.getDynamicSignature attempted: diff -r 9dc87837f70a src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java Fri Oct 10 17:59:22 2014 +0530 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java Mon Oct 13 14:10:01 2014 +0530 @@ -2125,7 +2125,11 @@ int pos = 0; for (int i = argCount - 1; i >= 0; i--) { - paramTypes[i] = stack.peek(pos++); + Type type = stack.peek(pos++); + if (type.isObject() && ScriptObject.class.isAssignableFrom(type.getTypeClass())) { + type = Type.OBJECT; + } + paramTypes[i] = type; } final String descriptor = Type.getMethodDescriptor(returnType, paramTypes); for (int i = 0; i < argCount; i++) { The above fixes object literal passed as well. But with that change testVarargs method of jdk.nashorn.api.javaaccess.ArrayConversionTest test fails - as that one expects JS NativeArray be passable for Java varargs Object[]. With NativeArray type being encoded in Object type in dynamic signature, that conversion does not happen.
13-10-2014

While the fix wraps ScriptObjects as ScriptObjectMirrors for Object type params, I still face an issue. The fix works only when objects are not object literals! var obj = { foo: 23 }; jobj.javaMethod(obj); // obj -> wrapped as ScriptObjectMirror jobj.javaMethod({ foo: 23 }); // object literal is not converted as ScriptObjectMirror -as typed handle has JO* type for argument (and not Object) in this case. File: Test.java import javax.script.*; public class Test { public static class Context { private Object myobj; public void set(Object o) { myobj = o; } public Object get() { return myobj; } } // @bug 8050977: Java8 Javascript Nashorn exception: // no current Global instance for nashorn public static void main(String[] a) throws Exception { final ScriptEngineManager manager = new ScriptEngineManager(); final ScriptEngine e = manager.getEngineByName("nashorn"); final Context ctx = new Context(); e.put("ctx", ctx); e.eval("var obj = { foo: function(str) { return str.toUpperCase() } }"); e.eval("ctx.set(obj)"); // uncomment the line below to get exception. i.e., Object literal is not wrapped as ScriptObjectMirror // e.eval("ctx.set({ foo: function(str) { return str.toUpperCase() } })"); final Invocable inv = (Invocable)e; System.out.println(inv.invokeMethod(ctx.get(), "foo", "hello")); } } If I try to check for ScriptObject.class.isAssignableFrom (and not just == Object.class), I get target/filter method handle mismatch... (in NashornBeansLinker) Another attempt I made was to change MethodEmitter.getDynamicSignature to use generated Object for any ScriptObject subclass. While that fixes ObjectLiteral->to->mirror conversion, it introduces a different problem. Java vararg methods accept JS array for vararg Object[] param. That depends on call signature to be NativeArray. With Object as type, a test that checks testVarargs in Java access test suite fails.
13-10-2014

webrev.00.zip is first attempt webrev for this bug. It introduces mirror-for-scriptobject always (for "Object" type params, ScriptObject is converted to ScriptObjectMirror automatically)
13-10-2014

Do we want to wrap ScriptObjects as mirrors whenever Java call is made and unwrap Object values for possible ScriptObjectMirror objects from current global? Without doing that, it seems not possible to fix this issue. But doing such wrap/unwrap means every Java method with Object type param and Object type return value will be affected. That includes Java collection methods, (object) array element access as well. If we don't want to wrap/unwrap, then we should classify the current issue as "user error" (i.e., escaping raw Script Objects to Java level) and resolve as "will-not-fix".
08-10-2014

If we uniformly wrap ScriptObjects as ScriptObjectMirrors whenever Object type is required on java side (see JDK-8053910), this issue will go away - as user won't be able to get hold of 'raw' ScriptObject into java land.
05-08-2014

Hoping nashorn team can triage this webbug and determine if it's an issue.
17-07-2014