From 7ae98485b883f43812d89170887d3c90bb0bd75a Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Tue, 14 Apr 2020 18:57:25 +0300 Subject: [PATCH] IR: separate ClassCodegen & DescriptorBasedClassCodegen --- .../backend/jvm/codegen/ClassCodegen.kt | 95 ++------------- .../codegen/DescriptorBasedClassCodegen.kt | 113 ++++++++++++++++++ 2 files changed, 120 insertions(+), 88 deletions(-) create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/DescriptorBasedClassCodegen.kt 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 9e26252dc61..cb9700073b3 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 @@ -11,10 +11,8 @@ import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry import org.jetbrains.kotlin.backend.jvm.lower.buildAssertionsDisabledField import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField import org.jetbrains.kotlin.codegen.* -import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.inline.* import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings -import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibility @@ -30,8 +28,6 @@ import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.load.java.JvmAbi -import org.jetbrains.kotlin.load.java.JvmAnnotationNames -import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME @@ -40,23 +36,22 @@ import org.jetbrains.kotlin.resolve.jvm.annotations.VOLATILE_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.checkers.JvmSimpleNameBacktickChecker import org.jetbrains.kotlin.resolve.jvm.diagnostics.* import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature -import org.jetbrains.kotlin.serialization.DescriptorSerializer import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.org.objectweb.asm.* import org.jetbrains.org.objectweb.asm.commons.Method import org.jetbrains.org.objectweb.asm.tree.MethodNode import java.io.File -class ClassCodegen private constructor( +abstract class ClassCodegen protected constructor( internal val irClass: IrClass, val context: JvmBackendContext, private val parentFunction: IrFunction? = null ) : InnerClassConsumer { - private val parentClassCodegen = (parentFunction?.parentAsClass ?: irClass.parent as? IrClass)?.let { getOrCreate(it, context) } + protected val parentClassCodegen = (parentFunction?.parentAsClass ?: irClass.parent as? IrClass)?.let { getOrCreate(it, context) } private val withinInline: Boolean = parentClassCodegen?.withinInline == true || parentFunction?.isInline == true - private val state get() = context.state - private val typeMapper get() = context.typeMapper + protected val state get() = context.state + protected val typeMapper get() = context.typeMapper val type: Type = typeMapper.mapClass(irClass) @@ -79,7 +74,7 @@ class ClassCodegen private constructor( } } - private val visitor = state.factory.newVisitor(classOrigin, type, irClass.fileParent.loadSourceFilesInfo()).apply { + protected val visitor = state.factory.newVisitor(classOrigin, type, irClass.fileParent.loadSourceFilesInfo()).apply { val signature = getSignature(irClass, type, irClass.getSuperClassInfo(typeMapper), typeMapper) // Ensure that the backend only produces class names that would be valid in the frontend for JVM. if (context.state.classBuilderMode.generateBodies && signature.hasInvalidName()) { @@ -98,15 +93,6 @@ class ClassCodegen private constructor( internal var writeSourceMap: Boolean = withinInline - private val serializerExtension = JvmSerializerExtension(visitor.serializationBindings, state, typeMapper) - private val serializer: DescriptorSerializer? = - when (val metadata = irClass.metadata) { - is MetadataSource.Class -> DescriptorSerializer.create(metadata.descriptor, serializerExtension, parentClassCodegen?.serializer) - is MetadataSource.File -> DescriptorSerializer.createTopLevel(serializerExtension) - is MetadataSource.Function -> DescriptorSerializer.createForLambda(serializerExtension) - else -> null - } - private var regeneratedObjectNameGenerators = mutableMapOf() fun getRegeneratedObjectNameGenerator(function: IrFunction): NameGenerator { @@ -205,74 +191,7 @@ class ClassCodegen private constructor( return null } - private fun generateKotlinMetadataAnnotation() { - val localDelegatedProperties = (irClass.attributeOwnerId as? IrClass)?.let(context.localDelegatedProperties::get) - if (localDelegatedProperties != null && localDelegatedProperties.isNotEmpty()) { - state.bindingTrace.record(CodegenBinding.DELEGATED_PROPERTIES_WITH_METADATA, type, localDelegatedProperties.map { it.descriptor }) - } - - // TODO: if `-Xmultifile-parts-inherit` is enabled, write the corresponding flag for parts and facades to [Metadata.extraInt]. - var extraFlags = JvmAnnotationNames.METADATA_JVM_IR_FLAG - if (state.isIrWithStableAbi) { - extraFlags += JvmAnnotationNames.METADATA_JVM_IR_STABLE_ABI_FLAG - } - - when (val metadata = irClass.metadata) { - is MetadataSource.Class -> { - val classProto = serializer!!.classProto(metadata.descriptor).build() - writeKotlinMetadata(visitor, state, KotlinClassHeader.Kind.CLASS, extraFlags) { - AsmUtil.writeAnnotationData(it, serializer, classProto) - } - - assert(irClass !in context.classNameOverride) { - "JvmPackageName is not supported for classes: ${irClass.render()}" - } - } - is MetadataSource.File -> { - val packageFqName = irClass.getPackageFragment()!!.fqName - val packageProto = serializer!!.packagePartProto(packageFqName, metadata.descriptors) - - serializerExtension.serializeJvmPackage(packageProto, type) - - val facadeClassName = context.multifileFacadeForPart[irClass.attributeOwnerId] - val kind = if (facadeClassName != null) KotlinClassHeader.Kind.MULTIFILE_CLASS_PART else KotlinClassHeader.Kind.FILE_FACADE - writeKotlinMetadata(visitor, state, kind, extraFlags) { av -> - AsmUtil.writeAnnotationData(av, serializer, packageProto.build()) - - if (facadeClassName != null) { - av.visit(JvmAnnotationNames.METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME, facadeClassName.internalName) - } - - if (irClass in context.classNameOverride) { - av.visit(JvmAnnotationNames.METADATA_PACKAGE_NAME_FIELD_NAME, irClass.fqNameWhenAvailable!!.parent().asString()) - } - } - } - is MetadataSource.Function -> { - val fakeDescriptor = createFreeFakeLambdaDescriptor(metadata.descriptor) - val functionProto = serializer!!.functionProto(fakeDescriptor)?.build() - writeKotlinMetadata(visitor, state, KotlinClassHeader.Kind.SYNTHETIC_CLASS, extraFlags) { - if (functionProto != null) { - AsmUtil.writeAnnotationData(it, serializer, functionProto) - } - } - } - else -> { - val entry = irClass.fileParent.fileEntry - if (entry is MultifileFacadeFileEntry) { - val partInternalNames = entry.partFiles.mapNotNull { partFile -> - val fileClass = partFile.declarations.singleOrNull { it.isFileClass } as IrClass? - if (fileClass != null) typeMapper.mapClass(fileClass).internalName else null - } - MultifileClassCodegenImpl.writeMetadata( - visitor, state, extraFlags, partInternalNames, type, irClass.fqNameWhenAvailable!!.parent() - ) - } else { - writeSyntheticClassMetadata(visitor, state) - } - } - } - } + abstract protected fun generateKotlinMetadataAnnotation() private fun IrFile.loadSourceFilesInfo(): List { val entry = fileEntry @@ -285,7 +204,7 @@ class ClassCodegen private constructor( companion object { fun getOrCreate(irClass: IrClass, context: JvmBackendContext, parentFunction: IrFunction? = null): ClassCodegen = context.classCodegens.getOrPut(irClass) { - ClassCodegen(irClass, context, parentFunction) + DescriptorBasedClassCodegen(irClass, context, parentFunction) }.also { assert(parentFunction == null || it.parentFunction == parentFunction) { "inconsistent parent function for ${irClass.render()}:\n" + diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/DescriptorBasedClassCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/DescriptorBasedClassCodegen.kt new file mode 100644 index 00000000000..77747e91a22 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/DescriptorBasedClassCodegen.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.jvm.codegen + +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry +import org.jetbrains.kotlin.codegen.* +import org.jetbrains.kotlin.codegen.binding.CodegenBinding +import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.MetadataSource +import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable +import org.jetbrains.kotlin.ir.util.getPackageFragment +import org.jetbrains.kotlin.ir.util.isFileClass +import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.load.java.JvmAnnotationNames +import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader +import org.jetbrains.kotlin.serialization.DescriptorSerializer + +class DescriptorBasedClassCodegen internal constructor( + irClass: IrClass, + context: JvmBackendContext, + parentFunction: IrFunction? = null +) : ClassCodegen(irClass, context, parentFunction) { + + private val serializerExtension = JvmSerializerExtension(visitor.serializationBindings, state, typeMapper) + private val serializer: DescriptorSerializer? = + when (val metadata = irClass.metadata) { + is MetadataSource.Class -> DescriptorSerializer.create( + metadata.descriptor, serializerExtension, (parentClassCodegen as? DescriptorBasedClassCodegen)?.serializer + ) + is MetadataSource.File -> DescriptorSerializer.createTopLevel(serializerExtension) + is MetadataSource.Function -> DescriptorSerializer.createForLambda(serializerExtension) + else -> null + } + + override fun generateKotlinMetadataAnnotation() { + + val localDelegatedProperties = (irClass.attributeOwnerId as? IrClass)?.let(context.localDelegatedProperties::get) + if (localDelegatedProperties != null && localDelegatedProperties.isNotEmpty()) { + state.bindingTrace.record( + CodegenBinding.DELEGATED_PROPERTIES_WITH_METADATA, type, localDelegatedProperties.map { it.descriptor } + ) + } + + // TODO: if `-Xmultifile-parts-inherit` is enabled, write the corresponding flag for parts and facades to [Metadata.extraInt]. + var extraFlags = JvmAnnotationNames.METADATA_JVM_IR_FLAG + if (state.isIrWithStableAbi) { + extraFlags += JvmAnnotationNames.METADATA_JVM_IR_STABLE_ABI_FLAG + } + + when (val metadata = irClass.metadata) { + is MetadataSource.Class -> { + val classProto = serializer!!.classProto(metadata.descriptor).build() + writeKotlinMetadata(visitor, state, KotlinClassHeader.Kind.CLASS, extraFlags) { + AsmUtil.writeAnnotationData(it, serializer, classProto) + } + + assert(irClass !in context.classNameOverride) { + "JvmPackageName is not supported for classes: ${irClass.render()}" + } + } + is MetadataSource.File -> { + val packageFqName = irClass.getPackageFragment()!!.fqName + val packageProto = serializer!!.packagePartProto(packageFqName, metadata.descriptors) + + serializerExtension.serializeJvmPackage(packageProto, type) + + val facadeClassName = context.multifileFacadeForPart[irClass.attributeOwnerId] + val kind = if (facadeClassName != null) KotlinClassHeader.Kind.MULTIFILE_CLASS_PART else KotlinClassHeader.Kind.FILE_FACADE + writeKotlinMetadata(visitor, state, kind, extraFlags) { av -> + AsmUtil.writeAnnotationData(av, serializer, packageProto.build()) + + if (facadeClassName != null) { + av.visit(JvmAnnotationNames.METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME, facadeClassName.internalName) + } + + if (irClass in context.classNameOverride) { + av.visit(JvmAnnotationNames.METADATA_PACKAGE_NAME_FIELD_NAME, irClass.fqNameWhenAvailable!!.parent().asString()) + } + } + } + is MetadataSource.Function -> { + val fakeDescriptor = createFreeFakeLambdaDescriptor(metadata.descriptor) + val functionProto = serializer!!.functionProto(fakeDescriptor)?.build() + writeKotlinMetadata(visitor, state, KotlinClassHeader.Kind.SYNTHETIC_CLASS, extraFlags) { + if (functionProto != null) { + AsmUtil.writeAnnotationData(it, serializer, functionProto) + } + } + } + else -> { + val entry = irClass.fileParent.fileEntry + if (entry is MultifileFacadeFileEntry) { + val partInternalNames = entry.partFiles.mapNotNull { partFile -> + val fileClass = partFile.declarations.singleOrNull { it.isFileClass } as IrClass? + if (fileClass != null) typeMapper.mapClass(fileClass).internalName else null + } + MultifileClassCodegenImpl.writeMetadata( + visitor, state, extraFlags, partInternalNames, type, irClass.fqNameWhenAvailable!!.parent() + ) + } else { + writeSyntheticClassMetadata(visitor, state) + } + } + } + } + +} \ No newline at end of file