diff --git a/compiler/testData/codegen/box/coroutines/recursiveSuspend.kt b/compiler/testData/codegen/box/coroutines/recursiveSuspend.kt new file mode 100644 index 00000000000..099cbb57781 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/recursiveSuspend.kt @@ -0,0 +1,42 @@ +// IGNORE_BACKEND: NATIVE +// WITH_RUNTIME +// WITH_COROUTINES +import kotlin.coroutines.experimental.* +import kotlin.coroutines.experimental.intrinsics.* + +fun box(): String { + var result = 0 + + builder { + result = factorial(4) + } + + while (postponed != null) { + postponed!!() + } + + if (result != 24) return "fail1: $result" + if (log != "1;1;2;6;24;") return "fail2: $log" + + return "OK" +} + +suspend fun factorial(a: Int): Int = if (a > 0) suspendHere(factorial(a - 1) * a) else suspendHere(1) + +suspend fun suspendHere(value: Int): Int = suspendCoroutineOrReturn { x -> + postponed = { + log += "$value;" + x.resume(value) + } + COROUTINE_SUSPENDED +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(handleResultContinuation { + postponed = null + }) +} + +var postponed: (() -> Unit)? = { } + +var log = "" \ No newline at end of file diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index b448753c750..c260ef5159c 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -5114,6 +5114,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("recursiveSuspend.kt") + public void testRecursiveSuspend() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/recursiveSuspend.kt"); + doTest(fileName); + } + @TestMetadata("returnByLabel.kt") public void testReturnByLabel() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/returnByLabel.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 6dbe21991cc..47a1415bc7d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -5114,6 +5114,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("recursiveSuspend.kt") + public void testRecursiveSuspend() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/recursiveSuspend.kt"); + doTest(fileName); + } + @TestMetadata("returnByLabel.kt") public void testReturnByLabel() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/returnByLabel.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 0e9151b72bc..d2e17b54d1e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -5114,6 +5114,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("recursiveSuspend.kt") + public void testRecursiveSuspend() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/recursiveSuspend.kt"); + doTest(fileName); + } + @TestMetadata("returnByLabel.kt") public void testReturnByLabel() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/returnByLabel.kt"); 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 0afe5aea8b2..788d5c9eaaf 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 @@ -28,7 +28,8 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f private val innerFunction = function.getInnerFunction() private val functionWithBody = innerFunction ?: function private val body = functionWithBody.body - private val localVariables = (function.collectLocalVariables() + functionWithBody.collectLocalVariables()).toMutableSet() + private val localVariables = (function.collectLocalVariables() + functionWithBody.collectLocalVariables() - + functionWithBody.parameters.last().name).toMutableSet() private val className = function.scope.parent.declareFreshName("Coroutine\$${name ?: "anonymous"}") fun transform(): List { @@ -61,29 +62,20 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f if (context.metadata.hasReceiver) { constructor.parameters += JsParameter(context.receiverFieldName) } - constructor.parameters += function.parameters.map { JsParameter(it.name) } - if (innerFunction != null) { - constructor.parameters += innerFunction.parameters.map { JsParameter(it.name) } - } - - val lastParameter = function.parameters.lastOrNull()?.name + val parameters = function.parameters + innerFunction?.parameters.orEmpty() + constructor.parameters += parameters.map { JsParameter(it.name) } + val lastParameter = parameters.lastOrNull()?.name val controllerName = if (context.metadata.hasController) { - function.scope.declareFreshName("controller").apply { constructor.parameters += JsParameter(this) } + function.scope.declareFreshName("controller").apply { + constructor.parameters.add(constructor.parameters.lastIndex, JsParameter(this)) + } } else { null } - val interceptorRef = if (context.metadata.isLambda) { - val interceptorName = function.scope.declareFreshName("interceptor") - constructor.parameters += JsParameter(interceptorName) - interceptorName.makeRef() - } - else { - lastParameter!!.makeRef() - } - + val interceptorRef = lastParameter!!.makeRef() val parameterNames = (function.parameters.map { it.name } + innerFunction?.parameters?.map { it.name }.orEmpty()).toSet() constructor.body.statements.run { @@ -156,20 +148,14 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f if (context.metadata.hasReceiver) { instantiation.arguments += JsLiteral.THIS } - instantiation.arguments += function.parameters.map { it.name.makeRef() } - if (innerFunction != null) { - instantiation.arguments += innerFunction.parameters.map { it.name.makeRef() } - } + val parameters = function.parameters + innerFunction?.parameters.orEmpty() + instantiation.arguments += parameters.dropLast(1).map { it.name.makeRef() } if (function.coroutineMetadata!!.hasController) { instantiation.arguments += JsLiteral.THIS } - if (context.metadata.isLambda) { - val interceptorParamName = functionWithBody.scope.declareFreshName("interceptor") - functionWithBody.parameters += JsParameter(interceptorParamName) - instantiation.arguments += interceptorParamName.makeRef() - } + instantiation.arguments += parameters.last().name.makeRef() val suspendedName = functionWithBody.scope.declareFreshName("suspended") functionWithBody.parameters += JsParameter(suspendedName) 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 1df82a0d024..66d0188f008 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 @@ -5829,6 +5829,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("recursiveSuspend.kt") + public void testRecursiveSuspend() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/recursiveSuspend.kt"); + doTest(fileName); + } + @TestMetadata("returnByLabel.kt") public void testReturnByLabel() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/returnByLabel.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt index efc651e7bab..3293cf7182e 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt @@ -137,7 +137,7 @@ private fun translateFunctionCall( if (resolvedCall.resultingDescriptor.isSuspend) { if (context.isInStateMachine) { context.currentBlock.statements += JsAstUtils.asSyntheticStatement(callExpression.apply { isSuspend = true }) - val coroutineRef = TranslationUtils.translateContinuationArgument(context, resolvedCall) + val coroutineRef = TranslationUtils.translateContinuationArgument(context) return context.defineTemporary(JsNameRef("\$\$coroutineResult\$\$", coroutineRef).apply { sideEffects = SideEffectKind.DEPENDS_ON_STATE coroutineResult = true diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java index c5477aeaf04..b521dfb93ef 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java @@ -21,7 +21,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.Annotations; -import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor; import org.jetbrains.kotlin.incremental.components.NoLookupLocation; @@ -109,7 +108,7 @@ public class TranslationContext { } if (declarationDescriptor instanceof FunctionDescriptor) { FunctionDescriptor function = (FunctionDescriptor) declarationDescriptor; - if (function.isSuspend() && !(function instanceof AnonymousFunctionDescriptor)) { + if (function.isSuspend()) { ClassDescriptor continuationDescriptor = DescriptorUtilKt.findContinuationClassDescriptor(getCurrentModule(), NoLookupLocation.FROM_BACKEND); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt index 8ad8e0553d6..6dc77a8e09f 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt @@ -27,7 +27,6 @@ import org.jetbrains.kotlin.js.translate.expression.translateAndAliasParameters import org.jetbrains.kotlin.js.translate.expression.translateFunction import org.jetbrains.kotlin.js.translate.expression.wrapWithInlineMetadata import org.jetbrains.kotlin.js.translate.general.TranslatorVisitor -import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator import org.jetbrains.kotlin.js.translate.utils.* import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty @@ -99,17 +98,11 @@ abstract class AbstractDeclarationVisitor : TranslatorVisitor() { context: TranslationContext ): JsExpression { val function = context.getFunctionObject(descriptor) - var innerContext = context.newDeclaration(descriptor).translateAndAliasParameters(descriptor, function.parameters) + val innerContext = context.newDeclaration(descriptor).translateAndAliasParameters(descriptor, function.parameters) if (descriptor.isSuspend) { if (descriptor.requiresStateMachineTransformation(context)) { function.fillCoroutineMetadata(context, descriptor, hasController = false, isLambda = false) - innerContext = innerContext.innerContextWithAliased(descriptor, JsAstUtils.stateMachineReceiver()) - } - else { - val continuationRef = ReferenceTranslator.translateAsValueReference( - innerContext.continuationParameterDescriptor!!, innerContext) - innerContext = innerContext.innerContextWithAliased(descriptor, continuationRef) } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt index a56f1cc8287..02b6589c35b 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt @@ -29,6 +29,8 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.reference.CallExpressionTranslator.shouldBeInlined import org.jetbrains.kotlin.js.translate.utils.BindingUtils import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.translateFunctionBody +import org.jetbrains.kotlin.js.translate.utils.JsAstUtils +import org.jetbrains.kotlin.js.translate.utils.requiresStateMachineTransformation import org.jetbrains.kotlin.psi.KtDeclarationWithBody import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue @@ -66,6 +68,12 @@ fun TranslationContext.translateAndAliasParameters( if (continuationDescriptor != null) { val jsParameter = JsParameter(getNameForDescriptor(continuationDescriptor)) targetList += jsParameter + aliases[continuationDescriptor] = if (!descriptor.requiresStateMachineTransformation(this)) { + JsAstUtils.pureFqn(jsParameter.name, null) + } + else { + JsAstUtils.stateMachineReceiver() + } } return this.innerContextWithDescriptorsAliased(aliases) 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 5e434500f53..57f783d6b19 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 @@ -45,14 +45,8 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato val lambda = invokingContext.getFunctionObject(descriptor) - val aliases = mutableMapOf() - if (descriptor.isCoroutineLambda) { - aliases.put(descriptor, JsAstUtils.stateMachineReceiver()) - } - val functionContext = invokingContext .newFunctionBodyWithUsageTracker(lambda, descriptor) - .innerContextWithDescriptorsAliased(aliases) .translateAndAliasParameters(descriptor, lambda.parameters) descriptor.valueParameters.forEach { @@ -76,8 +70,7 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato } lambdaCreator.name.staticRef = lambdaCreator lambdaCreator.fillCoroutineMetadata(invokingContext, descriptor) - return lambdaCreator.withCapturedParameters(descriptor, descriptor.wrapContextForCoroutineIfNecessary(functionContext), - invokingContext) + return lambdaCreator.withCapturedParameters(descriptor, functionContext, invokingContext) } lambda.name = invokingContext.getInnerNameForDescriptor(descriptor) @@ -112,15 +105,6 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato } } -private fun CallableMemberDescriptor.wrapContextForCoroutineIfNecessary(context: TranslationContext): TranslationContext { - return if (isCoroutineLambda) { - context.innerContextWithDescriptorsAliased(mapOf(this to JsAstUtils.stateMachineReceiver())) - } - else { - context - } -} - fun JsFunction.withCapturedParameters( descriptor: CallableMemberDescriptor, context: TranslationContext, diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt index 04fce5a4d4d..24c9a99b728 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt @@ -155,7 +155,7 @@ class CallArgumentTranslator private constructor( val callableDescriptor = resolvedCall.resultingDescriptor if (callableDescriptor is FunctionDescriptor && callableDescriptor.isSuspend) { val facadeName = context().getNameForDescriptor(TranslationUtils.getCoroutineProperty(context(), "facade")) - result.add(JsAstUtils.pureFqn(facadeName, TranslationUtils.translateContinuationArgument(context(), resolvedCall))) + result.add(JsAstUtils.pureFqn(facadeName, TranslationUtils.translateContinuationArgument(context()))) } removeLastUndefinedArguments(result) 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 32eca48b59c..94e9f5d86cc 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 @@ -24,7 +24,6 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; import org.jetbrains.kotlin.incremental.components.NoLookupLocation; import org.jetbrains.kotlin.js.backend.ast.*; -import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator; import org.jetbrains.kotlin.js.translate.context.Namer; import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable; import org.jetbrains.kotlin.js.translate.context.TranslationContext; @@ -38,7 +37,6 @@ import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContextUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils; -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.serialization.deserialization.FindClassInModuleKt; @@ -81,7 +79,7 @@ public final class TranslationUtils { } @NotNull - public static String getAccessorFunctionName(@NotNull FunctionDescriptor descriptor) { + private static String getAccessorFunctionName(@NotNull FunctionDescriptor descriptor) { boolean isGetter = descriptor instanceof PropertyGetterDescriptor || descriptor instanceof LocalVariableAccessorDescriptor.Getter; return isGetter ? "get" : "set"; } @@ -227,12 +225,6 @@ public final class TranslationUtils { return Translation.translateAsExpression(left, context, block); } - @NotNull - public static JsExpression translateRightExpression(@NotNull TranslationContext context, - @NotNull KtBinaryExpression expression) { - return translateRightExpression(context, expression, context.dynamicContext().jsBlock()); - } - @NotNull public static JsExpression translateRightExpression( @NotNull TranslationContext context, @@ -350,19 +342,13 @@ public final class TranslationUtils { } @NotNull - public static JsExpression translateContinuationArgument(@NotNull TranslationContext context, @NotNull ResolvedCall resolvedCall) { - CallableDescriptor continuationDescriptor = - context.bindingContext().get(BindingContext.ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.getCall()); - - if (continuationDescriptor == null) { - continuationDescriptor = getEnclosingContinuationParameter(context); - } - + public static JsExpression translateContinuationArgument(@NotNull TranslationContext context) { + CallableDescriptor continuationDescriptor = getEnclosingContinuationParameter(context); return ReferenceTranslator.translateAsValueReference(continuationDescriptor, context); } @NotNull - public static VariableDescriptor getEnclosingContinuationParameter(@NotNull TranslationContext context) { + private static VariableDescriptor getEnclosingContinuationParameter(@NotNull TranslationContext context) { VariableDescriptor result = context.getContinuationParameterDescriptor(); if (result == null) { assert context.getParent() != null; @@ -395,13 +381,6 @@ public final class TranslationUtils { .iterator().next(); } - @NotNull - public static FunctionDescriptor getCoroutineResumeFunction(@NotNull TranslationContext context) { - return getCoroutineBaseClass(context).getUnsubstitutedMemberScope() - .getContributedFunctions(Name.identifier("resume"), NoLookupLocation.FROM_DESERIALIZATION) - .iterator().next(); - } - public static boolean isOverridableFunctionWithDefaultParameters(@NotNull FunctionDescriptor descriptor) { return DescriptorUtilsKt.hasOrInheritsParametersWithDefaultValue(descriptor) && !(descriptor instanceof ConstructorDescriptor) &&