Support 'interceptResume' operator in JVM backend
#KT-14891 In Progress
This commit is contained in:
@@ -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)
|
||||
|
||||
+30
-4
@@ -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
|
||||
|
||||
+11
-4
@@ -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
|
||||
|
||||
+36
-4
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user