Javet icon indicating copy to clipboard operation
Javet copied to clipboard

Crash by Nodejs Circular API

Open pbk20191 opened this issue 10 months ago • 1 comments

Thanks for this brilliant project. I found that some Nodejs Api which act as a builder pattern cause a cricular reference in this project which makes it crash.


class Block(private val block: java.lang.Runnable): IJavetDirectCallable.NoThisAndNoResult<RuntimeException> {
    override fun call(vararg v8Values: V8Value) {
        block.run()
    }

}

val runtime = V8Host.getNodeInstance().createV8Runtime<V8Runtime>()
    val call = it.createV8ValueFunction(
        JavetCallbackContext(
            "block",
            V8ValueSymbolType.None,
            JavetCallbackType.DirectCallNoThisAndNoResult,
            Block{
                println("fooo")
            }
        )
    )
    val foo = runtime.globalObject.invoke<V8ValueObject>("setImmediate", call, it.globalObject)

and then

// Boom!
foo.invokeObject<V8ValueObject>("unref").invokeObject<V8ValueObject>("ref")

or

foo.invokeObject<V8ValueObject>("unref")
runtime.globalObject.set("foo", foo)
// Boom!
runtime.getExecutor("foo.ref().unref().ref().unref()").executeVoid()

NodeJS setImmediate returns Immediate, which returns self when calling ref and unref.

which make stacktrace like below

Exception in thread "main" com.caoccao.javet.exceptions.JavetConverterException: Circular structure is detected with max depth 20 reached
	at com.caoccao.javet.exceptions.JavetConverterException.circularStructure(JavetConverterException.java:60)
	at com.caoccao.javet.interop.converters.BaseJavetConverter.validateDepth(BaseJavetConverter.java:111)
	at com.caoccao.javet.interop.converters.JavetPrimitiveConverter.toObject(JavetPrimitiveConverter.java:51)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:203)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$0(JavetObjectConverter.java:210)
	at com.caoccao.javet.values.reference.V8ValueArray.forEach(V8ValueArray.java:88)
	at com.caoccao.javet.values.reference.V8ValueArray.forEach(V8ValueArray.java:68)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:210)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.lambda$toObject$4(JavetObjectConverter.java:306)
	at com.caoccao.javet.values.reference.V8ValueObject.forEach(V8ValueObject.java:310)
	at com.caoccao.javet.values.reference.IV8ValueObject.forEach(IV8ValueObject.java:302)
	at com.caoccao.javet.interop.converters.JavetObjectConverter.toObject(JavetObjectConverter.java:297)
	at com.caoccao.javet.interop.converters.BaseJavetConverter.toObject(BaseJavetConverter.java:66)
	at com.caoccao.javet.interop.V8Runtime.toObject(V8Runtime.java:3666)
	at com.caoccao.javet.interop.IV8Convertible.toObject(IV8Convertible.java:58)
	at com.caoccao.javet.values.reference.IV8ValueObject.invokeObject(IV8ValueObject.java:1142)

WorkAround

Just not using builder pattern or using it only in nodejs world resolve for this kind of case.

foo.invokeVoid("ref", null)
foo.invokeVoid("unref", null)

or

it.getExecutor("foo.ref().unref().ref().unref()").executeVoid()

pbk20191 avatar Apr 25 '24 06:04 pbk20191