diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index 59ae2451b7b..3c2e3919b8b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -163,6 +163,9 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC get() = context.ir.symbols.coroutineContextGetter val jsGetContinuation = getInternalFunction("getContinuation") + val jsInvokeSuspendFunction = getInternalWithoutPackage("kotlin.coroutines.intrinsics.invokeSuspendFunction") + val jsInvokeSuspendFunctionWithReceiver = getInternalWithoutPackage("kotlin.coroutines.intrinsics.invokeSuspendFunctionWithReceiver") + val jsInvokeSuspendFunctionWithReceiverAndParam = getInternalWithoutPackage("kotlin.coroutines.intrinsics.invokeSuspendFunctionWithReceiverAndParam") val jsGetKClass = getInternalWithoutPackage("getKClass") val jsGetKClassFromExpression = getInternalWithoutPackage("getKClassFromExpression") diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt index a728fe027d2..1e3017e5136 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt @@ -8,12 +8,14 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext import org.jetbrains.kotlin.ir.backend.js.utils.Namer +import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope import org.jetbrains.kotlin.ir.backend.js.utils.getClassRef import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrFunctionReference +import org.jetbrains.kotlin.ir.expressions.IrGetField import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.types.classifierOrFail @@ -226,6 +228,61 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) { add(intrinsics.jsUndefined) { _, _ -> JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1)) } + + val suspendInvokeTransform: (IrCall, JsGenerationContext) -> JsExpression = { call, context: JsGenerationContext -> + // Because it is intrinsic, we know everything about this function + // There is callable reference as extension receiver + val invokeFun = (call.extensionReceiver as IrCall) + // Single parameter is field + .getValueArgument(0) + .let { it as IrGetField } + .symbol + .owner + // It should be SuspendFunctionN + .type + .classifierOrFail + .owner + .let { it as IrClass } + .declarations + .filterIsInstance() + .single { it.name.asString() == "invoke" } + + val jsInvokeFunName = context.getNameForMemberFunction(invokeFun) + + val jsExtensionReceiver = call.extensionReceiver?.accept(IrElementToJsExpressionTransformer(), context)!! as JsInvocation + val args = translateCallArguments(call, context) + val jsArgsForClosureFun = args.mapIndexed { index, _ -> JsName("${"$"}p$index") } + + // Function to make closure for invoke to avoid losing this + jsExtensionReceiver.arguments[0] = JsInvocation( + JsNameRef( + Namer.BIND_FUNCTION, + JsFunction( + emptyScope, + JsBlock( + JsReturn( + JsInvocation( + JsNameRef( + jsInvokeFunName, + jsExtensionReceiver.arguments[0] + ), + jsArgsForClosureFun.map { it.makeRef() } + ) + ) + ), + "Function to make closure for invoke to avoid losing this" + ).also { func -> + func.parameters.addAll(jsArgsForClosureFun.map { JsParameter(it) }) + } + ), JsThisRef() + ) + + JsInvocation(jsExtensionReceiver, args) + } + + add(intrinsics.jsInvokeSuspendFunction, suspendInvokeTransform) + add(intrinsics.jsInvokeSuspendFunctionWithReceiver, suspendInvokeTransform) + add(intrinsics.jsInvokeSuspendFunctionWithReceiverAndParam, suspendInvokeTransform) } } diff --git a/libraries/stdlib/js-ir/src/kotlin/coroutines_13/IntrinsicsJs.kt b/libraries/stdlib/js-ir/src/kotlin/coroutines_13/IntrinsicsJs.kt index 0650122671f..fe41aa79d52 100644 --- a/libraries/stdlib/js-ir/src/kotlin/coroutines_13/IntrinsicsJs.kt +++ b/libraries/stdlib/js-ir/src/kotlin/coroutines_13/IntrinsicsJs.kt @@ -10,6 +10,30 @@ package kotlin.coroutines.intrinsics import kotlin.coroutines.* import kotlin.internal.InlineOnly +@SinceKotlin("1.3") +public fun (suspend () -> T).invokeSuspendFunction( + completion: Continuation +): Any? { + throw NotImplementedError("It is intrinsic method") +} + +@SinceKotlin("1.3") +public fun (suspend R.() -> T).invokeSuspendFunctionWithReceiver( + receiver: R, + completion: Continuation +): Any? { + throw NotImplementedError("It is intrinsic method") +} + +@SinceKotlin("1.3") +public fun (suspend R.(P) -> T).invokeSuspendFunctionWithReceiverAndParam( + receiver: R, + param: P, + completion: Continuation +): Any? { + throw NotImplementedError("It is intrinsic method") +} + /** * Starts unintercepted coroutine without receiver and with result type [T] and executes it until its first suspension. * Returns the result of the coroutine or throws its exception if it does not suspend or [COROUTINE_SUSPENDED] if it suspends. @@ -29,7 +53,7 @@ public actual inline fun (suspend () -> T).startCoroutineUninterceptedOrRetu ): Any? { val a = this.asDynamic() return if (jsTypeOf(a) == "function") a(completion) - else ::invoke.asDynamic()(completion) + else ::invoke.invokeSuspendFunction(completion) } /** @@ -52,7 +76,7 @@ public actual inline fun (suspend R.() -> T).startCoroutineUninterceptedO ): Any? { val a = this.asDynamic() return if (jsTypeOf(a) == "function") a(receiver, completion) - else ::invoke.asDynamic()(receiver, completion) + else ::invoke.invokeSuspendFunctionWithReceiver(receiver, completion) } @InlineOnly @@ -63,7 +87,7 @@ internal actual inline fun (suspend R.(P) -> T).startCoroutineUninterc ): Any? { val a = this.asDynamic() return if (jsTypeOf(a) == "function") a(receiver, param, completion) - else ::invoke.asDynamic()(receiver, param, completion) + else ::invoke.invokeSuspendFunctionWithReceiverAndParam(receiver, param, completion) } /** @@ -89,7 +113,7 @@ public actual fun (suspend () -> T).createCoroutineUnintercepted( createCoroutineFromSuspendFunction(completion) { val a = this.asDynamic() if (jsTypeOf(a) == "function") a(completion) - else ::invoke.asDynamic()(completion) + else ::invoke.invokeSuspendFunction(completion) } /** @@ -116,7 +140,7 @@ public actual fun (suspend R.() -> T).createCoroutineUnintercepted( createCoroutineFromSuspendFunction(completion) { val a = this.asDynamic() if (jsTypeOf(a) == "function") a(receiver, completion) - else ::invoke.asDynamic()(receiver, completion) + else ::invoke.invokeSuspendFunctionWithReceiver(receiver, completion) } /**