Support 'interceptResume' operator in JVM backend

#KT-14891 In Progress
This commit is contained in:
Denis Zharkov
2016-11-22 11:44:10 +03:00
parent c2e6f92aa5
commit 5ff71e1330
8 changed files with 295 additions and 32 deletions
@@ -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<Unit>
// 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)
@@ -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.<TypeParameterDescriptor>emptyList(),
Collections.<ValueParameterDescriptor>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();
@@ -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<VariableDescriptor, VariableDescriptor> LOCAL_VARIABLE_PROPERTY_METADATA =
Slices.createSimpleSlice();
public static final WritableSlice<KtElement, FunctionGenerationStrategy> CUSTOM_STRATEGY_FOR_INLINE_LAMBDA = Slices.createSimpleSlice();
public static final WritableSlice<KtElement, FunctionDescriptor> CUSTOM_DESCRIPTOR_FOR_INLINE_LAMBDA = Slices.createSimpleSlice();
static {
BasicWritableSlice.initSliceDebugNames(CodegenBinding.class);
}
@@ -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<Unit>, along with
* FunctionK<Controller, .., Unit>.
* There are two kinds of interceptResume:
* 1. When the defined as not inline. In that case a continuation object is also implements `Function0<Unit>` (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<Unit>` 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
@@ -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<MethodInsnNode>().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
@@ -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 }
@@ -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 =
@@ -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);