Support local suspend functions

Treat them like lambdas. This means:
1) Like local suspend lambdas, which become "non-suspend" after creating
jvmSuspendView, they do this as well
2) They both are generated with the same codegen.
3) They are treated differently only on front-end side.
 #KT-20364: Fixed
This commit is contained in:
Ilmir Usmanov
2018-04-02 21:50:56 +03:00
parent 5a752da9e9
commit bd0ad26dcf
29 changed files with 797 additions and 29 deletions
@@ -23,9 +23,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
import org.jetbrains.kotlin.codegen.context.*;
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenForLambda;
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
import org.jetbrains.kotlin.codegen.coroutines.ResolvedCallWithRealDescriptor;
import org.jetbrains.kotlin.codegen.coroutines.*;
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension;
import org.jetbrains.kotlin.codegen.inline.*;
import org.jetbrains.kotlin.codegen.intrinsics.*;
@@ -1238,7 +1236,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@Nullable
public StackValue genCoroutineInstanceForSuspendLambda(@NotNull FunctionDescriptor suspendFunction) {
if (!(suspendFunction instanceof AnonymousFunctionDescriptor)) return null;
if (!CoroutineCodegenUtilKt.isSuspendLambdaOrLocalFunction(suspendFunction)) return null;
ClassDescriptor suspendLambdaClassDescriptor = bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, suspendFunction);
assert suspendLambdaClassDescriptor != null : "Coroutine class descriptor should not be null";
@@ -15,6 +15,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.builtins.PrimitiveType;
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
@@ -704,6 +705,18 @@ public abstract class StackValue {
return codegen.generateReceiverValue(receiverValue, false);
}
else if (isLocalFunCall(callableMethod) && !isExtension) {
if (descriptor instanceof SimpleFunctionDescriptor) {
SimpleFunctionDescriptor initial =
CoroutineCodegenUtilKt.unwrapInitialDescriptorForSuspendFunction((SimpleFunctionDescriptor) descriptor);
if (initial != null && initial.isSuspend()) {
StackValue value = codegen.findLocalOrCapturedValue(initial.getOriginal());
assert value != null : "Local suspend fun should be found in locals or in captured params: " +
descriptor +
" initial local suspend fun: " +
initial;
return value;
}
}
StackValue value = codegen.findLocalOrCapturedValue(descriptor.getOriginal());
assert value != null : "Local fun should be found in locals or in captured params: " + descriptor;
return value;
@@ -506,7 +506,8 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
String nameForClassOrPackageMember = getNameForClassOrPackageMember(functionDescriptor);
if (functionDescriptor instanceof SimpleFunctionDescriptor && functionDescriptor.isSuspend()) {
if (functionDescriptor instanceof SimpleFunctionDescriptor && functionDescriptor.isSuspend() &&
!functionDescriptor.getVisibility().equals(Visibilities.LOCAL)) {
SimpleFunctionDescriptor jvmSuspendFunctionView =
CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
(SimpleFunctionDescriptor) functionDescriptor
@@ -563,11 +564,29 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
else {
String name = inventAnonymousClassName();
ClassDescriptor classDescriptor = recordClassForFunction(function, functionDescriptor, name, null);
recordClosure(classDescriptor, name);
MutableClosure closure = recordClosure(classDescriptor, name);
classStack.push(classDescriptor);
functionsStack.push(functionDescriptor);
nameStack.push(name);
if (functionDescriptor instanceof SimpleFunctionDescriptor && functionDescriptor.isSuspend()) {
SimpleFunctionDescriptor jvmSuspendFunctionView =
CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
(SimpleFunctionDescriptor) functionDescriptor,
/*bindingContext*/ null,
/*dropSuspend*/ true
);
bindingTrace.record(
CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW,
functionDescriptor,
jvmSuspendFunctionView
);
closure.setSuspend(true);
closure.setSuspendLambda();
}
functionsStack.push(functionDescriptor);
super.visitNamedFunction(function);
functionsStack.pop();
nameStack.pop();
@@ -20,10 +20,7 @@ import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
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.psi.*
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.module
@@ -330,7 +327,7 @@ class CoroutineCodegenForLambda private constructor(
declaration: KtElement,
classBuilder: ClassBuilder
): ClosureCodegen? {
if (declaration !is KtFunctionLiteral || !originalSuspendLambdaDescriptor.isSuspendLambda) return null
if (!originalSuspendLambdaDescriptor.isSuspendLambdaOrLocalFunction()) return null
return CoroutineCodegenForLambda(
expressionCodegen,
@@ -26,8 +26,10 @@ import org.jetbrains.kotlin.codegen.inline.addFakeContinuationMarker
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.codegen.topLevelClassAsmType
import org.jetbrains.kotlin.codegen.topLevelClassInternalName
import org.jetbrains.kotlin.coroutines.isSuspendLambda
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name
@@ -182,7 +184,7 @@ fun ResolvedCall<*>.isSuspendNoInlineCall(codegen: ExpressionCodegen): Boolean {
?.let { it.isCrossinline || (!it.isNoinline && codegen.context.functionDescriptor.isInline) } == true
val functionDescriptor = resultingDescriptor as? FunctionDescriptor ?: return false
if (!functionDescriptor.isSuspend) return false
if (!functionDescriptor.unwrapInitialDescriptorForSuspendFunction().isSuspend) return false
if (functionDescriptor.isBuiltInSuspendCoroutineOrReturnInJvm() || functionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm()) return true
return !(functionDescriptor.isInline || isInlineLambda)
}
@@ -196,7 +198,11 @@ fun CallableDescriptor.isSuspendFunctionNotSuspensionView(): Boolean {
// and return type Any?
// This function returns a function descriptor reflecting how the suspend function looks from point of view of JVM
@JvmOverloads
fun <D : FunctionDescriptor> getOrCreateJvmSuspendFunctionView(function: D, bindingContext: BindingContext? = null): D {
fun <D : FunctionDescriptor> getOrCreateJvmSuspendFunctionView(
function: D,
bindingContext: BindingContext? = null,
dropSuspend: Boolean = false
): D {
assert(function.isSuspend) {
"Suspended function is expected, but $function was found"
}
@@ -220,6 +226,9 @@ fun <D : FunctionDescriptor> getOrCreateJvmSuspendFunctionView(function: D, bind
setPreserveSourceElement()
setReturnType(function.builtIns.nullableAnyType)
setValueParameters(it.valueParameters + continuationParameter)
if (dropSuspend) {
setDropSuspend()
}
putUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION, it)
}
}
@@ -433,3 +442,9 @@ fun InstructionAdapter.invokeDoResumeWithUnit(thisName: String) {
fun Method.getImplForOpenMethod(ownerInternalName: String) =
Method("$name\$suspendImpl", returnType, arrayOf(Type.getObjectType(ownerInternalName)) + argumentTypes)
fun FunctionDescriptor.isSuspendLambdaOrLocalFunction() = this.isSuspend && when (this) {
is AnonymousFunctionDescriptor -> this.isSuspendLambda
is SimpleFunctionDescriptor -> this.visibility == Visibilities.LOCAL
else -> false
}
@@ -170,7 +170,8 @@ public class KotlinTypeMapper {
@NotNull
private Type mapOwner(@NotNull DeclarationDescriptor descriptor, boolean publicFacade) {
if (isLocalFunction(descriptor)) {
return asmTypeForAnonymousClass(bindingContext, (FunctionDescriptor) descriptor);
return asmTypeForAnonymousClass(bindingContext,
CoroutineCodegenUtilKt.unwrapInitialDescriptorForSuspendFunction((FunctionDescriptor) descriptor));
}
if (descriptor instanceof ConstructorDescriptor) {