Well, the basic property access/modification, indexed element access/modification, function call etc. are indeed glued to JSObject methods. Nashorn includes separate dynalink linker to implement this. But, a JSObject is *not* a Nashorn ScriptObject in *every* possible context. This is by design. This applies to Java POJOs as well. Nashorn tries to give illusion of scriptobject-like behavior for Java POJOs using dynalink "beans linker".
That said, we have been trying to increase number of places where JSObjects and ScriptObjects are uniformly treated. There was a recent fix: http://hg.openjdk.java.net/jdk9/dev/nashorn/rev/c24beef07d1b https://bugs.openjdk.java.net/browse/JDK-8157160 And that has been backported to 8u as well. Thanks, -Sundar On 6/10/2016 10:33 PM, Bill Reed wrote: > I have a Java class ExampleTuple2 that extends AbstractJSObject. My > ExampleTuple2 has a toJSON method. I instantiate the object in JavaScript > (nashorn) if I do a JSON.stringify(tuple2Instance) the result "undefined" > > The API documentation for AbstractJSObject > https://docs.oracle.com/javase/8/docs/jdk/api/nashorn/jdk/nashorn/api/scripting/AbstractJSObject.html > states *"This is the base class for nashorn ScriptObjectMirror class. This > class can also be subclassed by an arbitrary Java class. Nashorn will treat > objects of such classes just like nashorn script objects. Usual nashorn > operations like obj[i], obj.foo, obj.func(), delete obj.foo will be glued > to appropriate method call of this class." * > > Should my toJSON method be calls when using JSON.stringify for this type of > object? > > Example of the Java code. > > package org.eclairjs.nashorn.wrap; > > import jdk.nashorn.api.scripting.AbstractJSObject; > > import org.eclairjs.nashorn.Utils; > > public class ExampleTuple2 extends AbstractJSObject { > > public abstract class WrappedFunction extends AbstractJSObject { > > @Override > > public boolean isFunction() { > > return true; > > } > > } > > Object _1; > > Object _2; > > public ExampleTuple2(scala.Tuple2 tuple2) { > > _1=Utils.javaToJs(tuple2._1(),null); > > _2=Utils.javaToJs(tuple2._2(),null); > > } > > public ExampleTuple2(Object _1, Object _2) { > > this._1=_1; > > this._2=_2; > > } > > WrappedFunction F_toJSON = new WrappedFunction () { > > @Override > > public Object call(Object thiz, Object... args) { > > return "{\"0\":" + _1 + ",\"1\":" + _2 + "}" ; > > } > > }; > > WrappedFunction F_toString = new WrappedFunction () { > > @Override > > public Object call(Object thiz, Object... args) { > > return "(" + _1 + "," + _2 + ")" ; > > } > > }; > > WrappedFunction F_valueOf = new WrappedFunction () { > > @Override > > public Object call(Object thiz, Object... args) { > > return "(" + _1 + "," + _2 + ")"; > > } > > }; > > WrappedFunction F_1 = new WrappedFunction () { > > @Override > > public Object call(Object thiz, Object... args) { > > return _1; > > } > > }; > > WrappedFunction F_2 = new WrappedFunction () { > > @Override > > public Object call(Object thiz, Object... args) { > > return _2; > > } > > }; > > // get the value of that named property > > @Override > > public Object getMember(String name) { > > switch (name) { > > case "_1": return F_1; > > case "_2": return F_2; > > case "valueOf": return F_valueOf; > > case "toJSON": return F_toJSON; > > case "toString": return F_toString; > > } > > throw new RuntimeException("Tuple2."+name+" is not defined"); > > } > > @Override > > public boolean hasMember(String name) { > > switch (name) { > > case "_1": > > case "_2": > > case "valueOf": > > case "toJSON": > > case "toString": > > return true; > > } > > return super.hasMember(name); > > } > > } > > > Example of the JavaScript code. > > var Tuple2 = Java.type('org.eclairjs.nashorn.wrap.ExampleTuple2'); > > var t = new Tuple2("value1", "value2"); > > print("t1 " + t._1()); > > print("t " + t); > > print(JSON.stringify(t));