All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nativeMain.com.dokar.quickjs.bridge.async.kt Maven / Gradle / Ivy

The newest version!
package com.dokar.quickjs.bridge

import com.dokar.quickjs.QuickJsException
import com.dokar.quickjs.qjsError
import com.dokar.quickjs.util.isPromise
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.CPointerVar
import kotlinx.cinterop.CValue
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.alloc
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.ptr
import kotlinx.cinterop.value
import quickjs.JSContext
import quickjs.JSPromiseStateEnum
import quickjs.JSRuntime
import quickjs.JSValue
import quickjs.JS_ExecutePendingJob
import quickjs.JS_FreeValue
import quickjs.JS_GetException
import quickjs.JS_GetPropertyStr
import quickjs.JS_IsError
import quickjs.JS_IsException
import quickjs.JS_IsNull
import quickjs.JS_PromiseResult
import quickjs.JS_PromiseState
import quickjs.JS_UpdateStackTop

@OptIn(ExperimentalForeignApi::class)
internal value class JsPromise(
    private val value: CValue
) {
    fun result(context: CPointer): Any? {
        val ctxException = JS_GetException(context)
        if (JS_IsNull(ctxException) != 1) {
            ctxException.use(context) {
                if (JS_IsError(context, this) == 1) {
                    throw jsErrorToKtError(context, this)
                } else {
                    throw QuickJsException(toKtString(context))
                }
            }
        }

        return when (val state = JS_PromiseState(context, value)) {
            JSPromiseStateEnum.JS_PROMISE_FULFILLED -> {
                JS_PromiseResult(context, value).use(context) {
                    val result = JS_GetPropertyStr(context, this, "value")
                    if (result.isPromise(context)) {
                        val stateText = when (val retState = JS_PromiseState(context, result)) {
                            JSPromiseStateEnum.JS_PROMISE_PENDING -> STATE_PENDING
                            JSPromiseStateEnum.JS_PROMISE_FULFILLED -> STATE_FULFILLED
                            JSPromiseStateEnum.JS_PROMISE_REJECTED -> STATE_REJECTED
                            else -> qjsError("Unknown promise state: $retState")
                        }
                        JS_FreeValue(context, result)
                        stateText
                    } else if (JS_IsException(result) != 1) {
                        result.use(context) { toKtValue(context) }
                    } else {
                        // Is it safe to ignore the exception?
                        // This happens when executing a compiled module.
                        null
                    }
                }
            }

            JSPromiseStateEnum.JS_PROMISE_REJECTED -> {
                val result = JS_PromiseResult(context, value).use(context) { toKtValue(context) }
                if (result is Throwable) {
                    throw result
                } else {
                    throw QuickJsException(result?.toString())
                }
            }

            JSPromiseStateEnum.JS_PROMISE_PENDING -> STATE_PENDING

            else -> qjsError("Unknown promise state: $state")
        }
    }

    fun free(context: CPointer) {
        JS_FreeValue(context, value)
    }

    companion object {
        private const val STATE_FULFILLED = """Promise { : "fulfilled" }"""
        private const val STATE_REJECTED = """Promise { : "rejected" }"""
        private const val STATE_PENDING = """Promise { : "pending" }"""
    }
}

internal sealed interface ExecuteJobResult {
    data object Success : ExecuteJobResult
    data object NoJobs : ExecuteJobResult
    class Failure(val error: Throwable) : ExecuteJobResult
}

@OptIn(ExperimentalForeignApi::class)
internal fun executePendingJob(runtime: CPointer): ExecuteJobResult = memScoped {
    JS_UpdateStackTop(runtime)
    val ctx = alloc>()
    val ret = JS_ExecutePendingJob(runtime, ctx.ptr)
    if (ret < 0) {
        val context = ctx.value ?: return@memScoped ExecuteJobResult.Failure(
            QuickJsException("Unknown execute error.")
        )
        val jsError = JS_GetException(context)
        val error = jsError.use(context) { jsErrorToKtError(context, this) }
        return ExecuteJobResult.Failure(error)
    } else if (ret == 1) {
        return ExecuteJobResult.Success
    } else {
        return ExecuteJobResult.NoJobs
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy