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.
This commit is contained in:
Alexander Udalov
2019-07-24 15:18:37 +02:00
parent babbbd0ff6
commit e1fc2c57cb
3 changed files with 30 additions and 37 deletions
@@ -38,6 +38,7 @@ import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method import org.jetbrains.org.objectweb.asm.commons.Method
class JvmSerializerExtension(private val bindings: JvmSerializationBindings, state: GenerationState) : SerializerExtension() { class JvmSerializerExtension(private val bindings: JvmSerializationBindings, state: GenerationState) : SerializerExtension() {
private val globalBindings = state.globalSerializationBindings
private val codegenBinding = state.bindingContext private val codegenBinding = state.bindingContext
private val typeMapper = state.typeMapper private val typeMapper = state.typeMapper
override val stringTable = JvmCodegenStringTable(typeMapper) override val stringTable = JvmCodegenStringTable(typeMapper)
@@ -151,10 +152,10 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
} }
} }
override fun serializeConstructor(descriptor: ConstructorDescriptor, override fun serializeConstructor(
proto: ProtoBuf.Constructor.Builder, descriptor: ConstructorDescriptor, proto: ProtoBuf.Constructor.Builder, childSerializer: DescriptorSerializer
childSerializer: DescriptorSerializer) { ) {
val method = bindings.get(METHOD_FOR_FUNCTION, descriptor) val method = getBinding(METHOD_FOR_FUNCTION, descriptor)
if (method != null) { if (method != null) {
val signature = SignatureSerializer().methodSignature(descriptor, method) val signature = SignatureSerializer().methodSignature(descriptor, method)
if (signature != null) { if (signature != null) {
@@ -163,10 +164,10 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
} }
} }
override fun serializeFunction(descriptor: FunctionDescriptor, override fun serializeFunction(
proto: ProtoBuf.Function.Builder, descriptor: FunctionDescriptor, proto: ProtoBuf.Function.Builder, childSerializer: DescriptorSerializer
childSerializer: DescriptorSerializer) { ) {
val method = bindings.get(METHOD_FOR_FUNCTION, descriptor) val method = getBinding(METHOD_FOR_FUNCTION, descriptor)
if (method != null) { if (method != null) {
val signature = SignatureSerializer().methodSignature(descriptor, method) val signature = SignatureSerializer().methodSignature(descriptor, method)
if (signature != null) { if (signature != null) {
@@ -176,20 +177,20 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
} }
override fun serializeProperty( override fun serializeProperty(
descriptor: PropertyDescriptor, descriptor: PropertyDescriptor,
proto: ProtoBuf.Property.Builder, proto: ProtoBuf.Property.Builder,
versionRequirementTable: MutableVersionRequirementTable?, versionRequirementTable: MutableVersionRequirementTable?,
childSerializer: DescriptorSerializer childSerializer: DescriptorSerializer
) { ) {
val signatureSerializer = SignatureSerializer() val signatureSerializer = SignatureSerializer()
val getter = descriptor.getter val getter = descriptor.getter
val setter = descriptor.setter val setter = descriptor.setter
val getterMethod = if (getter == null) null else bindings.get(METHOD_FOR_FUNCTION, getter) val getterMethod = if (getter == null) null else getBinding(METHOD_FOR_FUNCTION, getter)
val setterMethod = if (setter == null) null else bindings.get(METHOD_FOR_FUNCTION, setter) val setterMethod = if (setter == null) null else getBinding(METHOD_FOR_FUNCTION, setter)
val field = bindings.get(FIELD_FOR_PROPERTY, descriptor) val field = getBinding(FIELD_FOR_PROPERTY, descriptor)
val syntheticMethod = bindings.get(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor) val syntheticMethod = getBinding(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor)
val signature = signatureSerializer.propertySignature( val signature = signatureSerializer.propertySignature(
descriptor, descriptor,
@@ -232,6 +233,9 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
super.serializeErrorType(type, builder) super.serializeErrorType(type, builder)
} }
private fun <K, V> getBinding(slice: SerializationMappingSlice<K, V>, key: K): V? =
bindings.get(slice, key) ?: globalBindings.get(slice, key)
private inner class SignatureSerializer { private inner class SignatureSerializer {
fun methodSignature(descriptor: FunctionDescriptor?, method: Method): JvmProtoBuf.JvmMethodSignature? { fun methodSignature(descriptor: FunctionDescriptor?, method: Method): JvmProtoBuf.JvmMethodSignature? {
val builder = JvmProtoBuf.JvmMethodSignature.newBuilder() val builder = JvmProtoBuf.JvmMethodSignature.newBuilder()
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.codegen.inline.GlobalInlineContext
import org.jetbrains.kotlin.codegen.inline.InlineCache import org.jetbrains.kotlin.codegen.inline.InlineCache
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.optimization.OptimizationClassBuilderFactory import org.jetbrains.kotlin.codegen.optimization.OptimizationClassBuilderFactory
import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings
import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.ScriptDescriptor import org.jetbrains.kotlin.descriptors.ScriptDescriptor
@@ -256,6 +257,8 @@ class GenerationState private constructor(
val metadataVersion = configuration.get(CommonConfigurationKeys.METADATA_VERSION) ?: JvmMetadataVersion.INSTANCE val metadataVersion = configuration.get(CommonConfigurationKeys.METADATA_VERSION) ?: JvmMetadataVersion.INSTANCE
val globalSerializationBindings = JvmSerializationBindings()
init { init {
this.interceptedBuilderFactory = builderFactory this.interceptedBuilderFactory = builderFactory
.wrapWith( .wrapWith(
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.backend.jvm.codegen package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext 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.backend.jvm.lower.constantValue
import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.binding.CodegenBinding 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.java.JvmAbi
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.name.SpecialNames 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.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.TRANSIENT_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 val shortName = File(fileEntry.name).name
visitor.visitSource(shortName, null) visitor.visitSource(shortName, null)
val companionObjectCodegen = nestedClasses.firstOrNull { it.irClass.isCompanion }
for (declaration in irClass.declarations) { 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 // 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) { when (declaration) {
is IrField -> is IrField ->
generateField(declaration, companionObjectCodegen) generateField(declaration)
is IrFunction -> { is IrFunction -> {
generateMethod(declaration) generateMethod(declaration)
} }
@@ -207,7 +203,7 @@ open class ClassCodegen protected constructor(
ClassCodegen(klass, context, this).generate() ClassCodegen(klass, context, this).generate()
} }
private fun generateField(field: IrField, companionObjectCodegen: ClassCodegen?) { private fun generateField(field: IrField) {
if (field.origin == IrDeclarationOrigin.FAKE_OVERRIDE) return if (field.origin == IrDeclarationOrigin.FAKE_OVERRIDE) return
val fieldType = typeMapper.mapType(field) val fieldType = typeMapper.mapType(field)
@@ -228,10 +224,7 @@ open class ClassCodegen protected constructor(
val descriptor = field.metadata?.descriptor val descriptor = field.metadata?.descriptor
if (descriptor != null) { if (descriptor != null) {
val codegen = if (field.origin != IrDeclarationOrigin.DELEGATE && JvmAbi.isPropertyWithBackingFieldInOuterClass(descriptor)) { state.globalSerializationBindings.put(JvmSerializationBindings.FIELD_FOR_PROPERTY, descriptor, fieldType to fieldName)
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)
} }
} }
@@ -240,21 +233,14 @@ open class ClassCodegen protected constructor(
val signature = FunctionCodegen(method, this).generate().asmMethod val signature = FunctionCodegen(method, this).generate().asmMethod
val metadata = method.metadata when (val metadata = method.metadata) {
when (metadata) {
is MetadataSource.Property -> { is MetadataSource.Property -> {
// We can't check for JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS because for interface methods // We can't check for JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS because for interface methods
// moved to DefaultImpls, origin is changed to DEFAULT_IMPLS // 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 // 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() } assert(method.name.asString().endsWith(JvmAbi.ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX)) { method.dump() }
val codegen = if (DescriptorUtils.isInterface(metadata.descriptor.containingDeclaration)) { state.globalSerializationBindings.put(
assert(irClass.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS) { irClass.dump() }
parentClassCodegen!!
} else {
this
}
codegen.visitor.serializationBindings.put(
JvmSerializationBindings.SYNTHETIC_METHOD_FOR_PROPERTY, metadata.descriptor, signature JvmSerializationBindings.SYNTHETIC_METHOD_FOR_PROPERTY, metadata.descriptor, signature
) )
} }