From 7e49db87688f72b33b8ac8a3983ba0e8e41fab5e Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 8 Nov 2016 10:43:45 +0300 Subject: [PATCH] Introduce CoroutineImpl as a common super class for coroutines The main benefit are class-files sizes for them (not repeating the same declaration for each coroutine) Also it helped to simplify coroutine codegen code a little Note that controller/label field become non-volatile (see KT-14636) #KT-14636 In Progress --- .../kotlin/codegen/ClosureCodegen.java | 8 +- .../kotlin/codegen/ExpressionCodegen.java | 17 +- .../kotlin/codegen/JvmRuntimeTypes.kt | 5 +- .../codegen/coroutines/CoroutineCodegen.kt | 172 ++++++------------ .../CoroutineTransformationClassBuilder.kt | 18 +- .../inline/AnonymousObjectTransformer.java | 10 +- .../kotlin/resolve/jvm/AsmTypes.java | 2 + .../codegen/box/coroutines/illegalState.kt | 2 +- .../src/kotlin/jvm/internal/CoroutineImpl.kt | 40 ++++ 9 files changed, 133 insertions(+), 141 deletions(-) create mode 100644 core/runtime.jvm/src/kotlin/jvm/internal/CoroutineImpl.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java index e401e101755..3ec7565827c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java @@ -422,7 +422,7 @@ public class ClosureCodegen extends MemberCodegen { iv.load(0, superClassAsmType); - if (superClassAsmType.equals(LAMBDA) || superClassAsmType.equals(FUNCTION_REFERENCE)) { + if (superClassAsmType.equals(LAMBDA) || superClassAsmType.equals(FUNCTION_REFERENCE) || superClassAsmType.equals(COROUTINE_IMPL)) { int arity = funDescriptor.getValueParameters().size(); if (funDescriptor.getExtensionReceiverParameter() != null) arity++; if (funDescriptor.getDispatchReceiverParameter() != null) arity++; @@ -433,8 +433,6 @@ public class ClosureCodegen extends MemberCodegen { iv.invokespecial(superClassAsmType.getInternalName(), "", "()V", false); } - generateAdditionalCodeInConstructor(iv); - iv.visitInsn(RETURN); FunctionCodegen.endVisit(iv, "constructor", element); @@ -442,10 +440,6 @@ public class ClosureCodegen extends MemberCodegen { return constructor; } - protected void generateAdditionalCodeInConstructor(@NotNull InstructionAdapter iv) { - - } - @NotNull public static List calculateConstructorParameters( @NotNull KotlinTypeMapper typeMapper, diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 7f47def8f83..5ebdcba6188 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -3045,12 +3045,17 @@ public class ExpressionCodegen extends KtVisitor impleme assert classDescriptor != null : "class descriptor for coroutine " + descriptor + " should not be null"; StackValue coroutineReceiver = StackValue.thisOrOuter(this, classDescriptor, /* isSuper =*/ false, /* castReceiver */ false); - return StackValue.field( - FieldInfo.createForHiddenField( - typeMapper.mapClass(classDescriptor), - typeMapper.mapType(coroutineControllerType), - CoroutineCodegenUtilKt.COROUTINE_CONTROLLER_FIELD_NAME), - coroutineReceiver); + return StackValue.coercion( + StackValue.field( + FieldInfo.createForHiddenField( + AsmTypes.COROUTINE_IMPL, + AsmTypes.OBJECT_TYPE, + CoroutineCodegenUtilKt.COROUTINE_CONTROLLER_FIELD_NAME + ), + coroutineReceiver + ), + typeMapper.mapType(coroutineControllerType) + ); } return context.generateReceiver(descriptor, state, false); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt index 020b403fa90..bcd3acd4b41 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt @@ -43,6 +43,7 @@ class JvmRuntimeTypes(module: ModuleDescriptor) { private val functionReference: ClassDescriptor by klass("FunctionReference") private val localVariableReference: ClassDescriptor by klass("LocalVariableReference") private val mutableLocalVariableReference: ClassDescriptor by klass("MutableLocalVariableReference") + private val coroutineImplClass by klass("CoroutineImpl") private val propertyReferences: List by lazy { (0..2).map { i -> createClass(kotlinJvmInternalPackage, "PropertyReference$i") } @@ -52,7 +53,7 @@ class JvmRuntimeTypes(module: ModuleDescriptor) { (0..2).map { i -> createClass(kotlinJvmInternalPackage, "MutablePropertyReference$i") } } - private val defaultContinuationSupertype: KotlinType by lazy { createNullableAnyContinuation(module) } + val continuationOfAny: KotlinType by lazy { createNullableAnyContinuation(module) } /** * @return `Continuation` type @@ -89,7 +90,7 @@ class JvmRuntimeTypes(module: ModuleDescriptor) { val coroutineControllerType = descriptor.controllerTypeIfCoroutine if (coroutineControllerType != null) { - return listOf(lambda.defaultType, functionType, /*coroutineType,*/ defaultContinuationSupertype) + return listOf(coroutineImplClass.defaultType, functionType) } return listOf(lambda.defaultType, functionType) 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 265d18256b1..742b018f5ff 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt @@ -17,25 +17,25 @@ 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 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.KtFunction -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe -import org.jetbrains.kotlin.resolve.descriptorUtil.setSingleOverridden +import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.makeNullable import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Opcodes @@ -51,22 +51,11 @@ class CoroutineCodegen( strategy: FunctionGenerationStrategy, parentCodegen: MemberCodegen<*>, classBuilder: ClassBuilder, - private val continuationSuperType: KotlinType, - private val coroutineLambdaDescriptor: FunctionDescriptor + private val coroutineLambdaDescriptor: FunctionDescriptor, + private val controllerType: KotlinType ) : ClosureCodegen(state, element, null, closureContext, null, strategy, parentCodegen, classBuilder) { - private val controllerType = coroutineLambdaDescriptor.controllerTypeIfCoroutine!! override fun generateClosureBody() { - v.newField( - JvmDeclarationOrigin.NO_ORIGIN, AsmUtil.NO_FLAG_PACKAGE_PRIVATE or Opcodes.ACC_VOLATILE, - COROUTINE_CONTROLLER_FIELD_NAME, - typeMapper.mapType(controllerType).descriptor, null, null) - - v.newField( - JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_PRIVATE or Opcodes.ACC_VOLATILE, - COROUTINE_LABEL_FIELD_NAME, - Type.INT_TYPE.descriptor, null, null) - for (parameter in funDescriptor.valueParameters) { v.newField( OtherOrigin(parameter), @@ -77,37 +66,37 @@ class CoroutineCodegen( val classDescriptor = closureContext.contextDescriptor - val resumeFunctionDescriptor = - createSynthesizedImplementationByName( - "resume", - interfaceSupertype = continuationSuperType, - implementationClass = classDescriptor, - sourceElement = funDescriptor.source) - - val resumeWithExceptionFunctionDescriptor = - createSynthesizedImplementationByName( - "resumeWithException", - interfaceSupertype = continuationSuperType, - implementationClass = classDescriptor, - sourceElement = funDescriptor.source) - - // private fun doResume(result, throwable) + // protected fun doResume(result, throwable) val combinedResumeFunctionDescriptor = - resumeFunctionDescriptor.newCopyBuilder() - .setVisibility(Visibilities.PRIVATE) - .setName(Name.identifier("doResume")) - .setCopyOverrides(false) - .setPreserveSourceElement() - .setValueParameters( - listOf( - resumeFunctionDescriptor.valueParameters[0].copy( - resumeFunctionDescriptor, Name.identifier("value"), 0), - resumeWithExceptionFunctionDescriptor.valueParameters[0].copy( - resumeFunctionDescriptor, Name.identifier("exception"), 1)) - ).build()!! - - generatedDelegationToCombinedResume(resumeFunctionDescriptor, combinedResumeFunctionDescriptor, isSuccess = true) - generatedDelegationToCombinedResume(resumeWithExceptionFunctionDescriptor, combinedResumeFunctionDescriptor, isSuccess = false) + SimpleFunctionDescriptorImpl.create( + classDescriptor, Annotations.EMPTY, Name.identifier("doResume"), 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"), + module.builtIns.nullableAnyType, + /* isDefault = */ false, /* isCrossinline = */ false, + /* isNoinline = */ false, /* isCoroutine = */ false, + /* varargElementType = */ null, SourceElement.NO_SOURCE + ), + ValueParameterDescriptorImpl( + this@doResume, null, 1, Annotations.EMPTY, Name.identifier("throwable"), + module.builtIns.throwable.defaultType.makeNullable(), + /* isDefault = */ false, /* isCrossinline = */ false, + /* isNoinline = */ false, /* isCoroutine = */ false, + /* varargElementType = */ null, SourceElement.NO_SOURCE + ) + ), + module.builtIns.unitType, + Modality.FINAL, + Visibilities.PROTECTED + ) + } functionCodegen.generateMethod(OtherOrigin(element), combinedResumeFunctionDescriptor, object : FunctionGenerationStrategy.FunctionDefault(state, element as KtDeclarationWithBody) { @@ -130,9 +119,11 @@ class CoroutineCodegen( private fun generateInvokeMethod(codegen: ExpressionCodegen, signature: JvmMethodSignature) { val classDescriptor = closureContext.contextDescriptor val owner = typeMapper.mapClass(classDescriptor) - val controllerFieldInfo = FieldInfo.createForHiddenField( - owner, - typeMapper.mapType(controllerType), COROUTINE_CONTROLLER_FIELD_NAME) + val controllerFieldInfo = + FieldInfo.createForHiddenField( + AsmTypes.COROUTINE_IMPL, + AsmTypes.OBJECT_TYPE, COROUTINE_CONTROLLER_FIELD_NAME + ) val thisInstance = StackValue.thisOrOuter(codegen, classDescriptor, false, false) @@ -225,90 +216,43 @@ class CoroutineCodegen( codegen.v.areturn(Type.VOID_TYPE) } - private fun createSynthesizedImplementationByName( - name: String, - interfaceSupertype: KotlinType, - implementationClass: ClassDescriptor, - sourceElement: SourceElement - ): SimpleFunctionDescriptor { - val inSuperType = interfaceSupertype.memberScope.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).single() - val result = inSuperType.newCopyBuilder().setSource(sourceElement).setOwner(implementationClass).setModality(Modality.FINAL).build()!! - result.setSingleOverridden(inSuperType) - - return result - } - - private fun generatedDelegationToCombinedResume( - from: SimpleFunctionDescriptor, - combinedResume: SimpleFunctionDescriptor, - isSuccess: Boolean - ) { - functionCodegen.generateMethod(OtherOrigin(element), from, - object : FunctionGenerationStrategy.CodegenBased(state) { - override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { - with(codegen.v) { - load(0, AsmTypes.OBJECT_TYPE) - if (isSuccess) { - load(1, AsmTypes.OBJECT_TYPE) - aconst(null) - } - else { - aconst(null) - load(1, AsmTypes.OBJECT_TYPE) - } - - val delegateTo = typeMapper.mapAsmMethod(combinedResume) - invokevirtual(className, delegateTo.name, delegateTo.descriptor, false) - areturn(Type.VOID_TYPE) - } - } - }) - } - private fun InstructionAdapter.setLabelValue(value: Int) { load(0, AsmTypes.OBJECT_TYPE) iconst(value) - putfield(v.thisName, COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor) - } - - override fun generateAdditionalCodeInConstructor(iv: InstructionAdapter) { - super.generateAdditionalCodeInConstructor(iv) - // Change label value to illegal to make sure continuation will not be used until `invoke(Controler)` - // where correct value will be set up - iv.setLabelValue(LABEL_VALUE_BEFORE_INVOKE) + putfield(AsmTypes.COROUTINE_IMPL.internalName, COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor) } companion object { - private const val LABEL_VALUE_BEFORE_INVOKE = -2 private const val LABEL_VALUE_BEFORE_FIRST_SUSPENSION = 0 - @JvmStatic fun create( + @JvmStatic + fun create( expressionCodegen: ExpressionCodegen, originalCoroutineLambdaDescriptor: FunctionDescriptor, declaration: KtElement, classBuilder: ClassBuilder ): ClosureCodegen? { - val classDescriptor = expressionCodegen.bindingContext[CodegenBinding.CLASS_FOR_CALLABLE, originalCoroutineLambdaDescriptor] ?: return null - declaration as? KtFunction ?: return null - - val continuationSupertype = - classDescriptor.typeConstructor.supertypes.firstOrNull { - it.constructor.declarationDescriptor?.fqNameUnsafe == - DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME.toUnsafe() - } ?: return null + if (declaration !is KtFunction) return null + val controllerType = originalCoroutineLambdaDescriptor.controllerTypeIfCoroutine ?: return null val descriptorWithContinuationReturnType = - originalCoroutineLambdaDescriptor.newCopyBuilder().setPreserveSourceElement().setReturnType(continuationSupertype).build()!! + originalCoroutineLambdaDescriptor.newCopyBuilder() + .setPreserveSourceElement() + .setReturnType(expressionCodegen.state.jvmRuntimeTypes.continuationOfAny) + .build()!! val state = expressionCodegen.state return CoroutineCodegen( state, declaration, expressionCodegen.context.intoCoroutineClosure( - descriptorWithContinuationReturnType, originalCoroutineLambdaDescriptor, expressionCodegen, state.typeMapper), - FunctionGenerationStrategy.FunctionDefault(state, declaration), expressionCodegen.parentCodegen, classBuilder, - continuationSupertype, - originalCoroutineLambdaDescriptor) + descriptorWithContinuationReturnType, originalCoroutineLambdaDescriptor, expressionCodegen, state.typeMapper + ), + FunctionGenerationStrategy.FunctionDefault(state, declaration), + expressionCodegen.parentCodegen, classBuilder, + originalCoroutineLambdaDescriptor, + controllerType + ) } } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformationClassBuilder.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformationClassBuilder.kt index 8c626abc9a4..58a7bcb9f39 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformationClassBuilder.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformationClassBuilder.kt @@ -102,12 +102,18 @@ class CoroutineTransformerMethodVisitor( insnListOf( VarInsnNode(Opcodes.ALOAD, 0), FieldInsnNode( - Opcodes.GETFIELD, classBuilder.thisName, COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor), + Opcodes.GETFIELD, + AsmTypes.COROUTINE_IMPL.internalName, + COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor + ), TableSwitchInsnNode(0, suspensionPoints.size, defaultLabel, - *(arrayOf(startLabel) + suspensionPointLabels)), - startLabel)) + startLabel, *suspensionPointLabels.toTypedArray() + ), + startLabel + ) + ) insert(last, withInstructionAdapter { @@ -303,7 +309,11 @@ class CoroutineTransformerMethodVisitor( VarInsnNode(Opcodes.ALOAD, 0), *withInstructionAdapter { iconst(id) }.toArray(), FieldInsnNode( - Opcodes.PUTFIELD, classBuilder.thisName, COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor))) + Opcodes.PUTFIELD, AsmTypes.COROUTINE_IMPL.internalName, COROUTINE_LABEL_FIELD_NAME, + Type.INT_TYPE.descriptor + ) + ) + ) // Drop default value that purpose was to simulate returned value by suspension method suspension.fakeReturnValueInsns.forEach { methodNode.instructions.remove(it) } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.java index 315d45f0f96..2572803b05c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.java @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.codegen.AsmUtil; import org.jetbrains.kotlin.codegen.ClassBuilder; import org.jetbrains.kotlin.codegen.FieldInfo; import org.jetbrains.kotlin.codegen.StackValue; +import org.jetbrains.kotlin.resolve.jvm.AsmTypes; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin; import org.jetbrains.org.objectweb.asm.*; import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; @@ -67,13 +68,8 @@ public class AnonymousObjectTransformer extends ObjectTransformer Continuation) { fun builder2(coroutine c: Controller.() -> Continuation) { val continuation = c(Controller()) - val declaredField = continuation!!.javaClass.getDeclaredField("label") + val declaredField = continuation.javaClass.superclass.getDeclaredField("label") declaredField.setAccessible(true) declaredField.set(continuation, -3) continuation.resume(Unit) diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineImpl.kt b/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineImpl.kt new file mode 100644 index 00000000000..fdec0e5d527 --- /dev/null +++ b/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineImpl.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.jvm.internal + +abstract class CoroutineImpl(arity: Int) : Lambda(arity), Continuation { + // It's not protected because can be used from noinline lambdas inside coroutine (when calling non-suspend functions) + // Also there might be needed a way to access a controller by Continuation instance when it's inherited from CoroutineImpl + @JvmField + var controller: Any? = null + + // Any label state less then zero indicates that coroutine is not run and can't be resumed in any way. + // Specific values do not matter by now, but currently -2 used for uninitialized coroutine (no controller is assigned), + // and -1 will mean that coroutine execution is over (does not work yet). + @JvmField + protected var label: Int = -2 + + override fun resume(data: Any?) { + doResume(data, null) + } + + override fun resumeWithException(exception: Throwable) { + doResume(null, exception) + } + + protected abstract fun doResume(data: Any?, exception: Throwable?) +}