JS: make call to suspend lambda to resume it immediately. See KT-15379
This commit is contained in:
+1
-2
@@ -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 ->
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
+14
-4
@@ -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(
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-14
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-5
@@ -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
|
||||
|
||||
+1
-1
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user