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);
}