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
This commit is contained in:
Denis Zharkov
2016-11-08 10:43:45 +03:00
parent fc9605b94e
commit 7e49db8768
9 changed files with 133 additions and 141 deletions
@@ -422,7 +422,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
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<KtElement> {
iv.invokespecial(superClassAsmType.getInternalName(), "<init>", "()V", false);
}
generateAdditionalCodeInConstructor(iv);
iv.visitInsn(RETURN);
FunctionCodegen.endVisit(iv, "constructor", element);
@@ -442,10 +440,6 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
return constructor;
}
protected void generateAdditionalCodeInConstructor(@NotNull InstructionAdapter iv) {
}
@NotNull
public static List<FieldInfo> calculateConstructorParameters(
@NotNull KotlinTypeMapper typeMapper,
@@ -3045,12 +3045,17 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> 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);
@@ -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<ClassDescriptor> 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<Any?>` 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)
@@ -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
)
}
}
}
@@ -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) }
@@ -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<AnonymousObjec
public void visit(int version, int access, @NotNull String name, String signature, String superName, String[] interfaces) {
InlineCodegenUtil.assertVersionNotGreaterThanGeneratedOne(version, name, inliningContext.state);
classBuilder.defineClass(null, version, access, name, signature, superName, interfaces);
if (interfaces != null) {
for (String anInterface : interfaces) {
if("kotlin/coroutines/Continuation".equals(anInterface)) {
inliningContext.setContinuation(true);
break;
}
}
if(AsmTypes.COROUTINE_IMPL.getInternalName().equals(superName)) {
inliningContext.setContinuation(true);
}
}
@@ -40,6 +40,8 @@ public class AsmTypes {
public static final Type MUTABLE_PROPERTY_REFERENCE0 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference0");
public static final Type MUTABLE_PROPERTY_REFERENCE1 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference1");
public static final Type MUTABLE_PROPERTY_REFERENCE2 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference2");
public static final Type COROUTINE_IMPL = Type.getObjectType("kotlin/jvm/internal/CoroutineImpl");
public static final Type[] PROPERTY_REFERENCE_IMPL = {
Type.getObjectType("kotlin/jvm/internal/PropertyReference0Impl"),
+1 -1
View File
@@ -11,7 +11,7 @@ fun builder1(coroutine c: Controller.() -> Continuation<Unit>) {
fun builder2(coroutine c: Controller.() -> Continuation<Unit>) {
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)
@@ -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<Any?> {
// 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?)
}