From 1af01d0ecb478ad2f388071dbe0fc962202a7cef Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 22 Dec 2016 16:26:01 +0300 Subject: [PATCH] JS: make call to suspend lambda to resume it immediately. See KT-15379 --- .../suspendFunctionTypeCall/manyParameters.kt | 3 +-- .../suspendFunctionTypeCall/simple.kt | 1 - .../js/ast/metadata/metadataProperties.kt | 2 ++ .../coroutine/CoroutineFunctionTransformer.kt | 18 ++++++++++++++---- js/js.libraries/src/core/coroutines.kt | 8 ++++---- .../semantics/JsCodegenBoxTestGenerated.java | 16 ++-------------- .../expression/LiteralFunctionTranslator.kt | 15 ++++++++++----- .../utils/FunctionBodyTranslator.java | 2 +- .../js/translate/utils/TranslationUtils.java | 7 +++++++ 9 files changed, 41 insertions(+), 31 deletions(-) diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.kt index 91736517737..de436c53998 100644 --- a/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.kt +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.kt @@ -1,6 +1,5 @@ // WITH_RUNTIME // WITH_COROUTINES -// IGNORE_BACKEND: JS import kotlin.coroutines.* suspend fun suspendHere(v: String): String = CoroutineIntrinsics.suspendCoroutineOrReturn { x -> @@ -25,7 +24,7 @@ fun box(): String { } } - if (result != "1.0#56#55#abc") return "fail: $result" + if (result != "1.0#56#55#abc" && result != "1#56#55#abc") return "fail: $result" return final } diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.kt index acd7643e6ae..10c2b655e73 100644 --- a/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.kt +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.kt @@ -1,6 +1,5 @@ // WITH_RUNTIME // WITH_COROUTINES -// IGNORE_BACKEND: JS import kotlin.coroutines.* suspend fun suspendHere(v: String): String = CoroutineIntrinsics.suspendCoroutineOrReturn { x -> diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt index dd7e46d5ab9..785914022d7 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt @@ -101,11 +101,13 @@ var JsFunction.coroutineMetadata: CoroutineMetadata? by MetadataProperty(default class CoroutineMetadata( val doResumeName: JsName, + val resumeName: JsName, val stateName: JsName, val exceptionStateName: JsName, val finallyPathName: JsName, val resultName: JsName, val exceptionName: JsName, + val facadeName: JsName, val baseClassRef: JsExpression, val suspendObjectRef: JsExpression, val hasController: Boolean diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineFunctionTransformer.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineFunctionTransformer.kt index dfffad0c13a..da0b0289860 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineFunctionTransformer.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineFunctionTransformer.kt @@ -46,7 +46,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f generateDoResume(coroutineBlocks, context, additionalStatements) generateContinuationConstructor(context, additionalStatements, globalCatchBlockIndex) - generateCoroutineInstantiation() + generateCoroutineInstantiation(context) return additionalStatements } @@ -129,7 +129,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f this += coroutineBody } - val resumeName = function.coroutineMetadata!!.doResumeName + val resumeName = context.metadata.doResumeName statements.apply { assignToPrototype(resumeName, resumeFunction) } @@ -137,7 +137,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f FunctionPostProcessor(resumeFunction).apply() } - private fun generateCoroutineInstantiation() { + private fun generateCoroutineInstantiation(context: CoroutineTransformationContext) { val instantiation = JsNew(className.makeRef()) instantiation.arguments += function.parameters.map { it.name.makeRef() } if (innerFunction != null) { @@ -151,9 +151,19 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f val interceptorParamName = functionWithBody.scope.declareFreshName("interceptor") functionWithBody.parameters += JsParameter(interceptorParamName) + val suspendedName = functionWithBody.scope.declareFreshName("suspended") + functionWithBody.parameters += JsParameter(suspendedName) + instantiation.arguments += interceptorParamName.makeRef() - functionWithBody.body.statements += JsReturn(instantiation) + val instanceName = functionWithBody.scope.declareFreshName("instance") + functionWithBody.body.statements += JsAstUtils.newVar(instanceName, JsNameRef(context.metadata.facadeName, instantiation)) + + val invokeResume = JsInvocation(JsNameRef(context.metadata.resumeName, instanceName.makeRef()), JsLiteral.NULL).makeStmt() + val returnResult = JsReturn(JsNameRef(context.metadata.resultName, instanceName.makeRef())) + + functionWithBody.body.statements += JsIf( + suspendedName.makeRef(), JsReturn(instanceName.makeRef()), JsBlock(invokeResume, returnResult)) } private fun generateCoroutineBody( diff --git a/js/js.libraries/src/core/coroutines.kt b/js/js.libraries/src/core/coroutines.kt index 07551bd5066..b723267dc23 100644 --- a/js/js.libraries/src/core/coroutines.kt +++ b/js/js.libraries/src/core/coroutines.kt @@ -29,7 +29,7 @@ public fun (suspend R.() -> T).createCoroutine( receiver: R, completion: Continuation, dispatcher: ContinuationDispatcher? = null -): Continuation = this.asDynamic().call(receiver, withDispatcher(completion, dispatcher)).facade +): Continuation = this.asDynamic().call(receiver, withDispatcher(completion, dispatcher), true).facade /** * Starts coroutine with receiver type [R] and result type [T]. @@ -43,7 +43,7 @@ public fun (suspend R.() -> T).startCoroutine( completion: Continuation, dispatcher: ContinuationDispatcher? = null ) { - createCoroutine(receiver, completion, dispatcher).resume(Unit) + this.asDynamic().call(receiver, withDispatcher(completion, dispatcher)) } /** @@ -57,7 +57,7 @@ public fun (suspend R.() -> T).startCoroutine( public fun (suspend () -> T).createCoroutine( completion: Continuation, dispatcher: ContinuationDispatcher? = null -): Continuation = this.asDynamic()(withDispatcher(completion, dispatcher)).facade +): Continuation = this.asDynamic()(withDispatcher(completion, dispatcher), true).facade /** * Starts coroutine without receiver and with result type [T]. @@ -70,7 +70,7 @@ public fun (suspend () -> T).startCoroutine( completion: Continuation, dispatcher: ContinuationDispatcher? = null ) { - createCoroutine(completion, dispatcher).resume(Unit) + this.asDynamic()(withDispatcher(completion, dispatcher)) } /** diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 7c774e023e7..b7bd89d40cb 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -5813,25 +5813,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("manyParameters.kt") public void testManyParameters() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("simple.kt") public void testSimple() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt index b6193365b86..6a543647954 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt @@ -120,15 +120,20 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato val coroutineBaseClassRef = ReferenceTranslator.translateAsTypeReference(TranslationUtils.getCoroutineBaseClass(context), context) + fun getCoroutinePropertyName(id: String) = + context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, id)) + coroutineMetadata = CoroutineMetadata( doResumeName = context.getNameForDescriptor(TranslationUtils.getCoroutineDoResumeFunction(context)), + resumeName = context.getNameForDescriptor(TranslationUtils.getCoroutineResumeFunction(context)), suspendObjectRef = ReferenceTranslator.translateAsValueReference(suspendPropertyDescriptor, context()), baseClassRef = coroutineBaseClassRef, - stateName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "state")), - exceptionStateName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "exceptionState")), - finallyPathName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "finallyPath")), - resultName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "result")), - exceptionName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "exception")), + stateName = getCoroutinePropertyName("state"), + exceptionStateName = getCoroutinePropertyName("exceptionState"), + finallyPathName = getCoroutinePropertyName("finallyPath"), + resultName = getCoroutinePropertyName("result"), + exceptionName = getCoroutinePropertyName("exception"), + facadeName = getCoroutinePropertyName("facade"), hasController = continuationType.isBuiltinExtensionFunctionalType ) coroutineType = continuationType diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java index ee8c85bd89d..c33539f47de 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java @@ -134,7 +134,7 @@ public final class FunctionBodyTranslator extends AbstractTranslator { private boolean mustAddReturnToGeneratedFunctionBody() { KotlinType functionReturnType = descriptor.getReturnType(); assert functionReturnType != null : "Function return typed type must be resolved."; - return (!declaration.hasBlockBody()) && (!KotlinBuiltIns.isUnit(functionReturnType)); + return (!declaration.hasBlockBody()) && !(KotlinBuiltIns.isUnit(functionReturnType) && !descriptor.isSuspend()); } @NotNull diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java index 4ee88c508dd..de80c603092 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java @@ -358,4 +358,11 @@ public final class TranslationUtils { .getContributedFunctions(Name.identifier("doResume"), NoLookupLocation.FROM_DESERIALIZATION) .iterator().next(); } + + @NotNull + public static FunctionDescriptor getCoroutineResumeFunction(@NotNull TranslationContext context) { + return getCoroutineBaseClass(context).getUnsubstitutedMemberScope() + .getContributedFunctions(Name.identifier("resume"), NoLookupLocation.FROM_DESERIALIZATION) + .iterator().next(); + } }