Support 'interceptResume' operator in JVM backend
#KT-14891 In Progress
This commit is contained in:
@@ -16,23 +16,23 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen
|
package org.jetbrains.kotlin.codegen
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.codegen.coroutines.hasNoinlineInterceptResume
|
||||||
import org.jetbrains.kotlin.coroutines.controllerTypeIfCoroutine
|
import org.jetbrains.kotlin.coroutines.controllerTypeIfCoroutine
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor
|
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.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.calls.util.createFunctionType
|
import org.jetbrains.kotlin.resolve.calls.util.createFunctionType
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
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.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeConstructorSubstitution
|
import org.jetbrains.kotlin.types.TypeConstructorSubstitution
|
||||||
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||||
|
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||||
|
|
||||||
class JvmRuntimeTypes(module: ModuleDescriptor) {
|
class JvmRuntimeTypes(module: ModuleDescriptor) {
|
||||||
private val kotlinJvmInternalPackage = MutablePackageFragmentDescriptor(module, FqName("kotlin.jvm.internal"))
|
private val kotlinJvmInternalPackage = MutablePackageFragmentDescriptor(module, FqName("kotlin.jvm.internal"))
|
||||||
@@ -86,7 +86,22 @@ class JvmRuntimeTypes(module: ModuleDescriptor) {
|
|||||||
|
|
||||||
val coroutineControllerType = descriptor.controllerTypeIfCoroutine
|
val coroutineControllerType = descriptor.controllerTypeIfCoroutine
|
||||||
if (coroutineControllerType != null) {
|
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)
|
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.builtins.ReflectionTypes;
|
||||||
import org.jetbrains.kotlin.cfg.WhenChecker;
|
import org.jetbrains.kotlin.cfg.WhenChecker;
|
||||||
import org.jetbrains.kotlin.codegen.*;
|
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.GenerationState;
|
||||||
import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
|
import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
|
||||||
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil;
|
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.coroutines.CoroutineUtilKt;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
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.descriptors.impl.LocalVariableDescriptor;
|
||||||
import org.jetbrains.kotlin.fileClasses.FileClasses;
|
import org.jetbrains.kotlin.fileClasses.FileClasses;
|
||||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
|
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
|
||||||
@@ -273,12 +275,36 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
|||||||
ClassDescriptor classDescriptor = recordClassForCallable(functionLiteral, functionDescriptor, supertypes, name);
|
ClassDescriptor classDescriptor = recordClassForCallable(functionLiteral, functionDescriptor, supertypes, name);
|
||||||
MutableClosure closure = recordClosure(classDescriptor, name);
|
MutableClosure closure = recordClosure(classDescriptor, name);
|
||||||
|
|
||||||
if (CoroutineUtilKt.getControllerTypeIfCoroutine(functionDescriptor) != null) {
|
|
||||||
closure.setCoroutine(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
classStack.push(classDescriptor);
|
classStack.push(classDescriptor);
|
||||||
nameStack.push(name);
|
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);
|
super.visitLambdaExpression(lambdaExpression);
|
||||||
nameStack.pop();
|
nameStack.pop();
|
||||||
classStack.pop();
|
classStack.pop();
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.binding;
|
|||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.codegen.FunctionGenerationStrategy;
|
||||||
import org.jetbrains.kotlin.codegen.JvmCodegenUtil;
|
import org.jetbrains.kotlin.codegen.JvmCodegenUtil;
|
||||||
import org.jetbrains.kotlin.codegen.SamType;
|
import org.jetbrains.kotlin.codegen.SamType;
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||||
@@ -66,6 +67,10 @@ public class CodegenBinding {
|
|||||||
public static final WritableSlice<VariableDescriptor, VariableDescriptor> LOCAL_VARIABLE_PROPERTY_METADATA =
|
public static final WritableSlice<VariableDescriptor, VariableDescriptor> LOCAL_VARIABLE_PROPERTY_METADATA =
|
||||||
Slices.createSimpleSlice();
|
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 {
|
static {
|
||||||
BasicWritableSlice.initSliceDebugNames(CodegenBinding.class);
|
BasicWritableSlice.initSliceDebugNames(CodegenBinding.class);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.codegen.coroutines
|
package org.jetbrains.kotlin.codegen.coroutines
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.*
|
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.ClosureContext
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.coroutines.controllerTypeIfCoroutine
|
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.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
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.name.Name
|
||||||
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
import org.jetbrains.kotlin.psi.KtFunctionLiteral
|
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.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||||
@@ -166,10 +168,7 @@ class CoroutineCodegen(
|
|||||||
)
|
)
|
||||||
|
|
||||||
private fun generateExceptionHandlingBlock(codegen: ExpressionCodegen) {
|
private fun generateExceptionHandlingBlock(codegen: ExpressionCodegen) {
|
||||||
val handleExceptionFunction =
|
val handleExceptionFunction = findOperatorInController(controllerType, OperatorNameConventions.COROUTINE_HANDLE_EXCEPTION) ?: return
|
||||||
controllerType.memberScope.getContributedFunctions(
|
|
||||||
OperatorNameConventions.COROUTINE_HANDLE_EXCEPTION, KotlinLookupLocation(element)).singleOrNull { it.isOperator }
|
|
||||||
?: return
|
|
||||||
|
|
||||||
val (resolvedCall, fakeExceptionExpression, fakeThisContinuationException) =
|
val (resolvedCall, fakeExceptionExpression, fakeThisContinuationException) =
|
||||||
createResolvedCallForHandleExceptionCall(element, handleExceptionFunction, coroutineLambdaDescriptor)
|
createResolvedCallForHandleExceptionCall(element, handleExceptionFunction, coroutineLambdaDescriptor)
|
||||||
@@ -219,18 +218,184 @@ class CoroutineCodegen(
|
|||||||
Visibilities.PROTECTED
|
Visibilities.PROTECTED
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val interceptResume = findOperatorInController(controllerType, OperatorNameConventions.COROUTINE_INTERCEPT_RESUME)
|
||||||
functionCodegen.generateMethod(OtherOrigin(element), doResumeDescriptor,
|
if (interceptResume != null) {
|
||||||
object : FunctionGenerationStrategy.FunctionDefault(state, element as KtDeclarationWithBody) {
|
processInterceptResume(doResumeDescriptor, interceptResume)
|
||||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
}
|
||||||
codegen.v.visitAnnotation(CONTINUATION_METHOD_ANNOTATION_DESC, true).visitEnd()
|
else {
|
||||||
codegen.initializeCoroutineParameters()
|
functionCodegen.generateMethod(
|
||||||
super.doGenerateBody(codegen, signature)
|
OtherOrigin(element),
|
||||||
generateExceptionHandlingBlock(codegen)
|
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) {
|
private fun InstructionAdapter.setLabelValue(value: Int) {
|
||||||
load(0, AsmTypes.OBJECT_TYPE)
|
load(0, AsmTypes.OBJECT_TYPE)
|
||||||
iconst(value)
|
iconst(value)
|
||||||
@@ -273,3 +438,7 @@ class CoroutineCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private const val COROUTINE_LAMBDA_PARAMETER_PREFIX = "p$"
|
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
|
if (methodNode.visibleAnnotations?.none { it.desc == CONTINUATION_METHOD_ANNOTATION_DESC } != false) return
|
||||||
methodNode.visibleAnnotations.removeAll { it.desc == CONTINUATION_METHOD_ANNOTATION_DESC }
|
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)
|
val suspensionPoints = collectSuspensionPoints(methodNode)
|
||||||
|
|
||||||
for (suspensionPoint in suspensionPoints) {
|
for (suspensionPoint in suspensionPoints) {
|
||||||
@@ -72,7 +79,7 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add global exception handler
|
// 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
|
// Spill stack to variables before suspension points, try/catch blocks
|
||||||
FixStackWithLabelNormalizationMethodTransformer().transform(classBuilder.thisName, methodNode)
|
FixStackWithLabelNormalizationMethodTransformer().transform(classBuilder.thisName, methodNode)
|
||||||
@@ -105,7 +112,7 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
globalExceptionHandler.start.next.next
|
globalExceptionHandler.start.next.next
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
first
|
customCoroutineStart ?: first
|
||||||
// tableswitch(this.label)
|
// tableswitch(this.label)
|
||||||
insertBefore(firstToInsertBefore,
|
insertBefore(firstToInsertBefore,
|
||||||
insnListOf(
|
insnListOf(
|
||||||
@@ -408,7 +415,7 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processHandleExceptionCall(methodNode: MethodNode): Boolean {
|
private fun processHandleExceptionCall(methodNode: MethodNode, coroutineStart: AbstractInsnNode): Boolean {
|
||||||
val instructions = methodNode.instructions
|
val instructions = methodNode.instructions
|
||||||
val marker = instructions.toArray().firstOrNull { it.isHandleExceptionMarker() } ?: return false
|
val marker = instructions.toArray().firstOrNull { it.isHandleExceptionMarker() } ?: return false
|
||||||
|
|
||||||
@@ -418,7 +425,7 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
|
|
||||||
val startLabel = LabelNode()
|
val startLabel = LabelNode()
|
||||||
val endLabel = LabelNode()
|
val endLabel = LabelNode()
|
||||||
instructions.insertBefore(instructions.first, startLabel)
|
instructions.insertBefore(coroutineStart, startLabel)
|
||||||
instructions.set(marker, endLabel)
|
instructions.set(marker, endLabel)
|
||||||
|
|
||||||
// NOP is necessary to preserve common invariant: first insn of TCB is always NOP
|
// 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 com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.ValueArgument
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingTraceContext
|
import org.jetbrains.kotlin.resolve.BindingTraceContext
|
||||||
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
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.calls.util.CallMaker
|
||||||
import org.jetbrains.kotlin.resolve.coroutine.REPLACED_SUSPENSION_POINT_KEY
|
import org.jetbrains.kotlin.resolve.coroutine.REPLACED_SUSPENSION_POINT_KEY
|
||||||
import org.jetbrains.kotlin.resolve.coroutine.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.TypeConstructorSubstitution
|
||||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||||
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
// These classes do not actually exist at runtime
|
// These classes do not actually exist at runtime
|
||||||
val CONTINUATION_METHOD_ANNOTATION_DESC = "Lkotlin/ContinuationMethod;"
|
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 AFTER_SUSPENSION_POINT_MARKER_NAME = "afterSuspensionPoint"
|
||||||
const val HANDLE_EXCEPTION_MARKER_NAME = "handleException"
|
const val HANDLE_EXCEPTION_MARKER_NAME = "handleException"
|
||||||
const val HANDLE_EXCEPTION_ARGUMENT_MARKER_NAME = "handleExceptionArgument"
|
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_FIELD_NAME = "_controller"
|
||||||
const val COROUTINE_CONTROLLER_GETTER_NAME = "getController"
|
const val COROUTINE_CONTROLLER_GETTER_NAME = "getController"
|
||||||
@@ -157,6 +159,27 @@ private fun createFakeResolvedCall(
|
|||||||
return resolvedCall
|
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() =
|
fun ResolvedCall<*>.isSuspensionPoint() =
|
||||||
(candidateDescriptor as? FunctionDescriptor)?.let { it.isSuspend && it.getUserData(SUSPENSION_POINT_KEY) ?: false }
|
(candidateDescriptor as? FunctionDescriptor)?.let { it.isSuspend && it.getUserData(SUSPENSION_POINT_KEY) ?: false }
|
||||||
?: false
|
?: false
|
||||||
@@ -177,3 +200,12 @@ private fun FunctionDescriptor.createCustomCopy(
|
|||||||
|
|
||||||
return result
|
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.backend.common.CodegenUtil;
|
||||||
import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment;
|
import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment;
|
||||||
import org.jetbrains.kotlin.codegen.*;
|
import org.jetbrains.kotlin.codegen.*;
|
||||||
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
||||||
import org.jetbrains.kotlin.codegen.context.*;
|
import org.jetbrains.kotlin.codegen.context.*;
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt;
|
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt;
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||||
@@ -560,7 +561,10 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
);
|
);
|
||||||
|
|
||||||
FunctionGenerationStrategy strategy;
|
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;
|
KtCallableReferenceExpression callableReferenceExpression = (KtCallableReferenceExpression) expression;
|
||||||
KtExpression receiverExpression = callableReferenceExpression.getReceiverExpression();
|
KtExpression receiverExpression = callableReferenceExpression.getReceiverExpression();
|
||||||
Type receiverType =
|
Type receiverType =
|
||||||
|
|||||||
@@ -82,7 +82,12 @@ public class LambdaInfo implements LabelOwner {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
propertyReferenceInfo = null;
|
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();
|
assert functionDescriptor != null : "Function is not resolved to descriptor: " + expression.getText();
|
||||||
classDescriptor = anonymousClassForCallable(bindingContext, functionDescriptor);
|
classDescriptor = anonymousClassForCallable(bindingContext, functionDescriptor);
|
||||||
closureClassType = asmTypeForAnonymousClass(bindingContext, functionDescriptor);
|
closureClassType = asmTypeForAnonymousClass(bindingContext, functionDescriptor);
|
||||||
|
|||||||
Reference in New Issue
Block a user