JDK-8051749 : Nashorn - ClassCastException when using bound method of Java instance
  • Type: Bug
  • Component: core-libs
  • Sub-Component: jdk.nashorn
  • Affected Version: 8u20
  • Priority: P4
  • Status: Resolved
  • Resolution: Not an Issue
  • OS: os_x
  • CPU: x86
  • Submitted: 2014-07-20
  • Updated: 2014-07-23
  • Resolved: 2014-07-23
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
Bug seen on OS X + Linux 64 with both 1.8.0_20-ea-b23 and 1.9.0-ea-b23

ADDITIONAL OS VERSION INFORMATION :
Bug seen on OS X (10.9) + various Linux 64 with both 1.8.0_20-ea-b23 and 1.9.0-ea-b23

A DESCRIPTION OF THE PROBLEM :
Placing a reference to a bound java method into a javascript variable then calling it is not equivalent to calling it directly

var ArrayList = Java.type("java.util.ArrayList")
al = new ArrayList()
al.size() // WORKS
var size = al.size
size() // ClassCastException

java.lang.ClassCastException: Cannot cast jdk.nashorn.internal.runtime.Undefined to java.util.ArrayList
	at java.lang.invoke.MethodHandleImpl.newClassCastException(MethodHandleImpl.java:312)
	at java.lang.invoke.MethodHandleImpl.castReference(MethodHandleImpl.java:307)
	at jdk.nashorn.internal.scripts.Script$Recompilation$514$\^eval\_.:program(bx<Untitled>:2)
	at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:567)
	at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:222)
	at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:376)
	at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:465)
	at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:429)
	at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:425)
	at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:158)

Whereas with a pure JavaScript object, it works as expected:

Foo = function(){}
Foo.prototype.banana = function(){print("BANANA");}
foo = new Foo()
foo.banana() // WORKS
f = foo.banana
f() // WORKS

It seems that there isn't anything that one can actually legally do with a reference to al.size other than toString it. This is unexpected, but if it is to be the case, a better and more descriptive exception than a ClassCastException would be helpful.



REPRODUCIBILITY :
This bug can be reproduced always.


Comments
I don't think this should be treated as a bug. For example, even when using Rhino engine, when you try to run the following (equivalent) script, we get error: var l = new java.util.ArrayList(); var s = l.size; s(); js: Java method "size" was invoked with [object global] as "this" value that can not be converted to Java type java.util.ArrayList. That said, Rhino treats Java methods like script functions and so lets Function.prototype functions be called on it. For example, user can do something like: s.apply(l); // call "s" with "l" as "this". But that - i.e., providing Function.prototype like features on Java methods - would be a separate RFE. The current script "as is" won't work on Rhino too - which is right behavior as we can't find a proper "this" (of right java type) needed to call.
23-07-2014