Support non-tail suspend calls in JVM back-end
#KT-15597 In Progress
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
+50
-14
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
+52
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
-4
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user