Giter VIP home page Giter VIP logo

quickjs-kt's People

Contributors

dokar3 avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

quickjs-kt's Issues

Proper way to detect circular references

Currently, the detection of circular references not working on deep-level circular references:

// Not able to detect in this case
val set = mutableSetOf<Any?>()
val array = arrayOf(set)
set.add(array)

Need to re-implement the detection code.

Correct error handling for async functions

Currently, all jobs will get canceled if one throws an error, regardless of whether they have been caught in JavaScript.

The behavior needs to be implemented correctly.

Related to #12

Some questions about cocurrency

I love this library, but I have some questions.

Question1: Can I use exactly one same QuickJS instance on two coroutines/threads?

Question2: According to the README, QuickJs.create(Dispatchers.Default), what exactly is the dispatcher parameter used for? Must it be a single-thread dispatcher(because JavaScript is a single-thread language)?

evaluates block each other

private suspend fun evalAndAwait(evalBlock: suspend () -> Any?): Any? {
        ensureNotClosed()
        evalException = null
        loadModules()
        val result = jsResultMutex.withLock {
            jsMutex.withLock { evalBlock() }
            awaitAsyncJobs()
            jsMutex.withLock { getEvaluateResult(context, globals) }
        }
        handleException()
        return result
    }

I have a JS call which will be delayed for 5 seconds, within the 5 second other evaluate calls block. I think it is the jsResultMutex here blocks other evaluate calls.

[Feature request] Allow strictly typed arguments in FunctionBindings

The problem

Currently, both FunctionBinding<R> and AsyncFunctionBinding<R> only allow generic return types

fun interface FunctionBinding<R> : Binding {
    fun invoke(args: Array<Any?>): R
}

fun interface AsyncFunctionBinding<R> : Binding {
    suspend fun invoke(args: Array<Any?>): R
}

The purpose

I propose either making the Binding interface public to be able to create your own implementation of binding with custom arguments type or changing the FunctionBindings to support custom argument types. I am making this an issue first because this would be a breaking change.

fun interface FunctionBinding<A, R> : Binding {
    fun invoke(args: A): R
}

fun interface AsyncFunctionBinding<A, R> : Binding {
    suspend fun invoke(args: A): R
}

Use case

As this QuickJs bindings library doesn't provide a fetch implementation, following the example in your readme, having something like this:

interface FetchArguments {
    val url: String
    val options: FetchOptions
}

interface FetchOptions {
    val method: String
    val headers: Map<String, String>
    val body: String
}

interface FetchResponse {
    val ok: Boolean
    val status: Int
    val statusText: String
    val headers: Map<String, String>
    val body: String
    val responseURL: String
    val responseText: String
}

/* ------------------------- */

quickJs {
    asyncFunction<FetchArguments, FetchResponse>("fetch") { args: FetchArguments ->
        // Custom fetch implementation
    }
}

would simplify the way we parse arguments, allowing for fewer type errors overall and providing closer to the original JavaScript experience.

The alternative

Currently, the only alternative option I see is using Reflection. However, the documentation makes it unclear, whether we can provide pure functions through reflection, or we need to wrap them with objects (which is also not ideal, since calling http.get is non-standard JavaScript and not every developer is willing to read the documentation to understand, why there is no fetch function.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

This repository currently has no open or pending branches.

Detected dependencies

bun
scripts/package.json
  • @types/bun ^1.0.5
github-actions
.github/workflows/benchmark.yaml
  • actions/checkout v4
  • actions/setup-java v4
  • jwlawson/actions-setup-cmake v2.0
  • goto-bus-stop/setup-zig v2
  • oven-sh/setup-bun v2
  • gradle/actions v3
  • stefanzweifel/git-auto-commit-action v5
.github/workflows/build.yaml
  • actions/checkout v4
  • actions/setup-java v4
  • jwlawson/actions-setup-cmake v2.0
  • goto-bus-stop/setup-zig v2
  • oven-sh/setup-bun v2
  • gradle/actions v3
.github/workflows/publish.yaml
  • actions/checkout v4
  • actions/setup-java v4
  • jwlawson/actions-setup-cmake v2.0
  • goto-bus-stop/setup-zig v2
  • oven-sh/setup-bun v2
  • gradle/actions v3
gradle
buildSrc/src/main/kotlin/com/dokar/quickjs/Platform.kt
buildSrc/src/main/kotlin/com/dokar/quickjs/applyQuickJsNativeBuildTasks.kt
buildSrc/src/main/kotlin/com/dokar/quickjs/buildQuickJsNativeLibrary.kt
buildSrc/src/main/kotlin/com/dokar/quickjs/disableUnsupportedPlatformTasks.kt
gradle.properties
settings.gradle.kts
build.gradle.kts
benchmark/build.gradle.kts
buildSrc/settings.gradle.kts
buildSrc/build.gradle.kts
gradle/libs.versions.toml
  • androidx.core:core-ktx 1.13.1
  • junit:junit 4.13.2
  • androidx.test.ext:junit 1.2.1
  • androidx.activity:activity-compose 1.9.0
  • androidx.compose.ui:ui-tooling 1.6.8
  • androidx.compose.ui:ui-tooling-preview 1.6.8
  • org.jetbrains.kotlin:kotlin-test 2.0.0
  • org.jetbrains.kotlin:kotlin-test-junit 2.0.0
  • org.jetbrains.kotlinx:kotlinx-coroutines-core 1.8.1
  • org.jetbrains.kotlinx:kotlinx-coroutines-test 1.8.1
  • org.jetbrains.kotlinx:kotlinx-benchmark-runtime 0.4.11
  • com.github.ajalt.clikt:clikt 4.4.0
  • org.jetbrains.kotlinx:kotlinx-serialization-properties 1.7.1
  • io.ktor:ktor-client-core 3.0.0-beta-2
  • io.ktor:ktor-client-cio 3.0.0-beta-2
  • com.squareup.moshi:moshi 1.15.1
  • com.squareup.moshi:moshi-kotlin-codegen 1.15.1
  • com.android.application 8.5.1
  • com.android.library 8.5.1
  • org.jetbrains.compose 1.6.11
  • org.jetbrains.kotlin.multiplatform 2.0.0
  • org.jetbrains.kotlin.jvm 2.0.0
  • org.jetbrains.kotlin.android 2.0.0
  • org.jetbrains.kotlinx.benchmark 0.4.11
  • org.jetbrains.kotlin.plugin.allopen 2.0.0
  • org.jetbrains.kotlin.plugin.compose 2.0.0
  • org.jetbrains.kotlin.plugin.serialization 2.0.0
  • com.vanniktech.maven.publish 0.29.0
  • com.google.devtools.ksp 2.0.0-1.0.23
quickjs/gradle.properties
quickjs/build.gradle.kts
quickjs-converter-ktxserialization/gradle.properties
quickjs-converter-ktxserialization/build.gradle.kts
quickjs-converter-moshi/gradle.properties
quickjs-converter-moshi/build.gradle.kts
samples/js-eval/build.gradle.kts
samples/openai/build.gradle.kts
samples/repl/build.gradle.kts
  • com.github.johnrengelman.shadow 8.1.1
gradle-wrapper
gradle/wrapper/gradle-wrapper.properties
  • gradle 8.9

  • Check this box to trigger a request for Renovate to run again on this repository

Undefined Symbol for arm64 on linking

Hi,

I have a Kotlin Multiplatform project that fails at the linking step when building for iOS (device or simulator).

Here is the stack in XCode when trying to build :

Stack Trace :

Undefined symbols for architecture arm64:
  "_JS_AtomToValue", referenced from:
      _quickjs_JS_AtomToValue_wrapper62 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_Call", referenced from:
      _quickjs_JS_Call_wrapper145 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_CallConstructor", referenced from:
      _quickjs_JS_CallConstructor_wrapper147 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_DefinePropertyGetSet", referenced from:
      _quickjs_JS_DefinePropertyGetSet_wrapper158 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_DefinePropertyValue", referenced from:
      _quickjs_JS_DefinePropertyValue_wrapper155 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_DefinePropertyValueStr", referenced from:
      _quickjs_JS_DefinePropertyValueStr_wrapper157 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_Eval", referenced from:
      _quickjs_JS_Eval_wrapper150 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_EvalFunction", referenced from:
      _quickjs_JS_EvalFunction_wrapper188 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_ExecutePendingJob", referenced from:
      _quickjs_JS_ExecutePendingJob_wrapper184 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_FreeAtom", referenced from:
      _quickjs_JS_FreeAtom_wrapper60 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_FreeCString", referenced from:
      _quickjs_JS_FreeCString_wrapper118 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_GetArrayBuffer", referenced from:
      _quickjs_JS_GetArrayBuffer_wrapper168 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_GetException", referenced from:
      _quickjs_JS_GetException_wrapper91 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_GetGlobalObject", referenced from:
      _quickjs_JS_GetGlobalObject_wrapper152 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_GetOwnPropertyNames", referenced from:
      _quickjs_JS_GetOwnPropertyNames_wrapper143 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_GetPropertyInternal", referenced from:
      _quickjs_JS_GetProperty_wrapper129 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_GetPropertyStr", referenced from:
      _quickjs_JS_GetPropertyStr_wrapper130 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_GetPropertyUint32", referenced from:
      _quickjs_JS_GetPropertyUint32_wrapper131 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_GetRuntime", referenced from:
      _quickjs_JS_GetRuntime_wrapper20 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_IsArray", referenced from:
      _quickjs_JS_IsArray_wrapper127 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_IsError", referenced from:
      _quickjs_JS_IsError_wrapper92 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_IsFunction", referenced from:
      _quickjs_JS_IsFunction_wrapper123 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_IsInstanceOf", referenced from:
      _quickjs_JS_IsInstanceOf_wrapper153 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_JSONStringify", referenced from:
      _quickjs_JS_JSONStringify_wrapper164 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_NewArray", referenced from:
      _quickjs_JS_NewArray_wrapper126 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_NewArrayBuffer", referenced from:
      _quickjs_JS_NewArrayBuffer_wrapper165 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_NewAtom", referenced from:
      _quickjs_JS_NewAtom_wrapper57 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_NewCFunctionData", referenced from:
      _quickjs_JS_NewCFunctionData_wrapper193 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_NewContext", referenced from:
      _quickjs_JS_NewContext_wrapper15 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_NewError", referenced from:
      _quickjs_JS_NewError_wrapper94 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_NewObject", referenced from:
      _quickjs_JS_NewObject_wrapper122 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_NewPromiseCapability", referenced from:
      _quickjs_JS_NewPromiseCapability_wrapper171 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_NewRuntime", referenced from:
      _quickjs_JS_NewRuntime_wrapper2 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_NewString", referenced from:
      _quickjs_JS_NewString_wrapper111 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_PromiseResult", referenced from:
      _quickjs_JS_PromiseResult_wrapper173 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_PromiseState", referenced from:
      _quickjs_JS_PromiseState_wrapper172 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_ReadObject", referenced from:
      _quickjs_JS_ReadObject_wrapper187 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_SetHostPromiseRejectionTracker", referenced from:
      _quickjs_JS_SetHostPromiseRejectionTracker_wrapper174 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_SetPropertyStr", referenced from:
      _quickjs_JS_SetPropertyStr_wrapper136 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_SetPropertyUint32", referenced from:
      _quickjs_JS_SetPropertyUint32_wrapper134 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_Throw", referenced from:
      _quickjs_JS_Throw_wrapper90 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_ToBool", referenced from:
      _quickjs_JS_ToBool_wrapper102 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_ToCStringLen2", referenced from:
      _quickjs_JS_ToCString_wrapper117 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_ToFloat64", referenced from:
      _quickjs_JS_ToFloat64_wrapper107 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_ToInt32", referenced from:
      _quickjs_JS_ToInt32_wrapper103 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_ToInt64", referenced from:
      _quickjs_JS_ToInt64_wrapper105 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_UpdateStackTop", referenced from:
      _quickjs_JS_UpdateStackTop_wrapper7 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_JS_WriteObject", referenced from:
      _quickjs_JS_WriteObject_wrapper185 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "___JS_FreeValue", referenced from:
      _quickjs_JS_FreeValue_wrapper97 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
  "_js_free", referenced from:
      _quickjs_js_free_wrapper47 in Shared[arm64][9](libquickjs-kt:quickjs-cinterop-quickjs-cache.a.o)
ld: symbol(s) not found for architecture arm64

Steps to reproduce :

  • Create a new kotlin multiplatform project from their wizard here
  • Add implementation of quickjs in the sourceSets in build.gradle.kts of the shared module
  • Call a quickJs method in Greeting.kt in commonMain in shared module
  • Open the XCode project in iosApp folder
  • Try to build

Any idea of where it comes from ?

Thanks for your help !

iOS: com.dokar.quickjs.QuickJsException: InternalError: stack overflow

com.dokar.quickjs.QuickJsException: InternalError: stack overflow
    at <anonymous> (main.js)
    at <anonymous> (main.js)
    at <anonymous> (main.js)
    at <anonymous> (main.js)
    at <anonymous> (main.js)
    at Qu (main.js)
    at <anonymous> (main.js)
    at call (native)
    at <anonymous> (main.js)
    at Ut (main.js)
    at ce (main.js)
    at ve (main.js)
    at <anonymous> (main.js)
    at <anonymous> (main.js)
    at xn (main.js)
    at kn (main.js)
    at <anonymous> (main.js)
    at te (main.js)
    at ne (main.js)
    at <anonymous> (main.js)
    at <anonymous> (main.js)
    at <anonymous> (main.js)
    at <anonymous> (main.js)
    at <anonymous> (main.js)
    at <anonymous> (main.js)
    at <eval> (main.js:5)

Uncaught Kotlin exception:     at 0   KKit                                0x1032652c3        kfun:kotlin.Throwable#<init>(){} + 95 
    at 1   KKit                                0x10325e4ef        kfun:kotlin.Exception#<init>(){} + 87 
    at 2   KKit                                0x1035e5ab3        kfun:com.dokar.quickjs.QuickJsException#<init>(kotlin.String?){} + 87 
    at 3   KKit                                0x103604cd3        kfun:com.dokar.quickjs.bridge.newKtError#internal + 3611 
    at 4   KKit                                0x103601fab        kfun:com.dokar.quickjs.bridge#jsErrorToKtError(kotlinx.cinterop.CPointer<quickjs.JSContext>;kotlinx.cinterop.CValue<quickjs.JSValue>){}kotlin.Throwable + 5083 
    at 5   KKit                                0x1035ff997        kfun:com.dokar.quickjs.bridge#toKtValue__at__kotlinx.cinterop.CValue<quickjs.JSValue>(kotlinx.cinterop.CPointer<quickjs.JSContext>){}kotlin.Any? + 5287 
    at 6   KKit                                0x10361d673        kfun:com.dokar.quickjs.bridge.promiseRejectionHandler#internal + 491 
    at 7   KKit                                0x10361d90f        _717569636b6a732d6b743a717569636b6a732f55736572732f72756e6e65722f776f726b2f717569636b6a732d6b742f717569636b6a732d6b742f717569636b6a732f7372632f6e61746976654d61696e2f6b6f746c696e2f636f6d2f646f6b61722f717569636b6a732f6272696467652f70726f6d69736552656a656374696f6e48616e646c65722e6b74_knbridge0 + 307 
    at 8   KKit                                0x103622273        _717569636b6a732d6b743a717569636b6a732f55736572732f72756e6e65722f776f726b2f717569636b6a732d6b742f717569636b6a732d6b742f717569636b6a732f7372632f6e61746976654d61696e2f6b6f746c696e2f636f6d2f646f6b61722f717569636b6a732f6272696467652f70726f6d69736552656a656374696f6e48616e646c65722e6b74_kncfun3 + 63 
    at 9   KKit                                0x102ae122f        fulfill_or_reject_promise + 347 
    at 10  KKit                                0x102aa524f        js_promise_resolve_function_call + 651 
    at 11  KKit                                0x102a901fb        JS_CallInternal + 599 
    at 12  KKit                                0x102a8ff87        JS_Call + 107 
    at 13  KKit                                0x102ae18db        js_async_function_resume + 211 
    at 14  KKit                                0x102aa5533        js_async_function_resolve_call + 299 
    at 15  KKit                                0x102a901fb        JS_CallInternal + 599 
    at 16  KKit                                0x102a8ff87        JS_Call + 107 
    at 17  KKit                                0x102ac91bb        promise_reaction_job + 371 
    at 18  KKit                                0x102a79b4b        JS_ExecutePendingJob + 131 
    at 19  KKit                                0x1035f5f4f        kfun:com.dokar.quickjs.bridge#executePendingJob(kotlinx.cinterop.CPointer<quickjs.JSRuntime>){}com.dokar.quickjs.bridge.ExecuteJobResult + 847 
    at 20  KKit                                0x1035f0503        kfun:com.dokar.quickjs.QuickJs.$invokeAsyncFunction$lambda$2COROUTINE$7.invokeSuspend#internal + 3755 
    at 21  KKit                                0x1035f089b        kfun:com.dokar.quickjs.QuickJs.invokeAsyncFunction$lambda$2#internal + 451 
    at 22  KKit                                0x1035f0e63        kfun:com.dokar.quickjs.QuickJs.$invokeAsyncFunction$lambda$2$FUNCTION_REFERENCE$2.invoke#internal + 151 
    at 23  KKit                                0x103390e3f        kfun:kotlin.Function2#invoke(1:0;1:1){}1:2-trampoline + 115 
    at 24  KKit                                0x10326deab        kfun:kotlin.coroutines.intrinsics.object-4.invokeSuspend#internal + 731 
    at 25  KKit                                0x10339075b        kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any?-trampoline + 67 
    at 26  KKit                                0x10326aaa3        kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 623 
    at 27  KKit                                0x10339083b        kfun:kotlin.coroutines.Continuation#resumeWith(kotlin.Result<1:0>){}-trampoline + 99 
    at 28  KKit                                0x10347382b        kfun:kotlinx.coroutines.DispatchedTask#run(){} + 1879 
    at 29  KKit                                0x1034a1d77        kfun:kotlinx.coroutines.Runnable#run(){}-trampoline + 91 
    at 30  KKit                                0x10349962f        kfun:kotlinx.coroutines.MultiWorkerDispatcher.$workerRunLoop$lambda$2COROUTINE$0.invokeSuspend#internal + 1467 
    at 31  KKit                                0x10339075b        kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any?-trampoline + 67 
    at 32  KKit                                0x10326aaa3        kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 623 
    at 33  KKit                                0x10339083b        kfun:kotlin.coroutines.Continuation#resumeWith(kotlin.Result<1:0>){}-trampoline + 99 
    at 34  KKit                                0x10347382b        kfun:kotlinx.coroutines.DispatchedTask#run(){} + 1879 
    at 35  KKit                                0x1034a1d77        kfun:kotlinx.coroutines.Runnable#run(){}-trampoline + 91 
    at 36  KKit                                0x10341c027        kfun:kotlinx.coroutines.EventLoopImplBase#processNextEvent(){}kotlin.Long + 287 
    at 37  KKit                                0x1034a1ac3        kfun:kotlinx.coroutines.EventLoop#processNextEvent(){}kotlin.Long-trampoline + 51 
    at 38  KKit                                0x103492bb3        kfun:kotlinx.coroutines.BlockingCoroutine.joinBlocking#internal + 435 
    at 39  KKit                                0x103491a17        kfun:kotlinx.coroutines#runBlocking(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}0:0 + 1391 
    at 40  KKit                                0x103491bf7        kfun:kotlinx.coroutines#runBlocking$default(kotlin.coroutines.CoroutineContext?;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>;kotlin.Int){0§<kotlin.Any?>}0:0 + 239 
    at 41  KKit                                0x103497973        kfun:kotlinx.coroutines.MultiWorkerDispatcher.workerRunLoop#internal + 183 
    at 42  KKit                                0x103498cef        kfun:kotlinx.coroutines.MultiWorkerDispatcher.<init>$lambda$1$lambda$0#internal + 67 
    at 43  KKit                                0x103499feb        kfun:kotlinx.coroutines.MultiWorkerDispatcher.$<init>$lambda$1$lambda$0$FUNCTION_REFERENCE$5.invoke#internal + 71 
    at 44  KKit                                0x10349a0bb        kfun:kotlinx.coroutines.MultiWorkerDispatcher.$<init>$lambda$1$lambda$0$FUNCTION_REFERENCE$5.$<bridge-UNN>invoke(){}#internal + 71 
    at 45  KKit                                0x10338d167        kfun:kotlin.Function0#invoke(){}1:0-trampoline + 99 
    at 46  KKit                                0x103276b03        WorkerLaunchpad + 131 
    at 47  KKit                                0x1033ed5f7        _ZN6Worker19processQueueElementEb + 2635 
    at 48  KKit                                0x1033eca37        _ZN12_GLOBAL__N_113workerRoutineEPv + 219 
    at 49  libsystem_pthread.dylib             0x10591b413        _pthread_start + 103 
    at 50  libsystem_pthread.dylib             0x1059165df        thread_start + 7 
    Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [androidx.compose.runtime.BroadcastFrameClock@66c0528, StandaloneCoroutine{Cancelling}@d1d3290, FlushCoroutineDispatcher@6d74160]
        at 0   KKit                                0x10326512b        kfun:kotlin.Throwable#<init>(kotlin.String?){} + 119 
        at 1   KKit                                0x10325e577        kfun:kotlin.Exception#<init>(kotlin.String?){} + 115 
        at 2   KKit                                0x10325e797        kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 115 
        at 3   KKit                                0x10349b427        kfun:kotlinx.coroutines.internal.DiagnosticCoroutineContextException#<init>(kotlin.coroutines.CoroutineContext){} + 167 
        at 4   KKit                                0x10346facb        kfun:kotlinx.coroutines.internal#handleUncaughtCoroutineException(kotlin.coroutines.CoroutineContext;kotlin.Throwable){} + 647 
        at 5   KKit                                0x103417b57        kfun:kotlinx.coroutines#handleCoroutineException(kotlin.coroutines.CoroutineContext;kotlin.Throwable){} + 515 
        at 6   KKit                                0x10340b5a7        kfun:kotlinx.coroutines.StandaloneCoroutine.handleJobException#internal + 167 
        at 7   KKit                                0x1034a353f        kfun:kotlinx.coroutines.JobSupport#handleJobException(kotlin.Throwable){}kotlin.Boolean-trampoline + 59 
        at 8   KKit                                0x103421d27        kfun:kotlinx.coroutines.JobSupport.finalizeFinishingState#internal + 1131 
        at 9   KKit                                0x103429b43        kfun:kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath#internal + 2207 
        at 10  KKit                                0x10342926b        kfun:kotlinx.coroutines.JobSupport.tryMakeCompleting#internal + 755 
        at 11  KKit                                0x103428d9b        kfun:kotlinx.coroutines.JobSupport#makeCompletingOnce(kotlin.Any?){}kotlin.Any? + 379 
        at 12  KKit                                0x10340953b        kfun:kotlinx.coroutines.AbstractCoroutine#resumeWith(kotlin.Result<1:0>){} + 211 
        at 13  KKit                                0x10339083b        kfun:kotlin.coroutines.Continuation#resumeWith(kotlin.Result<1:0>){}-trampoline + 99 
        at 14  KKit                                0x10326acbf        kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 1163 
        at 15  KKit                                0x10339083b        kfun:kotlin.coroutines.Continuation#resumeWith(kotlin.Result<1:0>){}-trampoline + 99 
        at 16  KKit                                0x10347376f        kfun:kotlinx.coroutines.DispatchedTask#run(){} + 1691 
        at 17  KKit                                0x1034a1d77        kfun:kotlinx.coroutines.Runnable#run(){}-trampoline + 91 
        at 18  KKit                                0x1039ef357        kfun:androidx.compose.ui.platform.FlushCoroutineDispatcher.dispatch$lambda$1$lambda$0#internal + 475 
        at 19  KKit                                0x1039f08bf        kfun:androidx.compose.ui.platform.FlushCoroutineDispatcher.$dispatch$lambda$1$lambda$0$FUNCTION_REFERENCE$4.invoke#internal + 75 
        at 20  KKit                                0x1039f09bb        kfun:androidx.compose.ui.platform.FlushCoroutineDispatcher.$dispatch$lambda$1$lambda$0$FUNCTION_REFERENCE$4.$<bridge-UNN>invoke(){}#internal + 71 
        at 21  KKit                                0x10338d167        kfun:kotlin.Function0#invoke(){}1:0-trampoline + 99 
        at 22  KKit                                0x1039eec5f        kfun:androidx.compose.ui.platform.FlushCoroutineDispatcher.performRun#internal + 319 
        at 23  KKit                                0x1039ef4df        kfun:androidx.compose.ui.platform.FlushCoroutineDispatcher.dispatch$lambda$1#internal + 239 
        at 24  KKit                                0x1039f036b        kfun:androidx.compose.ui.platform.FlushCoroutineDispatcher.$dispatch$lambda$1$FUNCTION_REFERENCE$0.invoke#internal + 139 
        at 25  KKit                                0x103390e3f        kfun:kotlin.Function2#invoke(1:0;1:1){}1:2-trampoline + 115 
        at 26  KKit                                0x10326deab        kfun:kotlin.coroutines.intrinsics.object-4.invokeSuspend#internal + 731 
        at 27  KKit                                0x10339075b        kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any?-trampoline + 67 
        at 28  KKit                                0x10326aaa3        kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 623 
        at 29  KKit                                0x10339083b        kfun:kotlin.coroutines.Continuation#resumeWith(kotlin.Result<1:0>){}-trampoline + 99 
        at 30  KKit                                0x10347382b        kfun:kotlinx.coroutines.DispatchedTask#run(){} + 1879 
        at 31  KKit                                0x1034a1d77        kfun:kotlinx.coroutines.Runnable#run(){}-trampoline + 91 
        at 32  KKit                                0x10349d95f        kfun:kotlinx.coroutines.DarwinMainDispatcher.dispatch$lambda$0#internal + 67 
        at 33  KKit                                0x10349dbdb        kfun:kotlinx.coroutines.DarwinMainDispatcher.$dispatch$lambda$0$FUNCTION_REFERENCE$1.invoke#internal + 71 
        at 34  KKit                                0x10349dcab        kfun:kotlinx.coroutines.DarwinMainDispatcher.$dispatch$lambda$0$FUNCTION_REFERENCE$1.$<bridge-UNN>invoke(){}#internal + 71 
        at 35  KKit                                0x10338d167        kfun:kotlin.Function0#invoke(){}1:0-trampoline + 99 
        at 36  KKit                                0x10349ebef        _6f72672e6a6574627261696e732e6b6f746c696e783a6b6f746c696e782d636f726f7574696e65732d636f72652f6f70742f6275696c644167656e742f776f726b2f343465633665383530643563363366302f6b6f746c696e782d636f726f7574696e65732d636f72652f6e617469766544617277696e2f7372632f44697370617463686572732e6b74_knbridge13 + 191 
        at 37  libdispatch.dylib                   0x180170103        _dispatch_call_block_and_release + 23 
        at 38  libdispatch.dylib                   0x180171977        _dispatch_client_callout + 15 
        at 39  libdispatch.dylib                   0x1801807c7        _dispatch_main_queue_drain + 1275 
        at 40  libdispatch.dylib                   0x1801802bb        _dispatch_main_queue_callback_4CF + 39 
        at 41  CoreFoundation                      0x18040e95f        __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 11 
        at 42  CoreFoundation                      0x180409077        __CFRunLoopRun + 1935 
        at 43  CoreFoundation                      0x1804084d3        CFRunLoopRunSpecific + 571 
        at 44  GraphicsServices                    0x18ef2aae3        GSEventRunModal + 159 
        at 45  UIKitCore                           0x1853d0a27        -[UIApplication _run] + 867 
        at 46  UIKitCore                           0x1853d46af        UIApplicationMain + 123 
        at 47  SwiftUI                             0x1ccf100e3        OUTLINED_FUNCTION_65 + 491 
        at 48  SwiftUI                             0x1ccf0ff8b        OUTLINED_FUNCTION_65 + 147 
        at 49  SwiftUI                             0x1ccbcd6d7        OUTLINED_FUNCTION_0 + 91 
        at 50  KKit                                0x1028a4fb7        $s4KKit6iOSAppV5$mainyyFZ + 39 
        at 51  KKit                                0x1028a5067        main + 11 (/Users/telephone/Code/KKit/iosApp/iosApp/iOSApp.swift:<unknown>)
        at 52  dyld                                0x105965543        0x0 + 4388705603 
        at 53  ???                                 0x105a3a0df        0x0 + 4389576927 
        at 54  ???                                 0xe544ffffffffffff 0x0 + -1926133265631019009

Repeatable (but not 100% consistent) error in _platform_strcmp

                    val js = """
                        var response = await kml.requestJson('https://icanhazdadjoke.com/');
                        let parsed = JSON.parse(response);
                        parsed.joke;
                    """.trimIndent()

                    val joke = quickJs {
                        define("kml") {
                            asyncFunction("requestJson") { args ->
                                delay(500)
                                """
                                    {"id":"bxXLeqzP7wc","joke":"Americans can't switch from pounds to kilograms overnight. That would cause mass confusion.","status":200}
                                """.trimIndent()
                            }
                        }
                        evaluate<Any?>(js)?.toString()
                    }

Causes this error. Not every time but frequently:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x0000000188e4aa9c, pid=63005, tid=90371
#
# JRE version: OpenJDK Runtime Environment (17.0.7) (build 17.0.7+0-17.0.7b1000.6-10550314)
# Java VM: OpenJDK 64-Bit Server VM (17.0.7+0-17.0.7b1000.6-10550314, mixed mode, tiered, compressed oops, compressed class ptrs, g1 gc, bsd-aarch64)
# Problematic frame:
# C  [libsystem_platform.dylib+0xa9c]  _platform_strcmp+0xc
#
# No core dump will be written. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
JNI global refs:
JNI global refs: 161, weak refs: 11

JNI global refs memory usage: 2099, weak refs: 833

OOME stack traces (most recent first):
Classloader memory used:
Loader jdk.internal.loader.ClassLoaders$AppClassLoader                                 : 3926K
Loader bootstrap                                                                       : 2694K
Loader jdk.internal.loader.ClassLoaders$PlatformClassLoader                            : 18987B
Loader jdk.internal.reflect.DelegatingClassLoader                                      : 4278B

# An error report file with more information is saved as:
# /Users/dmelton/Developer/doug/kmlauncher/compose-multiplatform/composeApp/hs_err_pid63005.log
#
# If you would like to submit a bug report, please visit:
#   https://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

I removed any actual networking, but simulated by that delay(500)

2 files found with path 'lib/arm64-v8a/libquickjs.so' from inputs:

I try to run my project on my Android phone, but it fails with error:

2 files found with path 'lib/arm64-v8a/libquickjs.so' from inputs:
 - C:\Users\Terry\.gradle\caches\8.8\transforms\d815028faf901d4d4975b34335a30e4f\transformed\quickjs-release\jni\arm64-v8a\libquickjs.so
 - C:\Users\Terry\.gradle\caches\8.8\transforms\c5c5b7415d5b142347150f6c559b9ab3\transformed\zipline-release\jni\arm64-v8a\libquickjs.so
If you are using jniLibs and CMake IMPORTED targets, see
https://developer.android.com/r/tools/jniLibs-vs-imported-targets

Fix leaks

There are some leaks in the JS runtime after evaluation. They won't crash JS_FreeRuntime but need to be fixed.

evaluate cancelled but async jobs continue

Currently, the evaluate can cooperate with coroutine cancel, it will return when the current coroutine is cancelled, this is good.

BUT the async jobs triggered by evaluate including their callbacks will last forever and continue to run after evaluate has been cancelled, this is bad. We can say that the async jobs are leaked.

The root cause is here:

private val coroutineScope = CoroutineScope(jobDispatcher + exceptionHandler)
// ...
internal actual fun invokeAsyncFunction(
        args: Array<Any?>,
        block: suspend (bindingArgs: Array<Any?>) -> Any?,
    ) {
        ensureNotClosed()
        val (resolveHandle, rejectHandle) = promiseHandlesFromArgs(args)
        val job = coroutineScope.launch {
            try {
                val result = block(args.sliceArray(2..<args.size))
                jsMutex.withLock {
                    // Call resolve() on JNI side
                    invokeJsFunction(
                        context = context,
                        globals = globals,
                        handle = resolveHandle,
                        args = arrayOf(result)
                    )
                }
            } catch (e: Throwable) {
                jsMutex.withLock {
                    // Call reject() on JNI side
                    invokeJsFunction(
                        context = context,
                        globals = globals,
                        handle = rejectHandle,
                        args = arrayOf(e)
                    )
                }
            }
            jsMutex.withLock {
                // The job is completed, see what we can do next:
                // - Execute subsequent Promises
                // - Cancel all jobs and fail, if rejected and JS didn't handle it
                do {
                    val executed = executePendingJob(context, globals)
                } while (executed)
            }
        }
        jobsMutex.withLockSync { asyncJobs += job }
        job.invokeOnCompletion {
            jobsMutex.withLockSync { asyncJobs -= job }
        }
    }

invokeAsyncFunction uses a independent CoroutineScope instance to launch async jobs, but this CoroutineScope instance doesn't has the coroutine Job of evaluate's current coroutine as its scope Job, so all async jobs escape out of the "structured concurrency" of the evaluate's current coroutine and won't get cancelled if evaluate is cancelled.

The solution is quite simple: inject the Job of evaluate that triggers async jobs to the coroutine scope of those async jobs, then every async job triggered by an evaluate will listen to this evaluate's Job and get cancelled automatically when this evaluate is cancelled:

internal actual fun invokeAsyncFunction(
        // ###################################################################
        // inject the Job either through parameters or through outside context
        evaluateJob: Job,
        // ###################################################################
        args: Array<Any?>,
        block: suspend (bindingArgs: Array<Any?>) -> Any?,
    ) {
        ensureNotClosed()
        val (resolveHandle, rejectHandle) = promiseHandlesFromArgs(args)
        // ###################################################################
        // use the evaluate Job as the parent Job of this async job
        val job = coroutineScope.launch(evaluateJob) {
        // ###################################################################
            // ............
        }
        jobsMutex.withLockSync { asyncJobs += job }
        job.invokeOnCompletion {
            jobsMutex.withLockSync { asyncJobs -= job }
        }
    }

Now our async jobs will get cancelled automatically, so we can wait for them in evaluate even if we are in cancelled state:

private suspend fun awaitAsyncJobs() {
        jsMutex.withLock {
            do {
                // Execute JS Promises, putting this in while(true) is unnecessary
                // since we have the same loop after every asyncFunction call
                val executed = executePendingJob(context, globals)
            } while (executed)
        }
        // ######################################################################
        // This is important.
        //
        // We have to wait for all async jobs to get completed (even if they are
        // cancelled) before we return, so the code here must continue to run
        // even if we are in cancelled state.
        withContext(NonCancellable){
        // ######################################################################
            while (true) {
                val jobs = jobsMutex.withLock { asyncJobs.filter { it.isActive } }
                if (jobs.isEmpty()) {
                    // No jobs to run
                    break
                }
                jobs.joinAll()
            }
        }
    }

Additionally, even when evaluate is cancelled, the sync JS code must continue to run to push the JS code state to the correct final state so we can make sure no pending jobs are leaked, and JS code is not in a strange middle state that could probably causes strange bugs. All the clean-up steps must run as usual too:

internal actual fun invokeAsyncFunction(
        // ###################################################################
        // inject the Job either through parameters or through outside context
        evaluateJob: Job,
        // ###################################################################
        args: Array<Any?>,
        block: suspend (bindingArgs: Array<Any?>) -> Any?,
    ) {
        ensureNotClosed()
        val (resolveHandle, rejectHandle) = promiseHandlesFromArgs(args)
        // ###################################################################
        // use the evaluate Job as the parent Job of this async job
        val job = coroutineScope.launch(evaluateJob) {
        // ###################################################################
            try {
                val result = block(args.sliceArray(2..<args.size))
                // ###################################################################
                // JS sync code
                withContext(NonCancellable){
                // ###################################################################
                    jsMutex.withLock {
                        // Call resolve() on JNI side
                        invokeJsFunction(
                            context = context,
                            globals = globals,
                            handle = resolveHandle,
                            args = arrayOf(result)
                        )
                    }
                }
            } catch (e: Throwable) {
                // ###################################################################
                // JS sync code
                withContext(NonCancellable) {
                // ###################################################################
                    jsMutex.withLock {
                        // Call reject() on JNI side
                        invokeJsFunction(
                            context = context,
                            globals = globals,
                            handle = rejectHandle,
                            args = arrayOf(e)
                        )
                    }
                }
            }
            // ###################################################################
            // JS sync code
            withContext(NonCancellable) {
            // ###################################################################
                jsMutex.withLock {
                    // The job is completed, see what we can do next:
                    // - Execute subsequent Promises
                    // - Cancel all jobs and fail, if rejected and JS didn't handle it
                    do {
                        val executed = executePendingJob(context, globals)
                    } while (executed)
                }
            }
        }
        jobsMutex.withLockSync { asyncJobs += job }
        job.invokeOnCompletion {
            jobsMutex.withLockSync { asyncJobs -= job }
        }
    }

private suspend fun awaitAsyncJobs() {
        // ###################################################################
        // JS sync code
        withContext(NonCancellable) {
        // ###################################################################
            jsMutex.withLock {
                do {
                    // Execute JS Promises, putting this in while(true) is unnecessary
                    // since we have the same loop after every asyncFunction call
                    val executed = executePendingJob(context, globals)
                } while (executed)
            }
        }
        // ######################################################################
        // This is important.
        //
        // We have to wait for all async jobs to get completed (even if they are
        // cancelled) before we return, so the code here must continue to run
        // even if we are in cancelled state.
        withContext(NonCancellable){
        // ######################################################################
            while (true) {
                val jobs = jobsMutex.withLock { asyncJobs.filter { it.isActive } }
                if (jobs.isEmpty()) {
                    // No jobs to run
                    break
                }
                jobs.joinAll()
            }
        }
    }

private suspend fun evalAndAwait(evalBlock: suspend () -> Any?): Any? {
        ensureNotClosed()
        evalException = null
        // ###################################################################
        // NonCancellable to avoid loadModules() stops at a middle state
        withContext(NonCancellable) {
        // ###################################################################
            loadModules()
        }
        val result = jsResultMutex.withLock {
            jsMutex.withLock {
                // ###################################################################
                // NonCancellable to avoid evalBlock() stops at a middle state
                withContext(NonCancellable) {
                // ###################################################################
                    evalBlock()
                }
            }
            // ###################################################################
            // JS sync code
            withContext(NonCancellable) {
            // ###################################################################
                awaitAsyncJobs()
            }
            // ###################################################################
            // Clean-up run as usual
            withContext(NonCancellable) {
            // ###################################################################
                jsMutex.withLock { getEvaluateResult(context, globals) }
            }
        }
        handleException()
        return result
    }

Android Studio failed to load this gradle project

Exception in thread "main" java.lang.IllegalStateException: Could not find 'libquickjs_ios_aarch64.a' binary in neither of [native/build/static_libs/]
	at org.jetbrains.kotlin.native.interop.gen.LibraryUtilsKt.resolveLibraries(LibraryUtils.kt:31)
	at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.processCLib(main.kt:459)
	at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.processCLibSafe(main.kt:247)
	at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.access$processCLibSafe(main.kt:1)
	at org.jetbrains.kotlin.native.interop.gen.jvm.Interop.interop(main.kt:105)
	at org.jetbrains.kotlin.cli.utilities.InteropCompilerKt.invokeInterop(InteropCompiler.kt:49)
	at org.jetbrains.kotlin.cli.utilities.MainKt.mainImpl(main.kt:23)
	at org.jetbrains.kotlin.cli.utilities.MainKt.main(main.kt:44)
Warning: Failed to generate cinterop for :quickjs:cinteropQuickjsIosArm64: Process 'command '/Users/telephone/Applications/Android Studio Koala Feature Drop 2024.1.2 Canary 8.app/Contents/jbr/Contents/Home/bin/java'' finished with non-zero exit value 1
Failed to generate cinterop for :quickjs:cinteropQuickjsIosArm64: Process 'command '/Users/telephone/Applications/Android Studio Koala Feature Drop 2024.1.2 Canary 8.app/Contents/jbr/Contents/Home/bin/java'' finished with non-zero exit value 1

org.gradle.process.internal.ExecException: Process 'command '/Users/telephone/Applications/Android Studio Koala Feature Drop 2024.1.2 Canary 8.app/Contents/jbr/Contents/Home/bin/java'' finished with non-zero exit value 1
	at org.gradle.process.internal.DefaultExecHandle$ExecResultImpl.assertNormalExitValue(DefaultExecHandle.java:442)
	at org.gradle.process.internal.DefaultJavaExecAction.execute(DefaultJavaExecAction.java:52)
	at org.gradle.process.internal.DefaultExecActionFactory.javaexec(DefaultExecActionFactory.java:195)
	at org.gradle.process.internal.DefaultExecOperations.javaexec(DefaultExecOperations.java:42)
	at org.jetbrains.kotlin.compilerRunner.KotlinToolRunner$GradleExecutionContext$Companion$fromTaskContext$2.invoke(KotlinToolRunner.kt:70)
	at org.jetbrains.kotlin.compilerRunner.KotlinToolRunner$GradleExecutionContext$Companion$fromTaskContext$2.invoke(KotlinToolRunner.kt:70)
	at org.jetbrains.kotlin.compilerRunner.KotlinToolRunner.runViaExec(KotlinToolRunner.kt:175)
	at org.jetbrains.kotlin.compilerRunner.KotlinToolRunner.run(KotlinToolRunner.kt:142)
	at org.jetbrains.kotlin.compilerRunner.KotlinNativeToolRunner.run(nativeToolRunners.kt:153)
	at org.jetbrains.kotlin.compilerRunner.KotlinNativeCInteropRunner$Companion$run$1.invoke(nativeToolRunners.kt:222)
	at org.jetbrains.kotlin.compilerRunner.KotlinNativeCInteropRunner$Companion$run$1.invoke(nativeToolRunners.kt:222)
	at org.jetbrains.kotlin.gradle.targets.native.tasks.IdeaSyncKotlinNativeCInteropRunnerExecutionContext.runWithContext(CInteropRunnerExecutionContextFactory.kt:51)
	at org.jetbrains.kotlin.compilerRunner.KotlinNativeCInteropRunner$Companion.run(nativeToolRunners.kt:222)
	at org.jetbrains.kotlin.gradle.tasks.CInteropProcess$processInterop$1.invoke(KotlinNativeTasks.kt:1216)
	at org.jetbrains.kotlin.gradle.tasks.CInteropProcess$processInterop$1.invoke(KotlinNativeTasks.kt:1208)
	at org.jetbrains.kotlin.compilerRunner.ReportUtilsKt.addBuildMetricsForTaskAction(reportUtils.kt:261)
	at org.jetbrains.kotlin.gradle.tasks.CInteropProcess.processInterop(KotlinNativeTasks.kt:1208)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.base/java.lang.reflect.Method.invoke(Unknown Source)
	at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:125)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
	at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:244)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:229)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:212)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:195)
	at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:162)
	at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:105)
	at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:44)
	at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:59)
	at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:56)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:56)
	at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:44)
	at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:41)
	at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:74)
	at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
	at org.gradle.internal.execution.steps.PreCreateOutputParentsStep.execute(PreCreateOutputParentsStep.java:50)
	at org.gradle.internal.execution.steps.PreCreateOutputParentsStep.execute(PreCreateOutputParentsStep.java:28)
	at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:67)
	at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:37)
	at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:61)
	at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:26)
	at org.gradle.internal.execution.steps.CaptureOutputsAfterExecutionStep.execute(CaptureOutputsAfterExecutionStep.java:67)
	at org.gradle.internal.execution.steps.CaptureOutputsAfterExecutionStep.execute(CaptureOutputsAfterExecutionStep.java:45)
	at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:40)
	at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:29)
	at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:189)
	at org.gradle.internal.execution.steps.BuildCacheStep.lambda$execute$1(BuildCacheStep.java:75)
	at org.gradle.internal.Either$Right.fold(Either.java:175)
	at org.gradle.internal.execution.caching.CachingState.fold(CachingState.java:62)
	at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:73)
	at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:48)
	at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:46)
	at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:35)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:76)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$2(SkipUpToDateStep.java:54)
	at java.base/java.util.Optional.orElseGet(Unknown Source)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:54)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:36)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:37)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:27)
	at org.gradle.internal.execution.steps.ResolveIncrementalCachingStateStep.executeDelegate(ResolveIncrementalCachingStateStep.java:49)
	at org.gradle.internal.execution.steps.ResolveIncrementalCachingStateStep.executeDelegate(ResolveIncrementalCachingStateStep.java:27)
	at org.gradle.internal.execution.steps.AbstractResolveCachingStateStep.execute(AbstractResolveCachingStateStep.java:71)
	at org.gradle.internal.execution.steps.AbstractResolveCachingStateStep.execute(AbstractResolveCachingStateStep.java:39)
	at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:65)
	at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:36)
	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:106)
	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:55)
	at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:64)
	at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:43)
	at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.executeWithNonEmptySources(AbstractSkipEmptyWorkStep.java:125)
	at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:56)
	at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:36)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
	at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:36)
	at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:23)
	at org.gradle.internal.execution.steps.HandleStaleOutputsStep.execute(HandleStaleOutputsStep.java:75)
	at org.gradle.internal.execution.steps.HandleStaleOutputsStep.execute(HandleStaleOutputsStep.java:41)
	at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.lambda$execute$0(AssignMutableWorkspaceStep.java:35)
	at org.gradle.api.internal.tasks.execution.TaskExecution$4.withWorkspace(TaskExecution.java:289)
	at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:31)
	at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:22)
	at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:40)
	at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:23)
	at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.lambda$execute$2(ExecuteWorkBuildOperationFiringStep.java:67)
	at java.base/java.util.Optional.orElseGet(Unknown Source)
	at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:67)
	at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:39)
	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:46)
	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:34)
	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:48)
	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:35)
	at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:61)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:127)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:116)
	at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
	at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:74)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
	at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:42)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:331)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:318)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.lambda$execute$0(DefaultTaskExecutionGraph.java:314)
	at org.gradle.internal.operations.CurrentBuildOperationRef.with(CurrentBuildOperationRef.java:80)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:314)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:303)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:463)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:380)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.base/java.lang.Thread.run(Unknown Source)

Kotlin/Native targets

Progress

  1. mingwX64 and linuxX64: #9
  2. linuxArm64, macosX64, macosArm64, iosX64, iosArm64 and iosSimulatorArm64: #16

suspend functions

Have you tried to use a suspend function via reflection, as shown below? I'm getting an error "parameter count mismatched"

class Http {
    suspend fun fetch(url: String) = "..."
}

quickJs {
    define<Http>("http", Http())

    evaluate<Any?>(
        """
        await http.fetch("https://www.example.com")
        """.trimIndent()
    )
}

Exception handling

Can you add a section to the README on how to properly handle js exceptions?

This does not work if an exception occurs in the js code. An error is thrown but this does not catch it:

                val result = try {
                    wrapper.evaluateJs(jscode)
                }
                catch (e: Exception) {
                    Logger.e { "Javascript error: $e" }
                    null
                }

throws this error:

Exception in thread "DefaultDispatcher-worker-10" java.lang.RuntimeException: Exception while trying to handle coroutine exception
	at kotlinx.coroutines.CoroutineExceptionHandlerKt.handlerException(CoroutineExceptionHandler.kt:33)
	at kotlinx.coroutines.internal.CoroutineExceptionHandlerImpl_commonKt.handleUncaughtCoroutineException(CoroutineExceptionHandlerImpl.common.kt:38)
	at kotlinx.coroutines.CoroutineExceptionHandlerKt.handleCoroutineException(CoroutineExceptionHandler.kt:28)
	at kotlinx.coroutines.StandaloneCoroutine.handleJobException(Builders.common.kt:190)
	at kotlinx.coroutines.JobSupport.finalizeFinishingState(JobSupport.kt:228)
	at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath(JobSupport.kt:907)
	at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:864)
	at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:829)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:97)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
	at kotlinx.coroutines.internal.ScopeCoroutine.afterResume(Scopes.kt:28)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:99)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:102)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:585)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:802)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:706)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:693)
	Suppressed: java.lang.Error: Error: test
    at dictionaryapi (main.js:5)
    at <anonymous> (main.js:12)
    at search (main.js)
    at <eval> (main.js:2)

		at com.dokar.quickjs.QuickJs.getEvaluateResult(Native Method)
		at com.dokar.quickjs.QuickJs.evalAndAwait(QuickJs.jni.kt:220)
		at com.dokar.quickjs.QuickJs.evaluateInternal(QuickJs.jni.kt:210)
...

This js code works as expected when I define my own host.log function, but it would be nicer if I could catch the error in kotlin:

                    try {
                      doStuff();
                    }
                    catch (err) {
                      host.log("caught js error: ", err);
                    }

thanks for all your quick updates!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.