diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt index f2e79e21177..8edcc3cf597 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt @@ -49,7 +49,7 @@ fun IrWhen.toJsNode( tr: BaseIrElementToJsNodeTransformer, context: JsGenerationContext, node: (JsExpression, T, T?) -> T, - implicitElse: T? = null + implicitElse: T? = null, ): T? = branches.foldRight(implicitElse) { br, n -> val body = br.result.accept(tr, context) @@ -142,15 +142,21 @@ private fun isFunctionTypeInvoke(receiver: JsExpression?, call: IrCall): Boolean if (call.origin === JsStatementOrigins.EXPLICIT_INVOKE) return false - return simpleFunction.name == OperatorNameConventions.INVOKE - && receiverType.isFunctionTypeOrSubtype() - && (!receiverType.isSuspendFunctionTypeOrSubtype() || receiverType.isSuspendFunction()) + val isInvokeFun = simpleFunction.name == OperatorNameConventions.INVOKE + if (!isInvokeFun) return false + + val isNonSuspendFunction = receiverType.isFunctionTypeOrSubtype() && !receiverType.isSuspendFunctionTypeOrSubtype() + val isSuspendFunction = receiverType.isSuspendFunction() + + // Dce can eliminate Function parent of SuspendFunctionN + // So we need to check them separately + return isNonSuspendFunction || isSuspendFunction } fun translateCall( expression: IrCall, context: JsGenerationContext, - transformer: IrElementToJsExpressionTransformer + transformer: IrElementToJsExpressionTransformer, ): JsExpression { val function = expression.symbol.owner.realOverrideTarget val currentDispatchReceiver = context.currentFunction?.parentClassOrNull @@ -387,7 +393,7 @@ fun translateCallArguments( expression: IrMemberAccessExpression, context: JsGenerationContext, transformer: IrElementToJsExpressionTransformer, - allowDropTailVoids: Boolean = true + allowDropTailVoids: Boolean = true, ): List { val size = expression.valueArgumentsCount @@ -450,7 +456,7 @@ object JsAstUtils { fun newJsIf( ifExpression: JsExpression, thenStatement: JsStatement, - elseStatement: JsStatement? = null + elseStatement: JsStatement? = null, ): JsIf { return JsIf(ifExpression, deBlockIfPossible(thenStatement), elseStatement?.let { deBlockIfPossible(it) }) } @@ -543,7 +549,7 @@ internal fun T.withSource( node: IrElement, context: JsGenerationContext, useNameOf: IrDeclarationWithName? = null, - container: IrDeclaration? = null + container: IrDeclaration? = null, ): T { addSourceInfoIfNeed(node, context, useNameOf, container) return this @@ -554,7 +560,7 @@ private inline fun T.addSourceInfoIfNeed( node: IrElement, context: JsGenerationContext, useNameOf: IrDeclarationWithName?, - container: IrDeclaration? + container: IrDeclaration?, ) { val sourceMapsInfo = context.staticContext.backendContext.sourceMapsInfo ?: return val originalName = useNameOf?.originalNameForUseInSourceMap(sourceMapsInfo.namesPolicy) @@ -588,7 +594,7 @@ private inline fun T.addSourceInfoIfNeed( private fun JsLocation.withEmbeddedSource( @Suppress("UNUSED_PARAMETER") - context: JsGenerationContext + context: JsGenerationContext, ): JsLocationWithEmbeddedSource { // FIXME: fileIdentity is used to distinguish between different files with the same paths. // For now we use the file's path to read its content, which makes fileIdentity useless. diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java index 8d247819ab3..7f9086e591f 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java @@ -956,6 +956,12 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest { runTest("js/js.translator/testData/box/coroutines/suspendFunctionalInterface.kt"); } + @Test + @TestMetadata("suspendInvokeWithSuspendKlassRef.kt") + public void testSuspendInvokeWithSuspendKlassRef() throws Exception { + runTest("js/js.translator/testData/box/coroutines/suspendInvokeWithSuspendKlassRef.kt"); + } + @Test @TestMetadata("suspendMethodWithSuperCall.kt") public void testSuspendMethodWithSuperCall() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsES6BoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsES6BoxTestGenerated.java index a83817ca06f..f95b1af4b88 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsES6BoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsES6BoxTestGenerated.java @@ -956,6 +956,12 @@ public class FirJsES6BoxTestGenerated extends AbstractFirJsES6BoxTest { runTest("js/js.translator/testData/box/coroutines/suspendFunctionalInterface.kt"); } + @Test + @TestMetadata("suspendInvokeWithSuspendKlassRef.kt") + public void testSuspendInvokeWithSuspendKlassRef() throws Exception { + runTest("js/js.translator/testData/box/coroutines/suspendInvokeWithSuspendKlassRef.kt"); + } + @Test @TestMetadata("suspendMethodWithSuperCall.kt") public void testSuspendMethodWithSuperCall() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsES6TestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsES6TestGenerated.java index 701ffb21eb0..6174d78bebb 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsES6TestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsES6TestGenerated.java @@ -956,6 +956,12 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test { runTest("js/js.translator/testData/box/coroutines/suspendFunctionalInterface.kt"); } + @Test + @TestMetadata("suspendInvokeWithSuspendKlassRef.kt") + public void testSuspendInvokeWithSuspendKlassRef() throws Exception { + runTest("js/js.translator/testData/box/coroutines/suspendInvokeWithSuspendKlassRef.kt"); + } + @Test @TestMetadata("suspendMethodWithSuperCall.kt") public void testSuspendMethodWithSuperCall() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java index 79cd187de06..dc2a78e2a39 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java @@ -956,6 +956,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/coroutines/suspendFunctionalInterface.kt"); } + @Test + @TestMetadata("suspendInvokeWithSuspendKlassRef.kt") + public void testSuspendInvokeWithSuspendKlassRef() throws Exception { + runTest("js/js.translator/testData/box/coroutines/suspendInvokeWithSuspendKlassRef.kt"); + } + @Test @TestMetadata("suspendMethodWithSuperCall.kt") public void testSuspendMethodWithSuperCall() throws Exception { diff --git a/js/js.translator/testData/box/coroutines/suspendInvokeWithSuspendKlassRef.kt b/js/js.translator/testData/box/coroutines/suspendInvokeWithSuspendKlassRef.kt new file mode 100644 index 00000000000..e1908d2028f --- /dev/null +++ b/js/js.translator/testData/box/coroutines/suspendInvokeWithSuspendKlassRef.kt @@ -0,0 +1,85 @@ +// TARGET_BACKEND: JS_IR +// ONLY_IR_DCE + +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +private var stopped = false + +fun build(c: suspend () -> Unit) { + c.startCoroutine(object : Continuation { + override fun resumeWith(x: Result) { + stopped = true + } + + override val context = EmptyCoroutineContext + }) +} + +inline fun klassToString() = T::class.toString() + +typealias SusFun0 = suspend () -> Unit +typealias SusFun1 = suspend (String) -> Unit +typealias SusFun2 = suspend (String, String) -> Unit + +val SusFun0Prop = klassToString() +val SusFun1Prop = klassToString() +val SusFun2Prop = klassToString() + +class Foo { + suspend fun susFun0( + parse: suspend () -> String + ): String { + return "0" + parse() + } + + suspend fun susFun1( + parse: suspend (String) -> String + ): String { + return "1" + parse("a") + } + + suspend fun susFun2( + parse: suspend (String, String) -> String + ): String { + return "2" + parse("a", "b") + } +} + + + +fun box(): String { + SusFun0Prop + SusFun1Prop + SusFun2Prop + + var log = "" + val foo = Foo() + + build { + val foo0 = foo.susFun0 { + "0" + } + + log += foo0 + + val foo1 = foo.susFun1 { one -> + one + "1" + } + + log += foo1 + + val foo2 = foo.susFun2 { one, two -> + one + two + "2" + } + + log += foo2 + } + + while (!stopped) { + } + + if (log != "001a12ab2") return "fail: $log" + + return "OK" +} \ No newline at end of file