diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 5ccec818a9b..8a883d334d2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -1037,7 +1037,7 @@ public class ExpressionCodegen extends KtVisitor 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 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); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt index 90ef9009920..f6490482dc7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt @@ -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 by lazy { (0..2).map { i -> createClass(kotlinJvmInternalPackage, "PropertyReference$i") } @@ -85,13 +84,10 @@ class JvmRuntimeTypes(module: ModuleDescriptor) { if (descriptor.isSuspend) { return mutableListOf().apply { + add(coroutineImplClass.defaultType) if (descriptor.isSuspendLambda) { - add(coroutineImplClass.defaultType) add(functionType) } - else { - add(coroutineImplForNamedFunctionClass.defaultType) - } } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt index 094e03f46ba..491714e4542 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt @@ -54,13 +54,45 @@ abstract class AbstractCoroutineCodegen( outerExpressionCodegen: ExpressionCodegen, element: KtElement, closureContext: ClosureContext, - classBuilder: ClassBuilder + classBuilder: ClassBuilder, + userDataForDoResume: Map, *>? = 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, "", 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, diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt index 86f89424d83..de477ee52c5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -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 + ) ) ) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt index 8dd27e7ec71..07d64165d5e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt @@ -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() diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.txt index 86c091fb732..957dbe4f089 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.txt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.txt @@ -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 (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 diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt index c042efb10c9..6c2735eec89 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt @@ -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 (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 diff --git a/compiler/testData/codegen/bytecodeListing/coroutineFields.txt b/compiler/testData/codegen/bytecodeListing/coroutineFields.txt index 00a6576f8d9..a92a8b481f8 100644 --- a/compiler/testData/codegen/bytecodeListing/coroutineFields.txt +++ b/compiler/testData/codegen/bytecodeListing/coroutineFields.txt @@ -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 (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 (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