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_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
}
@@ -1,6 +1,5 @@
// WITH_RUNTIME
// WITH_COROUTINES
// IGNORE_BACKEND: JS
import kotlin.coroutines.*
suspend fun suspendHere(v: String): String = CoroutineIntrinsics.suspendCoroutineOrReturn { x ->
@@ -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
@@ -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(
+4 -4
View File
@@ -29,7 +29,7 @@ public fun <R, T> (suspend R.() -> T).createCoroutine(
receiver: R,
completion: Continuation<T>,
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].
@@ -43,7 +43,7 @@ public fun <R, T> (suspend R.() -> T).startCoroutine(
completion: Continuation<T>,
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(
completion: Continuation<T>,
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].
@@ -70,7 +70,7 @@ public fun <T> (suspend () -> T).startCoroutine(
completion: Continuation<T>,
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")
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);
}
}
@@ -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
@@ -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
@@ -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();
}
}