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_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 ->
|
||||||
|
|||||||
+2
@@ -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
|
||||||
|
|||||||
+14
-4
@@ -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(
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+2
-14
@@ -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.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-5
@@ -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
|
||||||
|
|||||||
+1
-1
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user