Avoid dependency on coroutine-related public API changes

These changes has been introduced in c3a032ea0b
to support new scheme for named functions

At the same time they can be avoided by generating some additional
code/accessors in the anonymous classes for coroutine state

Now we cannot simply remove introduced API because of bootstrap problems

 #KT-17898 Fixed
This commit is contained in:
Denis Zharkov
2017-05-18 08:48:16 +03:00
parent 06b598e595
commit 49453ca705
8 changed files with 178 additions and 88 deletions
@@ -1037,7 +1037,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
if (captureReceiver != null) {
StackValue capturedReceiver =
functionReferenceReceiver != null ? functionReferenceReceiver :
generateExtensionReceiver(unwrapOriginalReceiverOwnerForSuspendFunction(context));
generateExtensionReceiver(unwrapOriginalReceiverOwnerForSuspendLambda(context));
callGenerator.putCapturedValueOnStack(capturedReceiver, capturedReceiver.type, paramIndex++);
}
@@ -1076,7 +1076,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
@NotNull
private static CallableDescriptor unwrapOriginalReceiverOwnerForSuspendFunction(@NotNull MethodContext context) {
private static CallableDescriptor unwrapOriginalReceiverOwnerForSuspendLambda(@NotNull MethodContext context) {
FunctionDescriptor originalForDoResume =
context.getFunctionDescriptor().getUserData(CoroutineCodegenUtilKt.INITIAL_SUSPEND_DESCRIPTOR_FOR_DO_RESUME);
@@ -43,7 +43,6 @@ class JvmRuntimeTypes(module: ModuleDescriptor) {
private val localVariableReference: ClassDescriptor by klass("LocalVariableReference")
private val mutableLocalVariableReference: ClassDescriptor by klass("MutableLocalVariableReference")
private val coroutineImplClass by lazy { createClass(kotlinCoroutinesJvmInternalPackage, "CoroutineImpl") }
private val coroutineImplForNamedFunctionClass by lazy { createClass(kotlinCoroutinesJvmInternalPackage, "CoroutineImplForNamedFunction") }
private val propertyReferences: List<ClassDescriptor> by lazy {
(0..2).map { i -> createClass(kotlinJvmInternalPackage, "PropertyReference$i") }
@@ -85,13 +84,10 @@ class JvmRuntimeTypes(module: ModuleDescriptor) {
if (descriptor.isSuspend) {
return mutableListOf<KotlinType>().apply {
add(coroutineImplClass.defaultType)
if (descriptor.isSuspendLambda) {
add(coroutineImplClass.defaultType)
add(functionType)
}
else {
add(coroutineImplForNamedFunctionClass.defaultType)
}
}
}
@@ -54,13 +54,45 @@ abstract class AbstractCoroutineCodegen(
outerExpressionCodegen: ExpressionCodegen,
element: KtElement,
closureContext: ClosureContext,
classBuilder: ClassBuilder
classBuilder: ClassBuilder,
userDataForDoResume: Map<out FunctionDescriptor.UserDataKey<*>, *>? = null
) : ClosureCodegen(
outerExpressionCodegen.state,
element, null, closureContext, null,
FailingFunctionGenerationStrategy,
outerExpressionCodegen.parentCodegen, classBuilder
) {
protected val classDescriptor = closureContext.contextDescriptor
protected val doResumeDescriptor =
SimpleFunctionDescriptorImpl.create(
classDescriptor, Annotations.EMPTY, Name.identifier(DO_RESUME_METHOD_NAME), CallableMemberDescriptor.Kind.DECLARATION,
funDescriptor.source
).apply doResume@{
initialize(
null,
classDescriptor.thisAsReceiverParameter,
emptyList(),
listOf(
createValueParameterForDoResume(Name.identifier("data"), builtIns.nullableAnyType, 0),
createValueParameterForDoResume(Name.identifier("throwable"), builtIns.throwable.defaultType.makeNullable(), 1)
),
builtIns.nullableAnyType,
Modality.FINAL,
Visibilities.PUBLIC,
userDataForDoResume
)
}
private fun FunctionDescriptor.createValueParameterForDoResume(name: Name, type: KotlinType, index: Int) =
ValueParameterDescriptorImpl(
this, null, index, Annotations.EMPTY, name,
type,
false, false,
false,
null, SourceElement.NO_SOURCE
)
override fun generateConstructor(): Method {
val args = calculateConstructorParameters(typeMapper, closure, asmType)
val argTypes = args.map { it.fieldType }.plus(CONTINUATION_ASM_TYPE).toTypedArray()
@@ -78,14 +110,12 @@ abstract class AbstractCoroutineCodegen(
iv.generateClosureFieldsInitializationFromParameters(closure, args)
iv.load(0, AsmTypes.OBJECT_TYPE)
if (passArityToSuperClass) {
iv.iconst(calculateArity())
}
iv.iconst(if (passArityToSuperClass) calculateArity() else 0)
iv.load(argTypes.map { it.size }.sum(), AsmTypes.OBJECT_TYPE)
val superClassConstructorDescriptor = Type.getMethodDescriptor(
Type.VOID_TYPE,
*(if (passArityToSuperClass) arrayOf(Type.INT_TYPE) else emptyArray()),
Type.INT_TYPE,
CONTINUATION_ASM_TYPE
)
iv.invokespecial(superClassAsmType.internalName, "<init>", superClassConstructorDescriptor, false)
@@ -107,45 +137,14 @@ class CoroutineCodegenForLambda private constructor(
private val closureContext: ClosureContext,
classBuilder: ClassBuilder,
private val originalSuspendFunctionDescriptor: FunctionDescriptor
) : AbstractCoroutineCodegen(outerExpressionCodegen, element, closureContext, classBuilder) {
private val classDescriptor = closureContext.contextDescriptor
) : AbstractCoroutineCodegen(
outerExpressionCodegen, element, closureContext, classBuilder,
userDataForDoResume = mapOf(INITIAL_SUSPEND_DESCRIPTOR_FOR_DO_RESUME to originalSuspendFunctionDescriptor)
) {
private val builtIns = funDescriptor.builtIns
private lateinit var constructorToUseFromInvoke: Method
// protected fun doResume(result, throwable)
private val doResumeDescriptor =
SimpleFunctionDescriptorImpl.create(
classDescriptor, Annotations.EMPTY, Name.identifier(DO_RESUME_METHOD_NAME), CallableMemberDescriptor.Kind.DECLARATION,
funDescriptor.source
).apply doResume@{
initialize(
/* receiverParameterType = */ null,
classDescriptor.thisAsReceiverParameter,
/* typeParameters = */ emptyList(),
listOf(
ValueParameterDescriptorImpl(
this@doResume, null, 0, Annotations.EMPTY, Name.identifier("data"),
builtIns.nullableAnyType,
/* isDefault = */ false, /* isCrossinline = */ false,
/* isNoinline = */ false,
/* varargElementType = */ null, SourceElement.NO_SOURCE
),
ValueParameterDescriptorImpl(
this@doResume, null, 1, Annotations.EMPTY, Name.identifier("throwable"),
builtIns.throwable.defaultType.makeNullable(),
/* isDefault = */ false, /* isCrossinline = */ false,
/* isNoinline = */ false,
/* varargElementType = */ null, SourceElement.NO_SOURCE
)
),
builtIns.nullableAnyType,
Modality.FINAL,
Visibilities.PUBLIC,
mapOf(INITIAL_SUSPEND_DESCRIPTOR_FOR_DO_RESUME to originalSuspendFunctionDescriptor)
)
}
private val createCoroutineDescriptor =
funDescriptor.createCustomCopy {
setName(Name.identifier(SUSPEND_FUNCTION_CREATE_METHOD_NAME))
@@ -355,28 +354,9 @@ class CoroutineCodegenForNamedFunction private constructor(
classBuilder: ClassBuilder,
originalSuspendFunctionDescriptor: FunctionDescriptor
) : AbstractCoroutineCodegen(outerExpressionCodegen, element, closureContext, classBuilder) {
private val classDescriptor = closureContext.contextDescriptor
private val suspendFunctionJvmView =
bindingContext[CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW, originalSuspendFunctionDescriptor]!!
// protected fun doResume(): Any?
private val doResumeDescriptor =
SimpleFunctionDescriptorImpl.create(
classDescriptor, Annotations.EMPTY, Name.identifier(DO_RESUME_METHOD_NAME), CallableMemberDescriptor.Kind.DECLARATION,
funDescriptor.source
).apply doResume@{
initialize(
/* receiverParameterType = */ null,
classDescriptor.thisAsReceiverParameter,
/* typeParameters = */ emptyList(),
listOf(),
builtIns.nullableAnyType,
Modality.FINAL,
Visibilities.PUBLIC
)
}
override val passArityToSuperClass get() = false
override fun generateBridges() {
@@ -385,6 +365,18 @@ class CoroutineCodegenForNamedFunction private constructor(
override fun generateClosureBody() {
generateDoResume()
generateGetLabelMethod()
generateSetLabelMethod()
v.newField(
JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_SYNTHETIC or AsmUtil.NO_FLAG_PACKAGE_PRIVATE,
DATA_FIELD_NAME, AsmTypes.OBJECT_TYPE.descriptor, null, null
)
v.newField(
JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_SYNTHETIC or AsmUtil.NO_FLAG_PACKAGE_PRIVATE,
EXCEPTION_FIELD_NAME, AsmTypes.JAVA_THROWABLE_TYPE.descriptor, null, null
)
}
private fun generateDoResume() {
@@ -393,6 +385,25 @@ class CoroutineCodegenForNamedFunction private constructor(
doResumeDescriptor,
object : FunctionGenerationStrategy.CodegenBased(state) {
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
StackValue.field(
AsmTypes.OBJECT_TYPE, Type.getObjectType(v.thisName), DATA_FIELD_NAME, false,
StackValue.LOCAL_0
).store(StackValue.local(1, AsmTypes.OBJECT_TYPE), codegen.v)
StackValue.field(
AsmTypes.JAVA_THROWABLE_TYPE, Type.getObjectType(v.thisName), EXCEPTION_FIELD_NAME, false,
StackValue.LOCAL_0
).store(StackValue.local(2, AsmTypes.JAVA_THROWABLE_TYPE), codegen.v)
LABEL_FIELD_STACK_VALUE.store(
StackValue.operation(Type.INT_TYPE) {
LABEL_FIELD_STACK_VALUE.put(Type.INT_TYPE, it)
it.iconst(1 shl 31)
it.or(Type.INT_TYPE)
},
codegen.v
)
val captureThisType = closure.captureThis?.let(typeMapper::mapType)
if (captureThisType != null) {
StackValue.field(
@@ -430,6 +441,38 @@ class CoroutineCodegenForNamedFunction private constructor(
)
}
private fun generateGetLabelMethod() {
val mv = v.newMethod(
JvmDeclarationOrigin.NO_ORIGIN,
Opcodes.ACC_SYNTHETIC or Opcodes.ACC_FINAL or AsmUtil.NO_FLAG_PACKAGE_PRIVATE,
"getLabel",
Type.getMethodDescriptor(Type.INT_TYPE),
null,
null
)
mv.visitCode()
LABEL_FIELD_STACK_VALUE.put(Type.INT_TYPE, InstructionAdapter(mv))
mv.visitInsn(Opcodes.IRETURN)
mv.visitEnd()
}
private fun generateSetLabelMethod() {
val mv = v.newMethod(
JvmDeclarationOrigin.NO_ORIGIN,
Opcodes.ACC_SYNTHETIC or Opcodes.ACC_FINAL or AsmUtil.NO_FLAG_PACKAGE_PRIVATE,
"setLabel",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE),
null,
null
)
mv.visitCode()
LABEL_FIELD_STACK_VALUE.store(StackValue.local(1, Type.INT_TYPE), InstructionAdapter(mv))
mv.visitInsn(Opcodes.RETURN)
mv.visitEnd()
}
override fun generateKotlinMetadataAnnotation() {
writeKotlinMetadata(v, state, KotlinClassHeader.Kind.SYNTHETIC_CLASS, 0) {
// Do not write method metadata for raw coroutine state machines
@@ -437,6 +480,12 @@ class CoroutineCodegenForNamedFunction private constructor(
}
companion object {
private val LABEL_FIELD_STACK_VALUE =
StackValue.field(
FieldInfo.createForHiddenField(COROUTINE_IMPL_ASM_TYPE, Type.INT_TYPE, COROUTINE_LABEL_FIELD_NAME),
StackValue.LOCAL_0
)
fun create(
cv: ClassBuilder,
expressionCodegen: ExpressionCodegen,
@@ -158,7 +158,7 @@ class CoroutineTransformerMethodVisitor(
val objectTypeForState = Type.getObjectType(classBuilderForCoroutineState.thisName)
methodNode.instructions.insert(withInstructionAdapter {
val createStateInstance = Label()
val storeStateObject = Label()
val afterCoroutineStateCreated = Label()
// We have to distinguish the following situations:
// - Our function got called in a common way (e.g. from another function or via recursive call) and we should execute our
@@ -180,15 +180,39 @@ class CoroutineTransformerMethodVisitor(
visitVarInsn(Opcodes.ALOAD, continuationIndex)
checkcast(objectTypeForState)
visitVarInsn(Opcodes.ASTORE, continuationIndex)
visitVarInsn(Opcodes.ALOAD, continuationIndex)
invokevirtual(
COROUTINE_IMPL_FOR_NAMED_ASM_TYPE.internalName,
"checkAndFlushLastBit", Type.getMethodDescriptor(Type.BOOLEAN_TYPE), false
classBuilderForCoroutineState.thisName,
"getLabel",
Type.getMethodDescriptor(Type.INT_TYPE),
false
)
iconst(1 shl 31)
and(Type.INT_TYPE)
ifeq(createStateInstance)
visitVarInsn(Opcodes.ALOAD, continuationIndex)
checkcast(objectTypeForState)
goTo(storeStateObject)
dup()
invokevirtual(
classBuilderForCoroutineState.thisName,
"getLabel",
Type.getMethodDescriptor(Type.INT_TYPE),
false
)
iconst(1 shl 31)
sub(Type.INT_TYPE)
invokevirtual(
classBuilderForCoroutineState.thisName,
"setLabel",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE),
false
)
goTo(afterCoroutineStateCreated)
visitLabel(createStateInstance)
@@ -218,17 +242,16 @@ class CoroutineTransformerMethodVisitor(
false
)
visitLabel(storeStateObject)
visitVarInsn(Opcodes.ASTORE, continuationIndex)
visitLabel(afterCoroutineStateCreated)
visitVarInsn(Opcodes.ALOAD, continuationIndex)
checkcast(objectTypeForState)
getfield(COROUTINE_IMPL_FOR_NAMED_ASM_TYPE.internalName, "data", AsmTypes.OBJECT_TYPE.descriptor)
getfield(classBuilderForCoroutineState.thisName, DATA_FIELD_NAME, AsmTypes.OBJECT_TYPE.descriptor)
visitVarInsn(Opcodes.ASTORE, dataIndex)
visitVarInsn(Opcodes.ALOAD, continuationIndex)
checkcast(objectTypeForState)
getfield(COROUTINE_IMPL_FOR_NAMED_ASM_TYPE.internalName, "exception", AsmTypes.JAVA_THROWABLE_TYPE.descriptor)
getfield(classBuilderForCoroutineState.thisName, EXCEPTION_FIELD_NAME, AsmTypes.JAVA_THROWABLE_TYPE.descriptor)
visitVarInsn(Opcodes.ASTORE, exceptionIndex)
})
}
@@ -418,10 +441,18 @@ class CoroutineTransformerMethodVisitor(
insnListOf(
VarInsnNode(Opcodes.ALOAD, continuationIndex),
*withInstructionAdapter { iconst(id) }.toArray(),
FieldInsnNode(
Opcodes.PUTFIELD, COROUTINE_IMPL_ASM_TYPE.internalName, COROUTINE_LABEL_FIELD_NAME,
Type.INT_TYPE.descriptor
)
if (isForNamedFunction)
MethodInsnNode(
Opcodes.INVOKEVIRTUAL, classBuilderForCoroutineState.thisName,
"setLabel",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE),
false
)
else
FieldInsnNode(
Opcodes.PUTFIELD, COROUTINE_IMPL_ASM_TYPE.internalName, COROUTINE_LABEL_FIELD_NAME,
Type.INT_TYPE.descriptor
)
)
)
@@ -60,6 +60,8 @@ const val AFTER_SUSPENSION_POINT_MARKER_NAME = "afterSuspensionPoint"
const val COROUTINE_LABEL_FIELD_NAME = "label"
const val SUSPEND_FUNCTION_CREATE_METHOD_NAME = "create"
const val DO_RESUME_METHOD_NAME = "doResume"
const val DATA_FIELD_NAME = "data"
const val EXCEPTION_FIELD_NAME = "exception"
@JvmField
val COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME =
@@ -71,10 +73,6 @@ val CONTINUATION_ASM_TYPE = DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME.topLe
@JvmField
val COROUTINE_IMPL_ASM_TYPE = COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(Name.identifier("CoroutineImpl")).topLevelClassAsmType()
@JvmField
val COROUTINE_IMPL_FOR_NAMED_ASM_TYPE =
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(Name.identifier("CoroutineImplForNamedFunction")).topLevelClassAsmType()
private val COROUTINES_INTRINSICS_FILE_FACADE_INTERNAL_NAME =
COROUTINES_INTRINSICS_PACKAGE_FQ_NAME.child(Name.identifier("IntrinsicsKt")).topLevelClassAsmType()
@@ -14,9 +14,13 @@ final class InlineWithStateMachineKt$box$1 {
final class InlineWithStateMachineKt$mainSuspend$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
synthetic field data: java.lang.Object
synthetic field exception: java.lang.Throwable
inner class InlineWithStateMachineKt$mainSuspend$1
method <init>(p0: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.Nullable method doResume(): java.lang.Object
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
synthetic final method getLabel(): int
synthetic final method setLabel(p0: int): void
}
@kotlin.Metadata
@@ -15,9 +15,13 @@ final class InlineWithoutStateMachineKt$box$1 {
final class InlineWithoutStateMachineKt$complexSuspend$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
synthetic field data: java.lang.Object
synthetic field exception: java.lang.Throwable
inner class InlineWithoutStateMachineKt$complexSuspend$1
method <init>(p0: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.Nullable method doResume(): java.lang.Object
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
synthetic final method getLabel(): int
synthetic final method setLabel(p0: int): void
}
@kotlin.Metadata
@@ -1,19 +1,27 @@
@kotlin.Metadata
final class Controller$multipleSuspensions$1 {
field L$0: java.lang.Object
synthetic field data: java.lang.Object
synthetic field exception: java.lang.Throwable
synthetic final field this$0: Controller
inner class Controller$multipleSuspensions$1
method <init>(p0: Controller, p1: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.Nullable method doResume(): java.lang.Object
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
synthetic final method getLabel(): int
synthetic final method setLabel(p0: int): void
}
@kotlin.Metadata
final class Controller$nonTailCall$1 {
field L$0: java.lang.Object
synthetic field data: java.lang.Object
synthetic field exception: java.lang.Throwable
synthetic final field this$0: Controller
inner class Controller$nonTailCall$1
method <init>(p0: Controller, p1: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.Nullable method doResume(): java.lang.Object
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
synthetic final method getLabel(): int
synthetic final method setLabel(p0: int): void
}
@kotlin.Metadata