From e1fc2c57cbc9e4da9ba64369b7bd294be895c41f Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 24 Jul 2019 15:18:37 +0200 Subject: [PATCH] JVM IR: introduce global serialization bindings for convenience In the old JVM backend, JvmSerializationBindings are created per ClassBuilder, and special care must be taken to make sure that JVM-specific metadata ends up in the correct bindings. This is especially relevant for declarations whose metadata is moved to other classes away from the place where the original declaration lies, for example properties moved from companion object to the outer class, or synthetic methods for annotation properties in interfaces moved to DefaultImpls, or const properties in multifile parts moved to the facade. In the JVM IR backend, this seems not necessary and actually it's complicated to ensure that we use the correct ClassBuilder for bindings (see the code simplification in ClassCodegen). Therefore, in case we don't have an easy way to retrieve the correct ClassBuilder instance, we now write all JVM-specific metadata to the new _global_ bindings map in GenerationState, which is used by JvmSerializerExtension as a fallback if the ClassBuilder's local map has no relevant key. --- .../serialization/JvmSerializerExtension.kt | 36 ++++++++++--------- .../kotlin/codegen/state/GenerationState.kt | 3 ++ .../backend/jvm/codegen/ClassCodegen.kt | 28 ++++----------- 3 files changed, 30 insertions(+), 37 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt index 3e17220f8e7..428e6882145 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt @@ -38,6 +38,7 @@ import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.Method class JvmSerializerExtension(private val bindings: JvmSerializationBindings, state: GenerationState) : SerializerExtension() { + private val globalBindings = state.globalSerializationBindings private val codegenBinding = state.bindingContext private val typeMapper = state.typeMapper override val stringTable = JvmCodegenStringTable(typeMapper) @@ -151,10 +152,10 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta } } - override fun serializeConstructor(descriptor: ConstructorDescriptor, - proto: ProtoBuf.Constructor.Builder, - childSerializer: DescriptorSerializer) { - val method = bindings.get(METHOD_FOR_FUNCTION, descriptor) + override fun serializeConstructor( + descriptor: ConstructorDescriptor, proto: ProtoBuf.Constructor.Builder, childSerializer: DescriptorSerializer + ) { + val method = getBinding(METHOD_FOR_FUNCTION, descriptor) if (method != null) { val signature = SignatureSerializer().methodSignature(descriptor, method) if (signature != null) { @@ -163,10 +164,10 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta } } - override fun serializeFunction(descriptor: FunctionDescriptor, - proto: ProtoBuf.Function.Builder, - childSerializer: DescriptorSerializer) { - val method = bindings.get(METHOD_FOR_FUNCTION, descriptor) + override fun serializeFunction( + descriptor: FunctionDescriptor, proto: ProtoBuf.Function.Builder, childSerializer: DescriptorSerializer + ) { + val method = getBinding(METHOD_FOR_FUNCTION, descriptor) if (method != null) { val signature = SignatureSerializer().methodSignature(descriptor, method) if (signature != null) { @@ -176,20 +177,20 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta } override fun serializeProperty( - descriptor: PropertyDescriptor, - proto: ProtoBuf.Property.Builder, - versionRequirementTable: MutableVersionRequirementTable?, - childSerializer: DescriptorSerializer + descriptor: PropertyDescriptor, + proto: ProtoBuf.Property.Builder, + versionRequirementTable: MutableVersionRequirementTable?, + childSerializer: DescriptorSerializer ) { val signatureSerializer = SignatureSerializer() val getter = descriptor.getter val setter = descriptor.setter - val getterMethod = if (getter == null) null else bindings.get(METHOD_FOR_FUNCTION, getter) - val setterMethod = if (setter == null) null else bindings.get(METHOD_FOR_FUNCTION, setter) + val getterMethod = if (getter == null) null else getBinding(METHOD_FOR_FUNCTION, getter) + val setterMethod = if (setter == null) null else getBinding(METHOD_FOR_FUNCTION, setter) - val field = bindings.get(FIELD_FOR_PROPERTY, descriptor) - val syntheticMethod = bindings.get(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor) + val field = getBinding(FIELD_FOR_PROPERTY, descriptor) + val syntheticMethod = getBinding(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor) val signature = signatureSerializer.propertySignature( descriptor, @@ -232,6 +233,9 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta super.serializeErrorType(type, builder) } + private fun getBinding(slice: SerializationMappingSlice, key: K): V? = + bindings.get(slice, key) ?: globalBindings.get(slice, key) + private inner class SignatureSerializer { fun methodSignature(descriptor: FunctionDescriptor?, method: Method): JvmProtoBuf.JvmMethodSignature? { val builder = JvmProtoBuf.JvmMethodSignature.newBuilder() diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index 5f2ff365599..35a6036e078 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.codegen.inline.GlobalInlineContext import org.jetbrains.kotlin.codegen.inline.InlineCache import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.optimization.OptimizationClassBuilderFactory +import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ScriptDescriptor @@ -256,6 +257,8 @@ class GenerationState private constructor( val metadataVersion = configuration.get(CommonConfigurationKeys.METADATA_VERSION) ?: JvmMetadataVersion.INSTANCE + val globalSerializationBindings = JvmSerializationBindings() + init { this.interceptedBuilderFactory = builderFactory .wrapWith( diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt index a9086a6099b..0dbbc101c16 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.backend.jvm.codegen import org.jetbrains.kotlin.backend.jvm.JvmBackendContext -import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.lower.constantValue import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.binding.CodegenBinding @@ -24,7 +23,6 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.name.SpecialNames -import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.annotations.TRANSIENT_ANNOTATION_FQ_NAME @@ -103,10 +101,8 @@ open class ClassCodegen protected constructor( val shortName = File(fileEntry.name).name visitor.visitSource(shortName, null) - val companionObjectCodegen = nestedClasses.firstOrNull { it.irClass.isCompanion } - for (declaration in irClass.declarations) { - generateDeclaration(declaration, companionObjectCodegen) + generateDeclaration(declaration) } // Generate nested classes at the end, to ensure that codegen for companion object will have the necessary JVM signatures in its @@ -186,10 +182,10 @@ open class ClassCodegen protected constructor( } } - private fun generateDeclaration(declaration: IrDeclaration, companionObjectCodegen: ClassCodegen?) { + private fun generateDeclaration(declaration: IrDeclaration) { when (declaration) { is IrField -> - generateField(declaration, companionObjectCodegen) + generateField(declaration) is IrFunction -> { generateMethod(declaration) } @@ -207,7 +203,7 @@ open class ClassCodegen protected constructor( ClassCodegen(klass, context, this).generate() } - private fun generateField(field: IrField, companionObjectCodegen: ClassCodegen?) { + private fun generateField(field: IrField) { if (field.origin == IrDeclarationOrigin.FAKE_OVERRIDE) return val fieldType = typeMapper.mapType(field) @@ -228,10 +224,7 @@ open class ClassCodegen protected constructor( val descriptor = field.metadata?.descriptor if (descriptor != null) { - val codegen = if (field.origin != IrDeclarationOrigin.DELEGATE && JvmAbi.isPropertyWithBackingFieldInOuterClass(descriptor)) { - companionObjectCodegen ?: error("Class with a property moved from the companion must have a companion:\n${irClass.dump()}") - } else this - codegen.visitor.serializationBindings.put(JvmSerializationBindings.FIELD_FOR_PROPERTY, descriptor, fieldType to fieldName) + state.globalSerializationBindings.put(JvmSerializationBindings.FIELD_FOR_PROPERTY, descriptor, fieldType to fieldName) } } @@ -240,21 +233,14 @@ open class ClassCodegen protected constructor( val signature = FunctionCodegen(method, this).generate().asmMethod - val metadata = method.metadata - when (metadata) { + when (val metadata = method.metadata) { is MetadataSource.Property -> { // We can't check for JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS because for interface methods // moved to DefaultImpls, origin is changed to DEFAULT_IMPLS // TODO: fix origin somehow, because otherwise $annotations methods in interfaces also don't have ACC_SYNTHETIC assert(method.name.asString().endsWith(JvmAbi.ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX)) { method.dump() } - val codegen = if (DescriptorUtils.isInterface(metadata.descriptor.containingDeclaration)) { - assert(irClass.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS) { irClass.dump() } - parentClassCodegen!! - } else { - this - } - codegen.visitor.serializationBindings.put( + state.globalSerializationBindings.put( JvmSerializationBindings.SYNTHETIC_METHOD_FOR_PROPERTY, metadata.descriptor, signature ) }