Support non-tail suspend calls in JVM back-end

#KT-15597 In Progress
This commit is contained in:
Denis Zharkov
2017-01-11 16:19:38 +03:00
parent a048dde7a5
commit 0240ab0738
33 changed files with 1065 additions and 82 deletions
@@ -198,7 +198,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
);
}
private void generateBridges() {
protected void generateBridges() {
FunctionDescriptor erasedInterfaceFunction;
if (samType == null) {
erasedInterfaceFunction = getErasedInvokeFunction(funDescriptor);
@@ -233,7 +233,10 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
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 =
@@ -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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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);
@@ -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.<FunctionDescriptor>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(),
@@ -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) {
@@ -70,7 +70,7 @@ class JvmRuntimeTypes(module: ModuleDescriptor) {
fun getSupertypesForClosure(descriptor: FunctionDescriptor): Collection<KotlinType> {
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<KotlinType>().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)
}
}
}
@@ -44,5 +44,7 @@ public interface CalculatedClosure {
@NotNull
List<Pair<String, Type>> getRecordedFields();
boolean isCoroutine();
boolean isSuspend();
boolean isSuspendLambda();
}
@@ -100,10 +100,21 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
@NotNull CallableDescriptor callableDescriptor,
@NotNull Collection<KotlinType> supertypes,
@NotNull String name
) {
return recordClassForCallable(element, callableDescriptor, supertypes, name, null);
}
@NotNull
private ClassDescriptor recordClassForCallable(
@NotNull KtElement element,
@NotNull CallableDescriptor callableDescriptor,
@NotNull Collection<KotlinType> 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("<closure-" + simpleName + ">"),
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<KotlinType> 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<KotlinType> 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();
@@ -66,7 +66,7 @@ public class CodegenBinding {
public static final WritableSlice<VariableDescriptor, VariableDescriptor> LOCAL_VARIABLE_PROPERTY_METADATA =
Slices.createSimpleSlice();
public static final WritableSlice<SimpleFunctionDescriptor, SimpleFunctionDescriptor> SUSPEND_FUNCTION_TO_JVM_VIEW =
public static final WritableSlice<FunctionDescriptor, FunctionDescriptor> SUSPEND_FUNCTION_TO_JVM_VIEW =
Slices.createSimpleSlice();
public static final WritableSlice<ValueParameterDescriptor, ValueParameterDescriptor> PARAMETER_SYNONYM =
@@ -40,7 +40,8 @@ public final class MutableClosure implements CalculatedClosure {
private Map<DeclarationDescriptor, Integer> parameterOffsetInConstructor;
private List<Pair<String, Type>> 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) {
@@ -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")
}
}
@@ -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)
}
}
}
@@ -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<FunctionDescriptor>()?.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<return-type>`,
// 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 : CallableDescriptor?> D.unwrapInitialDescriptorForSuspendFunction(): D =
this.safeAs<SimpleFunctionDescriptor>()?.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
)
}
@@ -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.<FunctionDescriptor>unwrapInitialDescriptorForSuspendFunction(descriptor),
(KtFunction) expression
);
}
else {
strategy = new FunctionGenerationStrategy.FunctionDefault(state, (KtDeclarationWithBody) expression);
}
@@ -0,0 +1,82 @@
// WITH_RUNTIME
// WITH_COROUTINES
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
class Controller {
var log = ""
var resumeIndex = 0
suspend fun <T> 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 <P> dispatchResume(data: P, continuation: Continuation<P>): 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"
}
@@ -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"
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -5,6 +5,21 @@ class Controller {
suspend fun suspendHere() = suspendCoroutineOrReturn<String> { 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) {
@@ -1,7 +1,26 @@
final class Controller$multipleSuspensions$1 {
synthetic final field this$0: Controller
inner class Controller$multipleSuspensions$1
method <init>(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 <init>(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 <init>(): 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 <init>(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
}
@@ -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 <init>(): 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 <init>(): 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
}
@@ -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 <init>(): 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 <init>(): 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
}
@@ -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 <init>(): 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
}
@@ -0,0 +1,28 @@
@kotlin.Metadata
public final class A {
private final @org.jetbrains.annotations.NotNull field v: java.lang.String
public method <init>(@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 <init>(): 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
}
@@ -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 <init>(): 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
}
@@ -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 <init>(): 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
}
@@ -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 <init>(): 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
}
@@ -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)
@@ -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)
@@ -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)
@@ -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)