From 5ff71e13300133962f5edea0031de8b17bb50ff0 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 22 Nov 2016 11:44:10 +0300 Subject: [PATCH] Support 'interceptResume' operator in JVM backend #KT-14891 In Progress --- .../kotlin/codegen/JvmRuntimeTypes.kt | 21 +- .../binding/CodegenAnnotatingVisitor.java | 34 ++- .../codegen/binding/CodegenBinding.java | 5 + .../codegen/coroutines/CoroutineCodegen.kt | 199 ++++++++++++++++-- .../CoroutineTransformationClassBuilder.kt | 15 +- .../coroutines/coroutineCodegenUtil.kt | 40 +++- .../kotlin/codegen/inline/InlineCodegen.java | 6 +- .../kotlin/codegen/inline/LambdaInfo.java | 7 +- 8 files changed, 295 insertions(+), 32 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt index 611860cd55b..398b7014532 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt @@ -16,23 +16,23 @@ package org.jetbrains.kotlin.codegen +import org.jetbrains.kotlin.codegen.coroutines.hasNoinlineInterceptResume import org.jetbrains.kotlin.coroutines.controllerTypeIfCoroutine import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor -import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.util.createFunctionType import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns -import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeConstructorSubstitution import org.jetbrains.kotlin.types.TypeProjectionImpl import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils +import org.jetbrains.kotlin.utils.singletonOrEmptyList class JvmRuntimeTypes(module: ModuleDescriptor) { private val kotlinJvmInternalPackage = MutablePackageFragmentDescriptor(module, FqName("kotlin.jvm.internal")) @@ -86,7 +86,22 @@ class JvmRuntimeTypes(module: ModuleDescriptor) { val coroutineControllerType = descriptor.controllerTypeIfCoroutine if (coroutineControllerType != null) { - return listOf(coroutineImplClass.defaultType, functionType) + val additionalType: KotlinType? + if (coroutineControllerType.hasNoinlineInterceptResume()) { + // for non-inline interceptResume we use coroutine instance as an argument for interceptRun call, i.e. it must be a Function0 + // See org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegen.processInterceptResume() for details + additionalType = + createFunctionType( + descriptor.builtIns, Annotations.EMPTY, + /* recieverParameter = */ null, /* parameterTypes = */ emptyList(), /* parameterNames = */ emptyList(), + /* returnType = */ descriptor.builtIns.unitType + ) + } + else { + additionalType = null + } + + return listOf(coroutineImplClass.defaultType, functionType) + additionalType.singletonOrEmptyList() } return listOf(lambda.defaultType, functionType) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java index 5285be75a4c..5d93e68143d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java @@ -26,6 +26,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.ReflectionTypes; import org.jetbrains.kotlin.cfg.WhenChecker; import org.jetbrains.kotlin.codegen.*; +import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt; import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil; @@ -33,6 +34,7 @@ import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping; import org.jetbrains.kotlin.coroutines.CoroutineUtilKt; 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.LocalVariableDescriptor; import org.jetbrains.kotlin.fileClasses.FileClasses; import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider; @@ -273,12 +275,36 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { ClassDescriptor classDescriptor = recordClassForCallable(functionLiteral, functionDescriptor, supertypes, name); MutableClosure closure = recordClosure(classDescriptor, name); - if (CoroutineUtilKt.getControllerTypeIfCoroutine(functionDescriptor) != null) { - closure.setCoroutine(true); - } - classStack.push(classDescriptor); nameStack.push(name); + + KotlinType controllerTypeIfCoroutine = CoroutineUtilKt.getControllerTypeIfCoroutine(functionDescriptor); + if (controllerTypeIfCoroutine != null) { + closure.setCoroutine(true); + + if (CoroutineCodegenUtilKt.hasInlineInterceptResume(controllerTypeIfCoroutine)) { + // for inline interceptResume we create a descriptor for fake lambda that must be inlined when generating interceptRun call + // See org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegen.processInterceptResume() for details + AnonymousFunctionDescriptor fakeDescriptorForInlineLambda = + new AnonymousFunctionDescriptor(functionDescriptor, Annotations.Companion.getEMPTY(), + CallableMemberDescriptor.Kind.DECLARATION, SourceElement.NO_SOURCE, false + ); + + fakeDescriptorForInlineLambda.initialize( + null, null, + Collections.emptyList(), + Collections.emptyList(), + DescriptorUtilsKt.getBuiltIns(functionDescriptor).getUnitType(), + Modality.FINAL, Visibilities.PUBLIC + ); + + bindingTrace.record(CUSTOM_DESCRIPTOR_FOR_INLINE_LAMBDA, functionLiteral, fakeDescriptorForInlineLambda); + + recordClosure(recordClassForCallable(functionLiteral, fakeDescriptorForInlineLambda, supertypes, name), + inventAnonymousClassName()); + } + } + super.visitLambdaExpression(lambdaExpression); nameStack.pop(); classStack.pop(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java index e16f9884b58..ad2cb439779 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.binding; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.codegen.FunctionGenerationStrategy; import org.jetbrains.kotlin.codegen.JvmCodegenUtil; import org.jetbrains.kotlin.codegen.SamType; import org.jetbrains.kotlin.codegen.state.GenerationState; @@ -66,6 +67,10 @@ public class CodegenBinding { public static final WritableSlice LOCAL_VARIABLE_PROPERTY_METADATA = Slices.createSimpleSlice(); + + public static final WritableSlice CUSTOM_STRATEGY_FOR_INLINE_LAMBDA = Slices.createSimpleSlice(); + public static final WritableSlice CUSTOM_DESCRIPTOR_FOR_INLINE_LAMBDA = Slices.createSimpleSlice(); + static { BasicWritableSlice.initSliceDebugNames(CodegenBinding.class); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt index b0333d0d2f4..a48c42fd83a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.codegen.coroutines import org.jetbrains.kotlin.codegen.* +import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.context.ClosureContext import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.coroutines.controllerTypeIfCoroutine @@ -24,11 +25,12 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl -import org.jetbrains.kotlin.incremental.KotlinLookupLocation +import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtDeclarationWithBody import org.jetbrains.kotlin.psi.KtElement 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 @@ -166,10 +168,7 @@ class CoroutineCodegen( ) private fun generateExceptionHandlingBlock(codegen: ExpressionCodegen) { - val handleExceptionFunction = - controllerType.memberScope.getContributedFunctions( - OperatorNameConventions.COROUTINE_HANDLE_EXCEPTION, KotlinLookupLocation(element)).singleOrNull { it.isOperator } - ?: return + val handleExceptionFunction = findOperatorInController(controllerType, OperatorNameConventions.COROUTINE_HANDLE_EXCEPTION) ?: return val (resolvedCall, fakeExceptionExpression, fakeThisContinuationException) = createResolvedCallForHandleExceptionCall(element, handleExceptionFunction, coroutineLambdaDescriptor) @@ -219,18 +218,184 @@ class CoroutineCodegen( Visibilities.PROTECTED ) } - - functionCodegen.generateMethod(OtherOrigin(element), doResumeDescriptor, - object : FunctionGenerationStrategy.FunctionDefault(state, element as KtDeclarationWithBody) { - override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { - codegen.v.visitAnnotation(CONTINUATION_METHOD_ANNOTATION_DESC, true).visitEnd() - codegen.initializeCoroutineParameters() - super.doGenerateBody(codegen, signature) - generateExceptionHandlingBlock(codegen) - } - }) + val interceptResume = findOperatorInController(controllerType, OperatorNameConventions.COROUTINE_INTERCEPT_RESUME) + if (interceptResume != null) { + processInterceptResume(doResumeDescriptor, interceptResume) + } + else { + functionCodegen.generateMethod( + OtherOrigin(element), + doResumeDescriptor, + object : FunctionGenerationStrategy.FunctionDefault(state, element as KtDeclarationWithBody) { + override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { + codegen.v.visitAnnotation(CONTINUATION_METHOD_ANNOTATION_DESC, true).visitEnd() + codegen.initializeCoroutineParameters() + super.doGenerateBody(codegen, signature) + generateExceptionHandlingBlock(codegen) + } + } + ) + } } + /** + * If there is defined an interceptResume operator in the controller class (it must have 'fun interceptResume(x: () -> Unit): Unit' signature), + * then continuation resume* operations must behave as they defined like `fun resume(x: T) = controller.interceptResume { doResume(x) }`. + * + * We do it the following way, our doResume implementation is defined as (at least for noinline case): + * fun doResume(v: Any?, t: Throwable?) { + * this.$v = v + * this.$t = t + * controller.interceptResume(this) + * } + * + * override fun invoke() { + * val v = this.$v + * val t = this.$t + * .. // common continuation state machine encoding + * } + * + * And yes, it means that `this`-object (i.e. a Continuation instance) also implements Function0, along with + * FunctionK. + * There are two kinds of interceptResume: + * 1. When the defined as not inline. In that case a continuation object is also implements `Function0` (literally in class-file) and defines + * an `invoke()Lj/l/Object` bridge. + * 2. Otherwise, it's defined as inline and we generate interceptResume call in the following way: + * controller.interceptResume { this.invoke() } + * + * The subtle difference with noinline case is that `this.invoke()` call is devirtualized after inlining + * (so there's no need to literally implement `Function0` nor to generate the bridges) + * + * Note that in the second case there must be created a fake lambda descriptor (and it's done via CodegenAnnotatingVisitor) + */ + private fun processInterceptResume(doResumeDescriptor: SimpleFunctionDescriptor, interceptResume: SimpleFunctionDescriptor) { + val (interceptResumeResolvedCall, lambdaExpressionForInterceptResume) = + createResolvedCallForInterceptResume(element as KtFunctionLiteral, interceptResume, coroutineLambdaDescriptor) + + val fieldInfoForValue = + createHiddenFieldInfo(funDescriptor.builtIns.anyType, COROUTINE_VALUE_FIELD_NAME_FOR_INTERCEPT_RESUME) + + val fieldInfoForThrowable = + createHiddenFieldInfo(funDescriptor.builtIns.throwable.defaultType, COROUTINE_THROWABLE_FIELD_NAME_FOR_INTERCEPT_RESUME) + + v.newField( + JvmDeclarationOrigin.NO_ORIGIN, + Opcodes.ACC_PRIVATE, + fieldInfoForValue.fieldName, + fieldInfoForValue.fieldType.descriptor, null, null) + + v.newField( + JvmDeclarationOrigin.NO_ORIGIN, + Opcodes.ACC_PRIVATE, + fieldInfoForThrowable.fieldName, + fieldInfoForThrowable.fieldType.descriptor, null, null) + + val classDescriptor = closureContext.contextDescriptor + val invokeDescriptor = + SimpleFunctionDescriptorImpl.create( + classDescriptor, Annotations.EMPTY, Name.identifier("invoke"), CallableMemberDescriptor.Kind.DECLARATION, + funDescriptor.source + ).apply { + initialize( + /* receiverParameterType = */ null, classDescriptor.thisAsReceiverParameter, + /* typeParameters = */ emptyList(), emptyList(), + module.builtIns.unitType, + Modality.FINAL, + Visibilities.PUBLIC + ) + + if (!interceptResume.isInline) { + // for generating necessary bridges + overriddenDescriptors = + funDescriptor.builtIns.getFunction(0) + .defaultType.memberScope.getContributedFunctions(Name.identifier("invoke"), + NoLookupLocation.FROM_BACKEND + ) + } + } + + if (interceptResume.isInline) { + state.bindingTrace.record( + CodegenBinding.CUSTOM_STRATEGY_FOR_INLINE_LAMBDA, element, + object : FunctionGenerationStrategy.FunctionDefault(state, element as KtDeclarationWithBody) { + override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { + // ? + val label = Label() + codegen.v.visitLineNumber(1, label) + codegen.v.visitLabel(label) + + codegen.generateThisOrOuter(context.thisDescriptor, false) + .put(Type.getObjectType(this@CoroutineCodegen.v.thisName), codegen.v) + codegen.v.invokevirtual(this@CoroutineCodegen.v.thisName, "invoke", "()V", false) + codegen.v.areturn(Type.VOID_TYPE) + } + } + ) + } + + functionCodegen.generateMethod( + OtherOrigin(element), + invokeDescriptor, + object : FunctionGenerationStrategy.FunctionDefault(state, element) { + override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { + codegen.v.visitAnnotation(CONTINUATION_METHOD_ANNOTATION_DESC, true).visitEnd() + + assert(codegen.frameMap.enterTemp(AsmTypes.OBJECT_TYPE) == COROUTINE_VALUE_PARAMETER_SLOT_IN_DO_RESUME) { + "Next free slot must be $COROUTINE_VALUE_PARAMETER_SLOT_IN_DO_RESUME" + } + assert(codegen.frameMap.enterTemp(AsmTypes.JAVA_THROWABLE_TYPE) == COROUTINE_THROWABLE_PARAMETER_SLOT_IN_DO_RESUME) { + "Next free slot must be $COROUTINE_THROWABLE_PARAMETER_SLOT_IN_DO_RESUME" + } + + codegen.generateLoadField(fieldInfoForValue) + codegen.v.store(COROUTINE_VALUE_PARAMETER_SLOT_IN_DO_RESUME, AsmTypes.OBJECT_TYPE) + + codegen.generateLoadField(fieldInfoForThrowable) + codegen.v.store(COROUTINE_THROWABLE_PARAMETER_SLOT_IN_DO_RESUME, AsmTypes.JAVA_THROWABLE_TYPE) + + codegen.v.invokestatic( + COROUTINE_MARKER_OWNER, ACTUAL_COROUTINE_START_MARKER_NAME, "()V", false + ) + + codegen.initializeCoroutineParameters() + super.doGenerateBody(codegen, signature) + generateExceptionHandlingBlock(codegen) + codegen.v.areturn(Type.VOID_TYPE) + } + } + ) + + functionCodegen.generateMethod( + OtherOrigin(element), + doResumeDescriptor, + object : FunctionGenerationStrategy.CodegenBased(state) { + override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { + AsmUtil.genAssignInstanceFieldFromParam( + fieldInfoForValue, COROUTINE_VALUE_PARAMETER_SLOT_IN_DO_RESUME, codegen.v + ) + + AsmUtil.genAssignInstanceFieldFromParam( + fieldInfoForThrowable, COROUTINE_THROWABLE_PARAMETER_SLOT_IN_DO_RESUME, codegen.v + ) + + if (!interceptResume.isInline) { + codegen.tempVariables.put( + lambdaExpressionForInterceptResume, + codegen.generateThisOrOuter(context.thisDescriptor, false) + ) + } + + codegen.invokeFunction( + interceptResumeResolvedCall, StackValue.none() + ).put(Type.VOID_TYPE, codegen.v) + + codegen.v.areturn(Type.VOID_TYPE) + } + } + ) + } + + private fun InstructionAdapter.setLabelValue(value: Int) { load(0, AsmTypes.OBJECT_TYPE) iconst(value) @@ -273,3 +438,7 @@ class CoroutineCodegen( } private const val COROUTINE_LAMBDA_PARAMETER_PREFIX = "p$" +private const val COROUTINE_VALUE_FIELD_NAME_FOR_INTERCEPT_RESUME = "v$" +private const val COROUTINE_THROWABLE_FIELD_NAME_FOR_INTERCEPT_RESUME = "throwable$" +private const val COROUTINE_VALUE_PARAMETER_SLOT_IN_DO_RESUME = 1 +private const val COROUTINE_THROWABLE_PARAMETER_SLOT_IN_DO_RESUME = 2 diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformationClassBuilder.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformationClassBuilder.kt index f988eff33aa..d559cf9e1ae 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformationClassBuilder.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformationClassBuilder.kt @@ -65,6 +65,13 @@ class CoroutineTransformerMethodVisitor( if (methodNode.visibleAnnotations?.none { it.desc == CONTINUATION_METHOD_ANNOTATION_DESC } != false) return methodNode.visibleAnnotations.removeAll { it.desc == CONTINUATION_METHOD_ANNOTATION_DESC } + val customCoroutineStartMarker = methodNode.instructions.toArray().filterIsInstance().firstOrNull { + it.owner == COROUTINE_MARKER_OWNER && it.name == ACTUAL_COROUTINE_START_MARKER_NAME + } + + val customCoroutineStart = customCoroutineStartMarker?.next + customCoroutineStartMarker?.let(methodNode.instructions::remove) + val suspensionPoints = collectSuspensionPoints(methodNode) for (suspensionPoint in suspensionPoints) { @@ -72,7 +79,7 @@ class CoroutineTransformerMethodVisitor( } // Add global exception handler - val isThereGlobalExceptionHandler = processHandleExceptionCall(methodNode) + val isThereGlobalExceptionHandler = processHandleExceptionCall(methodNode, customCoroutineStart ?: methodNode.instructions.first) // Spill stack to variables before suspension points, try/catch blocks FixStackWithLabelNormalizationMethodTransformer().transform(classBuilder.thisName, methodNode) @@ -105,7 +112,7 @@ class CoroutineTransformerMethodVisitor( globalExceptionHandler.start.next.next } else - first + customCoroutineStart ?: first // tableswitch(this.label) insertBefore(firstToInsertBefore, insnListOf( @@ -408,7 +415,7 @@ class CoroutineTransformerMethodVisitor( return } - private fun processHandleExceptionCall(methodNode: MethodNode): Boolean { + private fun processHandleExceptionCall(methodNode: MethodNode, coroutineStart: AbstractInsnNode): Boolean { val instructions = methodNode.instructions val marker = instructions.toArray().firstOrNull { it.isHandleExceptionMarker() } ?: return false @@ -418,7 +425,7 @@ class CoroutineTransformerMethodVisitor( val startLabel = LabelNode() val endLabel = LabelNode() - instructions.insertBefore(instructions.first, startLabel) + instructions.insertBefore(coroutineStart, startLabel) instructions.set(marker, endLabel) // NOP is necessary to preserve common invariant: first insn of TCB is always NOP diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt index 3c54010719a..2c0ef1564cf 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt @@ -19,10 +19,9 @@ package org.jetbrains.kotlin.codegen.coroutines import com.intellij.openapi.project.Project import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingTraceContext import org.jetbrains.kotlin.resolve.DelegatingBindingTrace import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument @@ -35,9 +34,11 @@ import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy import org.jetbrains.kotlin.resolve.calls.util.CallMaker import org.jetbrains.kotlin.resolve.coroutine.REPLACED_SUSPENSION_POINT_KEY import org.jetbrains.kotlin.resolve.coroutine.SUSPENSION_POINT_KEY +import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeConstructorSubstitution import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.typeUtil.asTypeProjection +import org.jetbrains.kotlin.util.OperatorNameConventions // These classes do not actually exist at runtime val CONTINUATION_METHOD_ANNOTATION_DESC = "Lkotlin/ContinuationMethod;" @@ -47,6 +48,7 @@ const val BEFORE_SUSPENSION_POINT_MARKER_NAME = "beforeSuspensionPoint" const val AFTER_SUSPENSION_POINT_MARKER_NAME = "afterSuspensionPoint" const val HANDLE_EXCEPTION_MARKER_NAME = "handleException" const val HANDLE_EXCEPTION_ARGUMENT_MARKER_NAME = "handleExceptionArgument" +const val ACTUAL_COROUTINE_START_MARKER_NAME = "actualCoroutineStart" const val COROUTINE_CONTROLLER_FIELD_NAME = "_controller" const val COROUTINE_CONTROLLER_GETTER_NAME = "getController" @@ -157,6 +159,27 @@ private fun createFakeResolvedCall( return resolvedCall } +data class InterceptResumeCallContext( + val resolvedCall: ResolvedCall<*>, + val thisExpression: KtExpression +) + +fun createResolvedCallForInterceptResume( + coroutineLambda: KtFunctionLiteral, + interceptResume: SimpleFunctionDescriptor, + coroutineLambdaDescriptor: FunctionDescriptor +): InterceptResumeCallContext { + val psiFactory = KtPsiFactory(coroutineLambda) + + val isInline = interceptResume.isInline + val thisExpression = psiFactory.createExpression("this") + val blockArgument = CallMaker.makeValueArgument(if (isInline) coroutineLambda.parent as KtExpression else thisExpression) + + val resolvedCall = createFakeResolvedCall(coroutineLambda, coroutineLambdaDescriptor, interceptResume, listOf(blockArgument)) + + return InterceptResumeCallContext(resolvedCall, thisExpression) +} + fun ResolvedCall<*>.isSuspensionPoint() = (candidateDescriptor as? FunctionDescriptor)?.let { it.isSuspend && it.getUserData(SUSPENSION_POINT_KEY) ?: false } ?: false @@ -177,3 +200,12 @@ private fun FunctionDescriptor.createCustomCopy( return result } + +fun KotlinType.hasInlineInterceptResume() = + findOperatorInController(this, OperatorNameConventions.COROUTINE_INTERCEPT_RESUME)?.isInline == true + +fun KotlinType.hasNoinlineInterceptResume() = + findOperatorInController(this, OperatorNameConventions.COROUTINE_INTERCEPT_RESUME)?.isInline == false + +fun findOperatorInController(controllerType: KotlinType, name: Name): SimpleFunctionDescriptor? = + controllerType.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).singleOrNull { it.isOperator } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java index 5d26992e04c..56db9cf7101 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java @@ -26,6 +26,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.backend.common.CodegenUtil; import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment; import org.jetbrains.kotlin.codegen.*; +import org.jetbrains.kotlin.codegen.binding.CodegenBinding; import org.jetbrains.kotlin.codegen.context.*; import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt; import org.jetbrains.kotlin.codegen.state.GenerationState; @@ -560,7 +561,10 @@ public class InlineCodegen extends CallGenerator { ); FunctionGenerationStrategy strategy; - if (expression instanceof KtCallableReferenceExpression) { + if (state.getBindingContext().get(CodegenBinding.CUSTOM_STRATEGY_FOR_INLINE_LAMBDA, expression) != null) { + strategy = state.getBindingContext().get(CodegenBinding.CUSTOM_STRATEGY_FOR_INLINE_LAMBDA, expression); + } + else if (expression instanceof KtCallableReferenceExpression) { KtCallableReferenceExpression callableReferenceExpression = (KtCallableReferenceExpression) expression; KtExpression receiverExpression = callableReferenceExpression.getReceiverExpression(); Type receiverType = diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.java index df2db844212..f62358ceb86 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.java @@ -82,7 +82,12 @@ public class LambdaInfo implements LabelOwner { } else { propertyReferenceInfo = null; - functionDescriptor = function; + if (bindingContext.get(CodegenBinding.CUSTOM_DESCRIPTOR_FOR_INLINE_LAMBDA, this.expression) != null) { + functionDescriptor = bindingContext.get(CodegenBinding.CUSTOM_DESCRIPTOR_FOR_INLINE_LAMBDA, this.expression); + } + else { + functionDescriptor = function; + } assert functionDescriptor != null : "Function is not resolved to descriptor: " + expression.getText(); classDescriptor = anonymousClassForCallable(bindingContext, functionDescriptor); closureClassType = asmTypeForAnonymousClass(bindingContext, functionDescriptor);