diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java index 7ab5e6f0f75..113779305b9 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java @@ -198,7 +198,7 @@ public class ClosureCodegen extends MemberCodegen { ); } - private void generateBridges() { + protected void generateBridges() { FunctionDescriptor erasedInterfaceFunction; if (samType == null) { erasedInterfaceFunction = getErasedInvokeFunction(funDescriptor); @@ -233,7 +233,10 @@ public class ClosureCodegen extends MemberCodegen { protected void generateKotlinMetadataAnnotation() { FunctionDescriptor freeLambdaDescriptor = createFreeLambdaDescriptor(funDescriptor); Method method = v.getSerializationBindings().get(METHOD_FOR_FUNCTION, funDescriptor); - assert method != null : "No method for " + funDescriptor; + + // Can be null for named suspend function + if (method == null) return; + v.getSerializationBindings().put(METHOD_FOR_FUNCTION, freeLambdaDescriptor, method); final DescriptorSerializer serializer = diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 357d1095df4..39226df9cf8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -51,7 +51,6 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; import org.jetbrains.kotlin.codegen.when.SwitchCodegen; import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil; import org.jetbrains.kotlin.descriptors.*; -import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor; import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor; @@ -182,7 +181,7 @@ public class ExpressionCodegen extends KtVisitor impleme if (originalCoroutineDescriptor != null) return originalCoroutineDescriptor; if (context.getFunctionDescriptor().isSuspend()) { - return (FunctionDescriptor) CoroutineCodegenUtilKt.unwrapInitialDescriptorForSuspendFunction(context.getFunctionDescriptor()); + return CoroutineCodegenUtilKt.unwrapInitialDescriptorForSuspendFunction(context.getFunctionDescriptor()); } return null; @@ -192,7 +191,7 @@ public class ExpressionCodegen extends KtVisitor impleme private static FunctionDescriptor getOriginalCoroutineDescriptor(MethodContext context) { if ((context.getParentContext() instanceof ClosureContext) && (context.getParentContext().closure != null) && - context.getParentContext().closure.isCoroutine()) { + context.getParentContext().closure.isSuspend()) { return ((ClosureContext) context.getParentContext()).getCoroutineDescriptor(); } @@ -1650,7 +1649,7 @@ public class ExpressionCodegen extends KtVisitor impleme declaration.getContainingFile() ); - ClosureCodegen coroutineCodegen = CoroutineCodegen.create(this, descriptor, declaration, cv); + ClosureCodegen coroutineCodegen = CoroutineCodegen.createByLambda(this, descriptor, declaration, cv); ClosureCodegen closureCodegen = coroutineCodegen != null ? coroutineCodegen : new ClosureCodegen( state, declaration, samType, context.intoClosure(descriptor, this, typeMapper), functionReferenceTarget, strategy, parentCodegen, cv @@ -1658,6 +1657,14 @@ public class ExpressionCodegen extends KtVisitor impleme closureCodegen.generate(); + return putClosureInstanceOnStack(closureCodegen, functionReferenceReceiver); + } + + @NotNull + public StackValue putClosureInstanceOnStack( + @NotNull ClosureCodegen closureCodegen, + @Nullable StackValue functionReferenceReceiver + ) { if (closureCodegen.getReifiedTypeParametersUsages().wereUsedReifiedParameters()) { ReifiedTypeInliner.putNeedClassReificationMarker(v); propagateChildReifiedTypeParametersUsages(closureCodegen.getReifiedTypeParametersUsages()); @@ -1770,9 +1777,20 @@ public class ExpressionCodegen extends KtVisitor impleme ); } - if (closure.isCoroutine()) { + if (closure.isSuspend()) { // resultContinuation - v.aconst(null); + if (closure.isSuspendLambda()) { + v.aconst(null); + } + else { + assert context.getFunctionDescriptor().isSuspend() : "Coroutines closure must be created only inside suspend functions"; + ValueParameterDescriptor continuationParameter = CollectionsKt.last(context.getFunctionDescriptor().getValueParameters()); + StackValue continuationValue = findLocalOrCapturedValue(continuationParameter); + + assert continuationValue != null : "Couldn't find a value for continuation parameter of " + context.getFunctionDescriptor(); + + callGenerator.putCapturedValueOnStack(continuationValue, continuationValue.type, paramIndex++); + } } } @@ -1870,17 +1888,17 @@ public class ExpressionCodegen extends KtVisitor impleme @Nullable private StackValue getCoroutineInstanceValueForSuspensionPoint(@NotNull ResolvedCall resolvedCall) { - CallableDescriptor enclosingSuspendLambdaForSuspensionPoint = - bindingContext.get(ENCLOSING_SUSPEND_LAMBDA_FOR_SUSPENSION_POINT, resolvedCall.getCall()); + FunctionDescriptor enclosingSuspendLambdaForSuspensionPoint = + bindingContext.get(ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.getCall()); if (enclosingSuspendLambdaForSuspensionPoint == null) return null; - return genCoroutineInstanceByLambda(enclosingSuspendLambdaForSuspensionPoint); + return genCoroutineInstanceBySuspendFunction(enclosingSuspendLambdaForSuspensionPoint); } - @NotNull - private StackValue genCoroutineInstanceByLambda(@NotNull CallableDescriptor suspendLambda) { - ClassDescriptor suspendLambdaClassDescriptor = - bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, suspendLambda); + @Nullable + private StackValue genCoroutineInstanceBySuspendFunction(@NotNull FunctionDescriptor suspendFunction) { + if (!CoroutineCodegenUtilKt.isStateMachineNeeded(suspendFunction, bindingContext)) return null; + ClassDescriptor suspendLambdaClassDescriptor = bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, suspendFunction); assert suspendLambdaClassDescriptor != null : "Coroutine class descriptor should not be null"; return StackValue.thisOrOuter(this, suspendLambdaClassDescriptor, false, false); @@ -2772,8 +2790,10 @@ public class ExpressionCodegen extends KtVisitor impleme StackValue result = callable.invokeMethodWithArguments(resolvedCall, receiver, this); - if (bindingContext.get(BindingContext.ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.getCall()) != null) { - // Suspend function's calls inside another suspend function should behave like they leave values of correct type, + if (resolvedCall.getResultingDescriptor() instanceof FunctionDescriptor && + ((FunctionDescriptor) resolvedCall.getResultingDescriptor()).isSuspend() && + !CoroutineCodegenUtilKt.isSuspensionPointInStateMachine(resolvedCall, bindingContext)) { + // Suspend function's tail calls inside another suspend function should behave like they leave values of correct type, // while real methods return java/lang/Object. // NB: They are always in return position at the moment. // If we didn't do this, StackValue.coerce would add proper CHECKCAST that would've failed in case of callee function @@ -2785,13 +2805,13 @@ public class ExpressionCodegen extends KtVisitor impleme } private StackValue getContinuationParameterFromEnclosingSuspendFunction(@NotNull ResolvedCall resolvedCall) { - SimpleFunctionDescriptor enclosingSuspendFunction = + FunctionDescriptor enclosingSuspendFunction = bindingContext.get(BindingContext.ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.getCall()); assert enclosingSuspendFunction != null : "Suspend functions may be called either as suspension points or from another suspend function"; - SimpleFunctionDescriptor enclosingSuspendFunctionJvmView = + FunctionDescriptor enclosingSuspendFunctionJvmView = bindingContext.get(CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW, enclosingSuspendFunction); assert enclosingSuspendFunctionJvmView != null : "No JVM view function found for " + enclosingSuspendFunction; @@ -2855,7 +2875,7 @@ public class ExpressionCodegen extends KtVisitor impleme @NotNull CallGenerator callGenerator, @NotNull ArgumentGenerator argumentGenerator ) { - boolean isSuspensionPoint = CoroutineCodegenUtilKt.isSuspensionPoint(resolvedCall, bindingContext); + boolean isSuspensionPoint = CoroutineCodegenUtilKt.isSuspensionPointInStateMachine(resolvedCall, bindingContext); if (isSuspensionPoint) { // Inline markers are used to spill the stack before coroutine suspension addInlineMarker(v, true); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 745bb932659..f097d85749b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -31,6 +31,8 @@ import org.jetbrains.kotlin.backend.common.bridges.ImplKt; import org.jetbrains.kotlin.codegen.annotation.AnnotatedWithOnlyTargetedAnnotations; import org.jetbrains.kotlin.codegen.binding.CodegenBinding; import org.jetbrains.kotlin.codegen.context.*; +import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; +import org.jetbrains.kotlin.codegen.coroutines.SuspendFunctionGenerationStrategy; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; import org.jetbrains.kotlin.descriptors.*; @@ -119,17 +121,29 @@ public class FunctionCodegen { public void gen(@NotNull KtNamedFunction function) { SimpleFunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, function); + if (bindingContext.get(CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW, functionDescriptor) != null) { + functionDescriptor = + (SimpleFunctionDescriptor) bindingContext.get(CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW, functionDescriptor); + } + if (functionDescriptor == null) { throw ExceptionLogger.logDescriptorNotFound("No descriptor for function " + function.getName(), function); } - if (bindingContext.get(CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW, functionDescriptor) != null) { - functionDescriptor = bindingContext.get(CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW, functionDescriptor); - } - if (owner.getContextKind() != OwnerKind.DEFAULT_IMPLS || function.hasBody()) { - generateMethod(JvmDeclarationOriginKt.OtherOrigin(function, functionDescriptor), functionDescriptor, - new FunctionGenerationStrategy.FunctionDefault(state, function)); + FunctionGenerationStrategy strategy; + if (functionDescriptor.isSuspend()) { + strategy = new SuspendFunctionGenerationStrategy( + state, + CoroutineCodegenUtilKt.unwrapInitialDescriptorForSuspendFunction(functionDescriptor), + function + ); + } + else { + strategy = new FunctionGenerationStrategy.FunctionDefault(state, function); + } + + generateMethod(JvmDeclarationOriginKt.OtherOrigin(function, functionDescriptor), functionDescriptor, strategy); } generateDefaultIfNeeded(owner.intoFunction(functionDescriptor, true), functionDescriptor, owner.getContextKind(), diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java index f6c115a86c5..90cc207da6c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java @@ -114,7 +114,7 @@ public class JvmCodegenUtil { return closure.getCaptureThis() == null && closure.getCaptureReceiverType() == null && closure.getCaptureVariables().isEmpty() && - !closure.isCoroutine(); + !closure.isSuspend(); } private static boolean isCallInsideSameClassAsDeclared(@NotNull CallableMemberDescriptor descriptor, @NotNull CodegenContext context) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt index cd39139ae74..df269206ba5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt @@ -70,7 +70,7 @@ class JvmRuntimeTypes(module: ModuleDescriptor) { fun getSupertypesForClosure(descriptor: FunctionDescriptor): Collection { val actualFunctionDescriptor = - if (descriptor.isSuspendLambda) + if (descriptor.isSuspend) createJvmSuspendFunctionView(descriptor) else descriptor @@ -84,15 +84,17 @@ class JvmRuntimeTypes(module: ModuleDescriptor) { actualFunctionDescriptor.returnType!! ) - if (descriptor.isSuspendLambda) { + if (descriptor.isSuspend) { return mutableListOf().apply { add(coroutineImplClass.defaultType) - add(functionType) - val parametersNumber = - descriptor.valueParameters.size + (if (functionType.isExtensionFunctionType) 1 else 0) + if (descriptor.isSuspendLambda) { + val parametersNumber = + descriptor.valueParameters.size + (if (functionType.isExtensionFunctionType) 1 else 0) - addIfNotNull(suspendFunctions.getOrNull(parametersNumber)?.defaultType) + addIfNotNull(suspendFunctions.getOrNull(parametersNumber)?.defaultType) + add(functionType) + } } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CalculatedClosure.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CalculatedClosure.java index 1031769e7ef..74f3fe5b477 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CalculatedClosure.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CalculatedClosure.java @@ -44,5 +44,7 @@ public interface CalculatedClosure { @NotNull List> getRecordedFields(); - boolean isCoroutine(); + boolean isSuspend(); + + boolean isSuspendLambda(); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java index 51d2c0bef42..3fa8e68dd50 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java @@ -100,10 +100,21 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { @NotNull CallableDescriptor callableDescriptor, @NotNull Collection supertypes, @NotNull String name + ) { + return recordClassForCallable(element, callableDescriptor, supertypes, name, null); + } + + @NotNull + private ClassDescriptor recordClassForCallable( + @NotNull KtElement element, + @NotNull CallableDescriptor callableDescriptor, + @NotNull Collection supertypes, + @NotNull String name, + @Nullable DeclarationDescriptor customContainer ) { String simpleName = name.substring(name.lastIndexOf('/') + 1); ClassDescriptor classDescriptor = new SyntheticClassDescriptorForLambda( - correctContainerForLambda(callableDescriptor, element), + customContainer != null ? customContainer : correctContainerForLambda(callableDescriptor, element), Name.special(""), supertypes, element @@ -289,7 +300,8 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { nameStack.push(name); if (CoroutineUtilKt.isSuspendLambda(functionDescriptor)) { - closure.setCoroutine(true); + closure.setSuspend(true); + closure.setSuspendLambda(); } super.visitLambdaExpression(lambdaExpression); @@ -415,6 +427,8 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { // working around a problem with shallow analysis if (functionDescriptor == null) return; + String nameForClassOrPackageMember = getNameForClassOrPackageMember(functionDescriptor); + if (functionDescriptor instanceof SimpleFunctionDescriptor && functionDescriptor.isSuspend()) { SimpleFunctionDescriptor jvmSuspendFunctionView = CoroutineCodegenUtilKt.createJvmSuspendFunctionView( @@ -437,31 +451,53 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { bindingTrace.record( CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW, - (SimpleFunctionDescriptor) functionDescriptor, + functionDescriptor, jvmSuspendFunctionView ); + + if (CoroutineCodegenUtilKt.containsNonTailSuspensionCalls(functionDescriptor, bindingContext)) { + if (nameForClassOrPackageMember != null) { + nameStack.push(nameForClassOrPackageMember); + } + + processNamedFunctionWithClosure(function, functionDescriptor, functionDescriptor).setSuspend(true); + + if (nameForClassOrPackageMember != null) { + nameStack.pop(); + } + return; + } } - String nameForClassOrPackageMember = getNameForClassOrPackageMember(functionDescriptor); if (nameForClassOrPackageMember != null) { nameStack.push(nameForClassOrPackageMember); super.visitNamedFunction(function); nameStack.pop(); } else { - String name = inventAnonymousClassName(); - Collection supertypes = runtimeTypes.getSupertypesForClosure(functionDescriptor); - ClassDescriptor classDescriptor = recordClassForCallable(function, functionDescriptor, supertypes, name); - recordClosure(classDescriptor, name); - - classStack.push(classDescriptor); - nameStack.push(name); - super.visitNamedFunction(function); - nameStack.pop(); - classStack.pop(); + processNamedFunctionWithClosure(function, functionDescriptor, null); } } + private MutableClosure processNamedFunctionWithClosure( + @NotNull KtNamedFunction function, + @NotNull FunctionDescriptor functionDescriptor, + @Nullable DeclarationDescriptor customContainer + ) { + String name = inventAnonymousClassName(); + Collection supertypes = runtimeTypes.getSupertypesForClosure(functionDescriptor); + ClassDescriptor classDescriptor = recordClassForCallable(function, functionDescriptor, supertypes, name, customContainer); + MutableClosure closure = recordClosure(classDescriptor, name); + + classStack.push(classDescriptor); + nameStack.push(name); + super.visitNamedFunction(function); + nameStack.pop(); + classStack.pop(); + + return closure; + } + @Nullable private String getNameForClassOrPackageMember(@NotNull DeclarationDescriptor descriptor) { DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java index 9d540c8e568..6fb69139246 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java @@ -66,7 +66,7 @@ public class CodegenBinding { public static final WritableSlice LOCAL_VARIABLE_PROPERTY_METADATA = Slices.createSimpleSlice(); - public static final WritableSlice SUSPEND_FUNCTION_TO_JVM_VIEW = + public static final WritableSlice SUSPEND_FUNCTION_TO_JVM_VIEW = Slices.createSimpleSlice(); public static final WritableSlice PARAMETER_SYNONYM = diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java index 72c185b140e..c56a0a19860 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java @@ -40,7 +40,8 @@ public final class MutableClosure implements CalculatedClosure { private Map parameterOffsetInConstructor; private List> recordedFields; private KotlinType captureReceiverType; - private boolean isCoroutine; + private boolean isSuspend; + private boolean isSuspendLambda; MutableClosure(@NotNull ClassDescriptor classDescriptor, @Nullable ClassDescriptor enclosingClass) { this.closureClass = classDescriptor; @@ -119,12 +120,21 @@ public final class MutableClosure implements CalculatedClosure { } @Override - public boolean isCoroutine() { - return isCoroutine; + public boolean isSuspend() { + return isSuspend; } - public void setCoroutine(boolean coroutine) { - this.isCoroutine = coroutine; + public void setSuspend(boolean suspend) { + this.isSuspend = suspend; + } + + @Override + public boolean isSuspendLambda() { + return isSuspendLambda; + } + + public void setSuspendLambda() { + isSuspendLambda = true; } public void recordField(String name, Type type) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt index cdd4379683b..edb545447b9 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt @@ -19,7 +19,9 @@ package org.jetbrains.kotlin.codegen.coroutines import com.intellij.util.ArrayUtil import org.jetbrains.kotlin.backend.common.CONTINUATION_RESUME_METHOD_NAME import org.jetbrains.kotlin.codegen.* +import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.context.ClosureContext +import org.jetbrains.kotlin.codegen.context.MethodContext import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.coroutines.isSuspendLambda import org.jetbrains.kotlin.descriptors.* @@ -29,9 +31,9 @@ import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtDeclarationWithBody import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtFunctionLiteral import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns -import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin @@ -42,6 +44,7 @@ import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.types.typeUtil.makeNullable import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.singletonOrEmptyList +import org.jetbrains.org.objectweb.asm.MethodVisitor import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter @@ -49,16 +52,20 @@ import org.jetbrains.org.objectweb.asm.commons.Method class CoroutineCodegen( - state: GenerationState, + outerExpressionCodegen: ExpressionCodegen, element: KtElement, private val closureContext: ClosureContext, - strategy: FunctionGenerationStrategy, - parentCodegen: MemberCodegen<*>, classBuilder: ClassBuilder, - private val coroutineLambdaDescriptor: FunctionDescriptor -) : ClosureCodegen(state, element, null, closureContext, null, strategy, parentCodegen, classBuilder) { + private val originalSuspendLambdaDescriptor: FunctionDescriptor? +) : ClosureCodegen( + outerExpressionCodegen.state, + element, null, closureContext, null, + FailingFunctionGenerationStrategy, + outerExpressionCodegen.parentCodegen, classBuilder +) { private val classDescriptor = closureContext.contextDescriptor + private val builtIns = funDescriptor.builtIns private lateinit var constructorToUseFromInvoke: Method @@ -75,33 +82,33 @@ class CoroutineCodegen( listOf( ValueParameterDescriptorImpl( this@doResume, null, 0, Annotations.EMPTY, Name.identifier("data"), - module.builtIns.nullableAnyType, + builtIns.nullableAnyType, /* isDefault = */ false, /* isCrossinline = */ false, /* isNoinline = */ false, /* varargElementType = */ null, SourceElement.NO_SOURCE ), ValueParameterDescriptorImpl( this@doResume, null, 1, Annotations.EMPTY, Name.identifier("throwable"), - module.builtIns.throwable.defaultType.makeNullable(), + builtIns.throwable.defaultType.makeNullable(), /* isDefault = */ false, /* isCrossinline = */ false, /* isNoinline = */ false, /* varargElementType = */ null, SourceElement.NO_SOURCE ) ), - funDescriptor.builtIns.nullableAnyType, + builtIns.nullableAnyType, Modality.FINAL, - Visibilities.PROTECTED + Visibilities.PUBLIC ) } private val createCoroutineDescriptor = funDescriptor.createCustomCopy { - setName(Name.identifier("create")) + setName(Name.identifier(SUSPEND_FUNCTION_CREATE_METHOD_NAME)) setReturnType( KotlinTypeFactory.simpleNotNullType( Annotations.EMPTY, - funDescriptor.builtIns.continuationClassDescriptor, - listOf(funDescriptor.builtIns.unitType.asTypeProjection()) + builtIns.continuationClassDescriptor, + listOf(builtIns.unitType.asTypeProjection()) ) ) setVisibility(Visibilities.PUBLIC) @@ -121,9 +128,17 @@ class CoroutineCodegen( generateDoResume() } + override fun generateBridges() { + if (originalSuspendLambdaDescriptor == null) return + super.generateBridges() + } + override fun generateBody() { super.generateBody() + if (originalSuspendLambdaDescriptor == null) return + + // create() = ... functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, createCoroutineDescriptor, object : FunctionGenerationStrategy.CodegenBased(state) { override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { @@ -182,7 +197,6 @@ class CoroutineCodegen( areturn(AsmTypes.OBJECT_TYPE) } - override fun generateConstructor(): Method { val args = calculateConstructorParameters(typeMapper, closure, asmType) val argTypes = args.map { it.fieldType }.plus(AsmTypes.CONTINUATION).toTypedArray() @@ -217,6 +231,7 @@ class CoroutineCodegen( } private fun generateCreateCoroutineMethod(codegen: ExpressionCodegen) { + assert(originalSuspendLambdaDescriptor != null) { "create method should only be generated for suspend lambdas" } val classDescriptor = closureContext.contextDescriptor val owner = typeMapper.mapClass(classDescriptor) @@ -265,7 +280,7 @@ class CoroutineCodegen( } private fun allLambdaParameters() = - coroutineLambdaDescriptor.extensionReceiverParameter.singletonOrEmptyList() + coroutineLambdaDescriptor.valueParameters + originalSuspendLambdaDescriptor?.extensionReceiverParameter.singletonOrEmptyList() + originalSuspendLambdaDescriptor?.valueParameters.orEmpty() private fun ExpressionCodegen.generateLoadField(fieldInfo: FieldInfo) { StackValue.field(fieldInfo, generateThisOrOuter(context.thisDescriptor, false)).put(fieldInfo.fieldType, v) @@ -298,7 +313,7 @@ class CoroutineCodegen( companion object { @JvmStatic - fun create( + fun createByLambda( expressionCodegen: ExpressionCodegen, originalCoroutineLambdaDescriptor: FunctionDescriptor, declaration: KtElement, @@ -307,21 +322,52 @@ class CoroutineCodegen( if (declaration !is KtFunctionLiteral) return null if (!originalCoroutineLambdaDescriptor.isSuspendLambda) return null - val descriptorWithContinuationReturnType = createJvmSuspendFunctionView(originalCoroutineLambdaDescriptor) - - val state = expressionCodegen.state return CoroutineCodegen( - state, + expressionCodegen, declaration, expressionCodegen.context.intoCoroutineClosure( - descriptorWithContinuationReturnType, originalCoroutineLambdaDescriptor, expressionCodegen, state.typeMapper + createJvmSuspendFunctionView(originalCoroutineLambdaDescriptor), + originalCoroutineLambdaDescriptor, expressionCodegen, expressionCodegen.state.typeMapper ), - FunctionGenerationStrategy.FunctionDefault(state, declaration), - expressionCodegen.parentCodegen, classBuilder, + classBuilder, originalCoroutineLambdaDescriptor ) } + + fun create( + expressionCodegen: ExpressionCodegen, + originalSuspendDescriptor: FunctionDescriptor, + declaration: KtFunction, + state: GenerationState + ): CoroutineCodegen { + val cv = state.factory.newVisitor( + OtherOrigin(declaration, originalSuspendDescriptor), + CodegenBinding.asmTypeForAnonymousClass(state.bindingContext, originalSuspendDescriptor), + declaration.containingFile + ) + + return CoroutineCodegen( + expressionCodegen, declaration, + expressionCodegen.context.intoClosure( + originalSuspendDescriptor, expressionCodegen, expressionCodegen.state.typeMapper + ), + cv, + originalSuspendLambdaDescriptor = null + ) + } } } private const val COROUTINE_LAMBDA_PARAMETER_PREFIX = "p$" + +private object FailingFunctionGenerationStrategy : FunctionGenerationStrategy() { + override fun generateBody( + mv: MethodVisitor, + frameMap: FrameMap, + signature: JvmMethodSignature, + context: MethodContext, + parentCodegen: MemberCodegen<*> + ) { + error("This functions must not be called") + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt new file mode 100644 index 00000000000..417d568ff3e --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.coroutines + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.FunctionGenerationStrategy +import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.resolve.jvm.AsmTypes +import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature +import org.jetbrains.org.objectweb.asm.Type + +class SuspendFunctionGenerationStrategy( + state: GenerationState, + private val originalSuspendDescriptor: FunctionDescriptor, + private val declaration: KtFunction +) : FunctionGenerationStrategy.CodegenBased(state) { + + override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { + if (!originalSuspendDescriptor.containsNonTailSuspensionCalls(state.bindingContext)) { + return codegen.returnExpression(declaration.bodyExpression) + } + + val coroutineCodegen = CoroutineCodegen.create(codegen, originalSuspendDescriptor, declaration, state) + coroutineCodegen.generate() + + codegen.putClosureInstanceOnStack(coroutineCodegen, null).put(Type.getObjectType(coroutineCodegen.className), codegen.v) + + with(codegen.v) { + invokeDoResumeWithUnit(coroutineCodegen.v.thisName) + + codegen.markLineNumber(declaration, true) + + areturn(AsmTypes.OBJECT_TYPE) + } + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt index 50d59c83146..3825107ed23 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt @@ -20,8 +20,10 @@ import com.intellij.openapi.project.Project import org.jetbrains.kotlin.backend.common.SUSPENDED_MARKER_NAME import org.jetbrains.kotlin.backend.common.isBuiltInSuspendCoroutineOrReturn import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType +import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper +import org.jetbrains.kotlin.coroutines.isSuspendLambda import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl @@ -55,6 +57,8 @@ const val AFTER_SUSPENSION_POINT_MARKER_NAME = "afterSuspensionPoint" const val ACTUAL_COROUTINE_START_MARKER_NAME = "actualCoroutineStart" const val COROUTINE_LABEL_FIELD_NAME = "label" +const val SUSPEND_FUNCTION_CREATE_METHOD_NAME = "create" +const val DO_RESUME_METHOD_NAME = "doResume" data class ResolvedCallWithRealDescriptor(val resolvedCall: ResolvedCall<*>, val fakeContinuationExpression: KtExpression) @@ -118,9 +122,18 @@ fun ResolvedCall<*>.replaceSuspensionFunctionWithRealDescriptor( return ResolvedCallWithRealDescriptor(newCall, thisExpression) } -fun ResolvedCall<*>.isSuspensionPoint(bindingContext: BindingContext) = - bindingContext[BindingContext.ENCLOSING_SUSPEND_LAMBDA_FOR_SUSPENSION_POINT, call] != null +fun ResolvedCall<*>.isSuspensionPointInStateMachine(bindingContext: BindingContext): Boolean { + if (resultingDescriptor.safeAs()?.isSuspend != true) return false + val enclosingSuspendFunction = bindingContext[BindingContext.ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, call] ?: return false + return enclosingSuspendFunction.isStateMachineNeeded(bindingContext) +} + +fun FunctionDescriptor.isStateMachineNeeded(bindingContext: BindingContext) = + isSuspendLambda || containsNonTailSuspensionCalls(bindingContext) + +fun FunctionDescriptor.containsNonTailSuspensionCalls(bindingContext: BindingContext) = + bindingContext[BindingContext.CONTAINS_NON_TAIL_SUSPEND_CALLS, original] == true // Suspend functions have irregular signatures on JVM, containing an additional last parameter with type `Continuation`, // and return type Any? // This function returns a function descriptor reflecting how the suspend function looks from point of view of JVM @@ -207,8 +220,9 @@ fun createMethodNodeForSuspendCoroutineOrReturn( return node } -fun CallableDescriptor?.unwrapInitialDescriptorForSuspendFunction() = - (this as? SimpleFunctionDescriptor)?.getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) ?: this +@Suppress("UNCHECKED_CAST") +fun D.unwrapInitialDescriptorForSuspendFunction(): D = + this.safeAs()?.getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) as D ?: this fun InstructionAdapter.loadSuspendMarker() { invokestatic( @@ -218,3 +232,17 @@ fun InstructionAdapter.loadSuspendMarker() { false ) } + +fun InstructionAdapter.invokeDoResumeWithUnit(thisName: String) { + // .doResume(Unit, null) + StackValue.putUnitInstance(this) + + aconst(null) + + invokevirtual( + thisName, + DO_RESUME_METHOD_NAME, + Type.getMethodDescriptor(AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE, AsmTypes.JAVA_THROWABLE_TYPE), + false + ) +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java index 0c8614cd321..1a3a90d1507 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment; import org.jetbrains.kotlin.codegen.*; import org.jetbrains.kotlin.codegen.context.*; import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; +import org.jetbrains.kotlin.codegen.coroutines.SuspendFunctionGenerationStrategy; import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; @@ -601,6 +602,14 @@ public class InlineCodegen extends CallGenerator { else if (expression instanceof KtFunctionLiteral) { strategy = new ClosureGenerationStrategy(state, (KtDeclarationWithBody) expression); } + else if (descriptor.isSuspend() && expression instanceof KtFunction) { + strategy = + new SuspendFunctionGenerationStrategy( + state, + CoroutineCodegenUtilKt.unwrapInitialDescriptorForSuspendFunction(descriptor), + (KtFunction) expression + ); + } else { strategy = new FunctionGenerationStrategy.FunctionDefault(state, (KtDeclarationWithBody) expression); } diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt new file mode 100644 index 00000000000..f351ef3a809 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt @@ -0,0 +1,82 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +class Controller { + var log = "" + var resumeIndex = 0 + + suspend fun suspendWithValue(value: T): T = suspendCoroutineOrReturn { continuation -> + log += "suspend($value);" + continuation.resume(value) + SUSPENDED_MARKER + } + + suspend fun suspendWithException(value: String): Unit = suspendCoroutineOrReturn { continuation -> + log += "error($value);" + continuation.resumeWithException(RuntimeException(value)) + SUSPENDED_MARKER + } +} + +fun test(c: suspend Controller.() -> Unit): String { + val controller = Controller() + c.startCoroutine(controller, EmptyContinuation, object: ContinuationDispatcher { + private fun dispatchResume(block: () -> Unit) { + val id = controller.resumeIndex++ + controller.log += "before $id;" + block() + controller.log += "after $id;" + } + + override fun

dispatchResume(data: P, continuation: Continuation

): Boolean { + dispatchResume { + continuation.resume(data) + } + return true + } + + override fun dispatchResumeWithException(exception: Throwable, continuation: Continuation<*>): Boolean { + dispatchResume { + continuation.resumeWithException(exception) + } + return true + } + }) + return controller.log +} + +suspend fun Controller.foo() = suspendWithValue("") + suspendWithValue("O") + +suspend fun Controller.test1() { + val o = foo() + val k = suspendWithValue("K") + log += "$o$k;" +} + +suspend fun Controller.test2() { + try { + foo() + + suspendWithException("OK") + log += "ignore;" + } + catch (e: RuntimeException) { + log += "${e.message};" + } +} + +fun box(): String { + var result = test { + test1() + } + if (result != "before 0;suspend();before 1;suspend(O);before 2;before 3;suspend(K);before 4;OK;before 5;after 5;after 4;after 3;after 2;after 1;after 0;") return "fail1: $result" + + result = test { + test2() + } + if (result != "before 0;suspend();before 1;suspend(O);before 2;before 3;error(OK);before 4;OK;before 5;after 5;after 4;after 3;after 2;after 1;after 0;") return "fail2: $result" + + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/handleException.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/handleException.kt new file mode 100644 index 00000000000..f6eafed0ab0 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/handleException.kt @@ -0,0 +1,112 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +class Controller { + var exception: Throwable? = null + val postponedActions = ArrayList<() -> Unit>() + + suspend fun suspendWithValue(v: String): String = suspendCoroutineOrReturn { x -> + postponedActions.add { + x.resume(v) + } + + SUSPENDED_MARKER + } + + suspend fun suspendWithException(e: Exception): String = suspendCoroutineOrReturn { x -> + postponedActions.add { + x.resumeWithException(e) + } + + SUSPENDED_MARKER + } + + fun run(c: suspend Controller.() -> Unit) { + c.startCoroutine(this, handleExceptionContinuation { + exception = it + }) + while (postponedActions.isNotEmpty()) { + postponedActions[0]() + postponedActions.removeAt(0) + } + } +} + +fun builder(c: suspend Controller.() -> Unit) { + val controller = Controller() + controller.run(c) + + if (controller.exception?.message != "OK") { + throw RuntimeException("Unexpected result: ${controller.exception?.message}") + } +} + +fun commonThrow(t: Throwable) { + throw t +} + +suspend fun justContinue(): Unit = suspendCoroutineOrReturn { x -> + x.resume(Unit) + + SUSPENDED_MARKER +} + +suspend fun Controller.test1() { + justContinue() + throw RuntimeException("OK") +} + +suspend fun Controller.test2() { + justContinue() + commonThrow(RuntimeException("OK")) +} + +suspend fun Controller.test3() { + justContinue() + suspendWithException(RuntimeException("OK")) +} + +suspend fun Controller.test4() { + justContinue() + try { + suspendWithException(RuntimeException("fail 1")) + } catch (e: RuntimeException) { + suspendWithException(RuntimeException("OK")) + } +} + +suspend fun Controller.test5() { + justContinue() + try { + suspendWithException(Exception("OK")) + } catch (e: RuntimeException) { + suspendWithException(RuntimeException("fail 3")) + throw RuntimeException("fail 4") + } +} + +fun box(): String { + builder { + test1() + } + + builder { + test2() + } + + builder { + test3() + } + + builder { + test4() + } + + builder { + test5() + } + + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/inline.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/inline.kt new file mode 100644 index 00000000000..5d25fb72f3a --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/inline.kt @@ -0,0 +1,32 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +suspend fun suspendThere(v: String): String = suspendCoroutineOrReturn { x -> + x.resume(v) + SUSPENDED_MARKER +} + +suspend inline fun suspendHere(crossinline block: () -> String): String { + return suspendThere(block()) + suspendThere(block()) +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + var q = "O" + result = suspendHere { + val r = q + q = "K" + r + } + } + + return result +} diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/member.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/member.kt new file mode 100644 index 00000000000..169f5f89309 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/member.kt @@ -0,0 +1,27 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +class A(val v: String) { + suspend fun suspendThere(v: String): String = suspendCoroutineOrReturn { x -> + x.resume(v) + SUSPENDED_MARKER + } + + suspend fun suspendHere(): String = suspendThere("O") + suspendThere(v) +} + +fun builder(c: suspend A.() -> Unit) { + c.startCoroutine(A("K"), EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + result = suspendHere() + } + + return result +} diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/returnNoSuspend.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/returnNoSuspend.kt new file mode 100644 index 00000000000..1ffb2eb49ac --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/returnNoSuspend.kt @@ -0,0 +1,28 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +suspend fun suspendThere(v: String): String = suspendCoroutineOrReturn { x -> + x.resume(v) + SUSPENDED_MARKER +} + +suspend fun suspendHere(suspend: Boolean): String { + if (!suspend) return "O" + + return suspendThere("") + suspendThere("K") +} +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + result = suspendHere(false) + suspendHere(true) + } + + return result +} diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/simple.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/simple.kt new file mode 100644 index 00000000000..367fff90589 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/simple.kt @@ -0,0 +1,25 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +suspend fun suspendThere(v: String): String = suspendCoroutineOrReturn { x -> + x.resume(v) + SUSPENDED_MARKER +} + +suspend fun suspendHere(): String = suspendThere("O") + suspendThere("K") + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + result = suspendHere() + } + + return result +} diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/withVariables.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/withVariables.kt new file mode 100644 index 00000000000..ebe64c061b7 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/withVariables.kt @@ -0,0 +1,31 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +suspend fun suspendThere(v: String): String = suspendCoroutineOrReturn { x -> + x.resume(v) + SUSPENDED_MARKER +} + +suspend fun suspendHere(): String { + val k = "K" + val x = suspendThere("O") + val y = x + suspendThere(k) + + return y +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + result = suspendHere() + } + + return result +} diff --git a/compiler/testData/codegen/bytecodeListing/coroutineFields.kt b/compiler/testData/codegen/bytecodeListing/coroutineFields.kt index ae3eba16d9d..ddb99466983 100644 --- a/compiler/testData/codegen/bytecodeListing/coroutineFields.kt +++ b/compiler/testData/codegen/bytecodeListing/coroutineFields.kt @@ -5,6 +5,21 @@ class Controller { suspend fun suspendHere() = suspendCoroutineOrReturn { x -> x.resume("OK") } + + suspend fun tailCall(): String { + return suspendHere() + } + + suspend fun nonTailCall(): String { + suspendHere() + + return "OK" + } + + suspend fun multipleSuspensions(): String { + suspendHere() + return suspendHere() + } } fun builder(c: suspend Controller.() -> Unit) { diff --git a/compiler/testData/codegen/bytecodeListing/coroutineFields.txt b/compiler/testData/codegen/bytecodeListing/coroutineFields.txt index 427d9b499bd..9ff6a69ece4 100644 --- a/compiler/testData/codegen/bytecodeListing/coroutineFields.txt +++ b/compiler/testData/codegen/bytecodeListing/coroutineFields.txt @@ -1,7 +1,26 @@ +final class Controller$multipleSuspensions$1 { + synthetic final field this$0: Controller + inner class Controller$multipleSuspensions$1 + method (p0: Controller, p1: kotlin.coroutines.Continuation): void + public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object +} + +final class Controller$nonTailCall$1 { + synthetic final field this$0: Controller + inner class Controller$nonTailCall$1 + method (p0: Controller, p1: kotlin.coroutines.Continuation): void + public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object +} + @kotlin.Metadata public final class Controller { + inner class Controller$multipleSuspensions$1 + inner class Controller$nonTailCall$1 public method (): void + public final @org.jetbrains.annotations.Nullable method multipleSuspensions(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + public final @org.jetbrains.annotations.Nullable method nonTailCall(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object public final @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + public final @org.jetbrains.annotations.Nullable method tailCall(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object } @kotlin.Metadata @@ -15,7 +34,7 @@ final class CoroutineFieldsKt$box$1 { method (p0: kotlin.jvm.internal.Ref$ObjectRef, p1: kotlin.coroutines.Continuation): void public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: Controller, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation public synthetic method create(p0: java.lang.Object, p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation - protected final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object + public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object public final @org.jetbrains.annotations.Nullable method invoke(@org.jetbrains.annotations.NotNull p0: Controller, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object public synthetic method invoke(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object } diff --git a/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/dispatchResume.txt b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/dispatchResume.txt new file mode 100644 index 00000000000..58319e22771 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/dispatchResume.txt @@ -0,0 +1,35 @@ +@kotlin.Metadata +public final class Controller { + private @org.jetbrains.annotations.NotNull field log: java.lang.String + private field resumeIndex: int + public method (): void + public final @org.jetbrains.annotations.NotNull method getLog(): java.lang.String + public final method getResumeIndex(): int + public final method setLog(@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public final method setResumeIndex(p0: int): void + public final @org.jetbrains.annotations.Nullable method suspendWithException(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final @org.jetbrains.annotations.Nullable method suspendWithValue(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CoroutineUtilKt { + public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation + public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation +} + +@kotlin.Metadata +public final class DispatchResumeKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static @org.jetbrains.annotations.Nullable method foo(@org.jetbrains.annotations.NotNull p0: Controller, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.NotNull method test(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2): java.lang.String + public final static @org.jetbrains.annotations.Nullable method test1(@org.jetbrains.annotations.NotNull p0: Controller, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method test2(@org.jetbrains.annotations.NotNull p0: Controller, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class EmptyContinuation { + public final static field INSTANCE: EmptyContinuation + private method (): void + public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void +} diff --git a/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/handleException.txt b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/handleException.txt new file mode 100644 index 00000000000..38a218440ec --- /dev/null +++ b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/handleException.txt @@ -0,0 +1,39 @@ +@kotlin.Metadata +public final class Controller { + private @org.jetbrains.annotations.Nullable field exception: java.lang.Throwable + private final @org.jetbrains.annotations.NotNull field postponedActions: java.util.ArrayList + public method (): void + public final @org.jetbrains.annotations.Nullable method getException(): java.lang.Throwable + public final @org.jetbrains.annotations.NotNull method getPostponedActions(): java.util.ArrayList + public final method run(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2): void + public final method setException(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void + public final @org.jetbrains.annotations.Nullable method suspendWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Exception, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final @org.jetbrains.annotations.Nullable method suspendWithValue(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CoroutineUtilKt { + public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation + public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation +} + +@kotlin.Metadata +public final class EmptyContinuation { + public final static field INSTANCE: EmptyContinuation + private method (): void + public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void +} + +@kotlin.Metadata +public final class HandleExceptionKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2): void + public final static method commonThrow(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void + public final static @org.jetbrains.annotations.Nullable method justContinue(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method test1(@org.jetbrains.annotations.NotNull p0: Controller, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method test2(@org.jetbrains.annotations.NotNull p0: Controller, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method test3(@org.jetbrains.annotations.NotNull p0: Controller, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method test4(@org.jetbrains.annotations.NotNull p0: Controller, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method test5(@org.jetbrains.annotations.NotNull p0: Controller, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} diff --git a/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/inline.txt b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/inline.txt new file mode 100644 index 00000000000..0a1fbf62b9d --- /dev/null +++ b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/inline.txt @@ -0,0 +1,21 @@ +@kotlin.Metadata +public final class CoroutineUtilKt { + public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation + public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation +} + +@kotlin.Metadata +public final class EmptyContinuation { + public final static field INSTANCE: EmptyContinuation + private method (): void + public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void +} + +@kotlin.Metadata +public final class InlineKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void + public final static @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function0, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} diff --git a/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/member.txt b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/member.txt new file mode 100644 index 00000000000..4dfb1c0d49a --- /dev/null +++ b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/member.txt @@ -0,0 +1,28 @@ +@kotlin.Metadata +public final class A { + private final @org.jetbrains.annotations.NotNull field v: java.lang.String + public method (@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public final @org.jetbrains.annotations.NotNull method getV(): java.lang.String + public final @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + public final @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CoroutineUtilKt { + public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation + public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation +} + +@kotlin.Metadata +public final class EmptyContinuation { + public final static field INSTANCE: EmptyContinuation + private method (): void + public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void +} + +@kotlin.Metadata +public final class MemberKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2): void +} diff --git a/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/returnNoSuspend.txt b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/returnNoSuspend.txt new file mode 100644 index 00000000000..8a27d6ae494 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/returnNoSuspend.txt @@ -0,0 +1,21 @@ +@kotlin.Metadata +public final class CoroutineUtilKt { + public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation + public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation +} + +@kotlin.Metadata +public final class EmptyContinuation { + public final static field INSTANCE: EmptyContinuation + private method (): void + public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void +} + +@kotlin.Metadata +public final class ReturnNoSuspendKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void + public final static @org.jetbrains.annotations.Nullable method suspendHere(p0: boolean, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} diff --git a/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/simple.txt b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/simple.txt new file mode 100644 index 00000000000..741cb521d36 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/simple.txt @@ -0,0 +1,21 @@ +@kotlin.Metadata +public final class CoroutineUtilKt { + public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation + public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation +} + +@kotlin.Metadata +public final class EmptyContinuation { + public final static field INSTANCE: EmptyContinuation + private method (): void + public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void +} + +@kotlin.Metadata +public final class SimpleKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void + public final static @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} diff --git a/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/withVariables.txt b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/withVariables.txt new file mode 100644 index 00000000000..e0edab0c303 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/withVariables.txt @@ -0,0 +1,21 @@ +@kotlin.Metadata +public final class CoroutineUtilKt { + public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation + public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation +} + +@kotlin.Metadata +public final class EmptyContinuation { + public final static field INSTANCE: EmptyContinuation + private method (): void + public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void +} + +@kotlin.Metadata +public final class WithVariablesKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void + public final static @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} 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 6e775e7e0e3..2aefa743859 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 @@ -5057,6 +5057,57 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } } + @TestMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SuspendFunctionAsCoroutine extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInSuspendFunctionAsCoroutine() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("dispatchResume.kt") + public void testDispatchResume() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt"); + doTest(fileName); + } + + @TestMetadata("handleException.kt") + public void testHandleException() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/handleException.kt"); + doTest(fileName); + } + + @TestMetadata("inline.kt") + public void testInline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/inline.kt"); + doTest(fileName); + } + + @TestMetadata("member.kt") + public void testMember() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/member.kt"); + doTest(fileName); + } + + @TestMetadata("returnNoSuspend.kt") + public void testReturnNoSuspend() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/returnNoSuspend.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/simple.kt"); + doTest(fileName); + } + + @TestMetadata("withVariables.kt") + public void testWithVariables() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/withVariables.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 031ad5bb05a..05cac935864 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -5057,6 +5057,57 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SuspendFunctionAsCoroutine extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInSuspendFunctionAsCoroutine() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("dispatchResume.kt") + public void testDispatchResume() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt"); + doTest(fileName); + } + + @TestMetadata("handleException.kt") + public void testHandleException() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/handleException.kt"); + doTest(fileName); + } + + @TestMetadata("inline.kt") + public void testInline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/inline.kt"); + doTest(fileName); + } + + @TestMetadata("member.kt") + public void testMember() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/member.kt"); + doTest(fileName); + } + + @TestMetadata("returnNoSuspend.kt") + public void testReturnNoSuspend() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/returnNoSuspend.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/simple.kt"); + doTest(fileName); + } + + @TestMetadata("withVariables.kt") + public void testWithVariables() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/withVariables.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java index afd471fed7b..132567ade69 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java @@ -5057,6 +5057,57 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis } } + @TestMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SuspendFunctionAsCoroutine extends AbstractLightAnalysisModeCodegenTest { + public void testAllFilesPresentInSuspendFunctionAsCoroutine() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("dispatchResume.kt") + public void testDispatchResume() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt"); + doTest(fileName); + } + + @TestMetadata("handleException.kt") + public void testHandleException() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/handleException.kt"); + doTest(fileName); + } + + @TestMetadata("inline.kt") + public void testInline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/inline.kt"); + doTest(fileName); + } + + @TestMetadata("member.kt") + public void testMember() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/member.kt"); + doTest(fileName); + } + + @TestMetadata("returnNoSuspend.kt") + public void testReturnNoSuspend() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/returnNoSuspend.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/simple.kt"); + doTest(fileName); + } + + @TestMetadata("withVariables.kt") + public void testWithVariables() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/withVariables.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 5c0f0de466f..c654e4bca7e 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 @@ -5784,6 +5784,57 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SuspendFunctionAsCoroutine extends AbstractJsCodegenBoxTest { + public void testAllFilesPresentInSuspendFunctionAsCoroutine() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("dispatchResume.kt") + public void testDispatchResume() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt"); + doTest(fileName); + } + + @TestMetadata("handleException.kt") + public void testHandleException() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/handleException.kt"); + doTest(fileName); + } + + @TestMetadata("inline.kt") + public void testInline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/inline.kt"); + doTest(fileName); + } + + @TestMetadata("member.kt") + public void testMember() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/member.kt"); + doTest(fileName); + } + + @TestMetadata("returnNoSuspend.kt") + public void testReturnNoSuspend() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/returnNoSuspend.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/simple.kt"); + doTest(fileName); + } + + @TestMetadata("withVariables.kt") + public void testWithVariables() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/withVariables.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)