LiquidPlayer / LiquidCore

Node.js virtual machine for Android and iOS
MIT License
1.01k stars 127 forks source link

NPE on boolean function result #143

Open kaneelyster opened 4 years ago

kaneelyster commented 4 years ago

I'm using LiquidCore Android 0.5.0 as a native JS engine. I load a JS file and then call into/from JS without issues.

jsContext.property("LibraryName").toObject().property(name).toFunction().call(context, theCallback)

The JS functions make use of callbacks

{key:"isInitialized",value:function isInitialized(callback){return promiseToLibraryNameCallback(new Promise(function(resolve){resolve(!_.isEmpty(LibraryName.instance))}),callback)}}

Which is handled Kotlin-side

JSObject(theContext).property("successWithResult", object : JSFunction(context, "successWithResult") {
    @Suppress("unused")
    fun successWithResult(res: JSObject) {
        //Do success things
    }
})

When updating to 0.5.1 or later however, I get NPEs when a boolean result is getting converted back to a Java object right before the callback is triggered.

Specifically, in JSFunction.functionCallback() on the line JSValue value = function(thiz,args,invokeObject);.

With these values:

Screenshot 2019-12-04 at 15 25 43

In 0.5.0 value gets calculated as null but in 0.5.1 a NPE is thrown inside function() for the same input, seemingly due to the value of args[0] which is a JSValue with a boolean value, i.e. new JSValue(context, true). If I debug and change args[0] to new JSValue(context, "true") then the callback triggers successfully with no NPE. Same result if I instead change the JS to return "true" instead of true. Other JS functions returning complex results in the same manner work without issue. On iOS with JavaScriptCore the exact same JS code runs without issue.

The NPE stack trace:

java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Object.equals(java.lang.Object)' on a null object reference
at org.liquidplayer.javascript.JSContext.getObjectFromRef(JSContext.java:279)
at org.liquidplayer.javascript.JSContext.getObjectFromRef(JSContext.java:300)
at org.liquidplayer.javascript.JSValue.toObject(JSValue.java:380)
at org.liquidplayer.javascript.JSValue.toJavaObject(JSValue.java:462)
at org.liquidplayer.javascript.JSFunction.function(JSFunction.java:534)
at org.liquidplayer.javascript.JSFunction.functionCallback(JSFunction.java:496)
at org.liquidplayer.javascript.JNIJSObject.callAsFunction(Native Method)
at org.liquidplayer.javascript.JNIJSObject.callAsFunction(JNIJSObject.java:87)
at org.liquidplayer.javascript.JSFunction.apply(JSFunction.java:443)
at org.liquidplayer.javascript.JSFunction.call(JSFunction.java:407)

My expectation is for callbacks returning boolean results to be handled the same as before and the same as complex objects. Or is there another explanation for why this stopped working?

ericwlange commented 4 years ago

Thanks for reporting. There was a pretty massive change between 5.0 and 5.1 under the covers, so I am not surprised there were some unintended artefacts. I am only surprised this hasn't been found before now. I will take a look.