-
Notifications
You must be signed in to change notification settings - Fork 900
Description
NativeJSON.stringify used to throw an exception when it experienced a problem. This way it was possible to identify the problem in the json string, usually an object that was a java string. Now (since 1.7.8) it goes into a Stackoverflow. This should be changed imho. To see how i struggeld to find out what was going on i left my analysis below.
We upgraded from 1.7.7 to version 1.7.11. After that update we now get a Stackoverflow for the following code that finally uses org.mozilla.javascript.NativeJSON.stringify() on the Java side. It seems that this occurs if you add a Java object to the json you want to stringify. The following example uses trim to generate a java string, you could however also use another object like Joda DateTime etc to provoke the stackoverflow.
Here's the client code
var Util =
{
getModel : function(x)
{
var model = {
x : x,
}
return model;
},
}
//example for a java object in the json:
var x = new Packages.java.lang.String("Hello World").trim(); //-> Stackoverfow
//var x = "Hello World"; -> works fine
var model = Util.getModel(x);
println(new Packages.org.acme.commons.lang.RhinoUtil.jsonStringify(model));
And here's the code for the RhinoUtil.jsonStringify()
public static final String jsonStringify(NativeObject nativeObj)
{
ContextFactory cf = null;
try
{
cf = new ContextFactory();
}
catch (NoClassDefFoundError nce)
{
throw nce;
}
Context cx = cf.enterContext();
try
{
ScriptableObject sealedSharedScope = cx.initStandardObjects(null, true); //sealed object
String loadMe = "RegExp; getClass; java; Packages; JavaAdapter;";
cx.evaluateString(sealedSharedScope , loadMe, "lazyLoad", 0, null);
sealedSharedScope.sealObject();
return NativeJSON.stringify(cx, sealedSharedScope, nativeObj, null, null) + "";
}
finally
{
Context.exit();
}
}
If we analyse the stackoverflow exception we get the following stacktrace
java.lang.StackOverflowError
at org.mozilla.javascript.JavaMembers.getIds(JavaMembers.java:177)
at org.mozilla.javascript.NativeJavaObject.getIds(NativeJavaObject.java:188)
at org.mozilla.javascript.NativeJSON.jo(NativeJSON.java:353)
at org.mozilla.javascript.NativeJSON.str(NativeJSON.java:322)
at org.mozilla.javascript.NativeJSON.jo(NativeJSON.java:359)
at org.mozilla.javascript.NativeJSON.str(NativeJSON.java:322)
at org.mozilla.javascript.NativeJSON.jo(NativeJSON.java:359)
at org.mozilla.javascript.NativeJSON.str(NativeJSON.java:322)
...
Please note that we need a way to stringify on the java side, therefore js JSON.stringify() is not an option. Please help.
Tested same code with 1.7.7 and it seems that it never worked, however the 1.7.7 error message was more clear
Java class "[Ljava.lang.reflect.Constructor;" has no public instance field or method named "toJSON"
Note that i also changed my jdk from oracle do adopt open jdk 1.8. Will research further...
Ok, found out that our internal util already checks for this and has a fallback
NativeObject nativeObj = (NativeObject) object;
try
{
//this will fail if the json contains a Java String, therefore we will use a fallback
return RhinoUtil.jsonStringify(nativeObj);
}
catch (Exception e)
{
// fallback to old style iteration with nativeObject.getIds()
return JSONStringifier.jsonStringify(nativeObj);
}
I now changed the catch from Exception to Throwable so that it also gets the Stackoverflow which is Throwable, not Exception. Now everything works as before. So actually for me the problme is solved and we can continue to use 1.7.11. Sorry for that elaborate story. But it shows that it would be useful to avoid the Stackoverflow and change it to the previous exception that was easier to understand.