JS: make call to suspend lambda to resume it immediately. See KT-15379

This commit is contained in:
Alexey Andreev
2016-12-22 16:26:01 +03:00
parent 79ba6a57d6
commit 1af01d0ecb
9 changed files with 41 additions and 31 deletions
@@ -1,6 +1,5 @@
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
// IGNORE_BACKEND: JS
import kotlin.coroutines.* import kotlin.coroutines.*
suspend fun suspendHere(v: String): String = CoroutineIntrinsics.suspendCoroutineOrReturn { x -> 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 return final
} }
@@ -1,6 +1,5 @@
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
// IGNORE_BACKEND: JS
import kotlin.coroutines.* import kotlin.coroutines.*
suspend fun suspendHere(v: String): String = CoroutineIntrinsics.suspendCoroutineOrReturn { x -> suspend fun suspendHere(v: String): String = CoroutineIntrinsics.suspendCoroutineOrReturn { x ->
@@ -101,11 +101,13 @@ var JsFunction.coroutineMetadata: CoroutineMetadata? by MetadataProperty(default
class CoroutineMetadata( class CoroutineMetadata(
val doResumeName: JsName, val doResumeName: JsName,
val resumeName: JsName,
val stateName: JsName, val stateName: JsName,
val exceptionStateName: JsName, val exceptionStateName: JsName,
val finallyPathName: JsName, val finallyPathName: JsName,
val resultName: JsName, val resultName: JsName,
val exceptionName: JsName, val exceptionName: JsName,
val facadeName: JsName,
val baseClassRef: JsExpression, val baseClassRef: JsExpression,
val suspendObjectRef: JsExpression, val suspendObjectRef: JsExpression,
val hasController: Boolean val hasController: Boolean
@@ -46,7 +46,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
generateDoResume(coroutineBlocks, context, additionalStatements) generateDoResume(coroutineBlocks, context, additionalStatements)
generateContinuationConstructor(context, additionalStatements, globalCatchBlockIndex) generateContinuationConstructor(context, additionalStatements, globalCatchBlockIndex)
generateCoroutineInstantiation() generateCoroutineInstantiation(context)
return additionalStatements return additionalStatements
} }
@@ -129,7 +129,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
this += coroutineBody this += coroutineBody
} }
val resumeName = function.coroutineMetadata!!.doResumeName val resumeName = context.metadata.doResumeName
statements.apply { statements.apply {
assignToPrototype(resumeName, resumeFunction) assignToPrototype(resumeName, resumeFunction)
} }
@@ -137,7 +137,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
FunctionPostProcessor(resumeFunction).apply() FunctionPostProcessor(resumeFunction).apply()
} }
private fun generateCoroutineInstantiation() { private fun generateCoroutineInstantiation(context: CoroutineTransformationContext) {
val instantiation = JsNew(className.makeRef()) val instantiation = JsNew(className.makeRef())
instantiation.arguments += function.parameters.map { it.name.makeRef() } instantiation.arguments += function.parameters.map { it.name.makeRef() }
if (innerFunction != null) { if (innerFunction != null) {
@@ -151,9 +151,19 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
val interceptorParamName = functionWithBody.scope.declareFreshName("interceptor") val interceptorParamName = functionWithBody.scope.declareFreshName("interceptor")
functionWithBody.parameters += JsParameter(interceptorParamName) functionWithBody.parameters += JsParameter(interceptorParamName)
val suspendedName = functionWithBody.scope.declareFreshName("suspended")
functionWithBody.parameters += JsParameter(suspendedName)
instantiation.arguments += interceptorParamName.makeRef() 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( private fun generateCoroutineBody(
+4 -4
View File
@@ -29,7 +29,7 @@ public fun <R, T> (suspend R.() -> T).createCoroutine(
receiver: R, receiver: R,
completion: Continuation<T>, completion: Continuation<T>,
dispatcher: ContinuationDispatcher? = null dispatcher: ContinuationDispatcher? = null
): Continuation<Unit> = this.asDynamic().call(receiver, withDispatcher(completion, dispatcher)).facade ): Continuation<Unit> = this.asDynamic().call(receiver, withDispatcher(completion, dispatcher), true).facade
/** /**
* Starts coroutine with receiver type [R] and result type [T]. * Starts coroutine with receiver type [R] and result type [T].
@@ -43,7 +43,7 @@ public fun <R, T> (suspend R.() -> T).startCoroutine(
completion: Continuation<T>, completion: Continuation<T>,
dispatcher: ContinuationDispatcher? = null dispatcher: ContinuationDispatcher? = null
) { ) {
createCoroutine(receiver, completion, dispatcher).resume(Unit) this.asDynamic().call(receiver, withDispatcher(completion, dispatcher))
} }
/** /**
@@ -57,7 +57,7 @@ public fun <R, T> (suspend R.() -> T).startCoroutine(
public fun <T> (suspend () -> T).createCoroutine( public fun <T> (suspend () -> T).createCoroutine(
completion: Continuation<T>, completion: Continuation<T>,
dispatcher: ContinuationDispatcher? = null dispatcher: ContinuationDispatcher? = null
): Continuation<Unit> = this.asDynamic()(withDispatcher(completion, dispatcher)).facade ): Continuation<Unit> = this.asDynamic()(withDispatcher(completion, dispatcher), true).facade
/** /**
* Starts coroutine without receiver and with result type [T]. * Starts coroutine without receiver and with result type [T].
@@ -70,7 +70,7 @@ public fun <T> (suspend () -> T).startCoroutine(
completion: Continuation<T>, completion: Continuation<T>,
dispatcher: ContinuationDispatcher? = null dispatcher: ContinuationDispatcher? = null
) { ) {
createCoroutine(completion, dispatcher).resume(Unit) this.asDynamic()(withDispatcher(completion, dispatcher))
} }
/** /**
@@ -5813,25 +5813,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("manyParameters.kt") @TestMetadata("manyParameters.kt")
public void testManyParameters() throws Exception { public void testManyParameters() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("simple.kt") @TestMetadata("simple.kt")
public void testSimple() throws Exception { public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
} }
@@ -120,15 +120,20 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
val coroutineBaseClassRef = ReferenceTranslator.translateAsTypeReference(TranslationUtils.getCoroutineBaseClass(context), context) val coroutineBaseClassRef = ReferenceTranslator.translateAsTypeReference(TranslationUtils.getCoroutineBaseClass(context), context)
fun getCoroutinePropertyName(id: String) =
context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, id))
coroutineMetadata = CoroutineMetadata( coroutineMetadata = CoroutineMetadata(
doResumeName = context.getNameForDescriptor(TranslationUtils.getCoroutineDoResumeFunction(context)), doResumeName = context.getNameForDescriptor(TranslationUtils.getCoroutineDoResumeFunction(context)),
resumeName = context.getNameForDescriptor(TranslationUtils.getCoroutineResumeFunction(context)),
suspendObjectRef = ReferenceTranslator.translateAsValueReference(suspendPropertyDescriptor, context()), suspendObjectRef = ReferenceTranslator.translateAsValueReference(suspendPropertyDescriptor, context()),
baseClassRef = coroutineBaseClassRef, baseClassRef = coroutineBaseClassRef,
stateName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "state")), stateName = getCoroutinePropertyName("state"),
exceptionStateName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "exceptionState")), exceptionStateName = getCoroutinePropertyName("exceptionState"),
finallyPathName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "finallyPath")), finallyPathName = getCoroutinePropertyName("finallyPath"),
resultName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "result")), resultName = getCoroutinePropertyName("result"),
exceptionName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "exception")), exceptionName = getCoroutinePropertyName("exception"),
facadeName = getCoroutinePropertyName("facade"),
hasController = continuationType.isBuiltinExtensionFunctionalType hasController = continuationType.isBuiltinExtensionFunctionalType
) )
coroutineType = continuationType coroutineType = continuationType
@@ -134,7 +134,7 @@ public final class FunctionBodyTranslator extends AbstractTranslator {
private boolean mustAddReturnToGeneratedFunctionBody() { private boolean mustAddReturnToGeneratedFunctionBody() {
KotlinType functionReturnType = descriptor.getReturnType(); KotlinType functionReturnType = descriptor.getReturnType();
assert functionReturnType != null : "Function return typed type must be resolved."; 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 @NotNull
@@ -358,4 +358,11 @@ public final class TranslationUtils {
.getContributedFunctions(Name.identifier("doResume"), NoLookupLocation.FROM_DESERIALIZATION) .getContributedFunctions(Name.identifier("doResume"), NoLookupLocation.FROM_DESERIALIZATION)
.iterator().next(); .iterator().next();
} }
@NotNull
public static FunctionDescriptor getCoroutineResumeFunction(@NotNull TranslationContext context) {
return getCoroutineBaseClass(context).getUnsubstitutedMemberScope()
.getContributedFunctions(Name.identifier("resume"), NoLookupLocation.FROM_DESERIALIZATION)
.iterator().next();
}
} }