From 49b534e894c8e26a7f496e77243bde6ef6da42d5 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 24 Jul 2019 14:53:46 +0200 Subject: [PATCH] JVM IR: move non-private const properties from multifile part to facade --- .../kotlin/backend/jvm/JvmBackendFacade.kt | 29 +++++---- .../jvm/lower/GenerateMultifileFacades.kt | 59 ++++++++++++++++++- ...onstPropertyReferenceFromMultifileClass.kt | 13 ++-- 3 files changed, 81 insertions(+), 20 deletions(-) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendFacade.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendFacade.kt index d947c57704b..64cc4639c50 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendFacade.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendFacade.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen +import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry import org.jetbrains.kotlin.codegen.CompilationErrorHandler import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.ir.builders.declarations.buildClass @@ -85,18 +86,26 @@ object JvmBackendFacade { errorHandler.reportException(e, null) } - for (irFile in irModuleFragment.files) { - try { - for (loweredClass in irFile.declarations) { - if (loweredClass !is IrClass) { - throw AssertionError("File-level declaration should be IrClass after JvmLower, got: " + loweredClass.render()) - } + for (generateMultifileFacade in listOf(true, false)) { + for (irFile in irModuleFragment.files) { + // Generate multifile facades first, to compute and store JVM signatures of const properties which are later used + // when serializing metadata in the multifile parts. + // TODO: consider dividing codegen itself into separate phases (bytecode generation, metadata serialization) to avoid this + val isMultifileFacade = irFile.fileEntry is MultifileFacadeFileEntry + if (isMultifileFacade != generateMultifileFacade) continue - ClassCodegen.generate(loweredClass, context) + try { + for (loweredClass in irFile.declarations) { + if (loweredClass !is IrClass) { + throw AssertionError("File-level declaration should be IrClass after JvmLower, got: " + loweredClass.render()) + } + + ClassCodegen.generate(loweredClass, context) + } + state.afterIndependentPart() + } catch (e: Throwable) { + errorHandler.reportException(e, null) // TODO ktFile.virtualFile.url } - state.afterIndependentPart() - } catch (e: Throwable) { - errorHandler.reportException(e, null) // TODO ktFile.virtualFile.url } } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/GenerateMultifileFacades.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/GenerateMultifileFacades.kt index 907a85eecf8..9ea586fca17 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/GenerateMultifileFacades.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/GenerateMultifileFacades.kt @@ -5,8 +5,10 @@ package org.jetbrains.kotlin.backend.jvm.lower +import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.deepCopyWithWrappedDescriptors import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor +import org.jetbrains.kotlin.backend.common.lower import org.jetbrains.kotlin.backend.common.lower.InitializersLowering import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig @@ -28,9 +30,17 @@ import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irReturn import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrGetField +import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl +import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols import org.jetbrains.kotlin.ir.util.dump +import org.jetbrains.kotlin.ir.util.transformFlat +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.resolve.jvm.JvmClassName internal val generateMultifileFacadesPhase = namedIrModulePhase( @@ -44,7 +54,9 @@ internal val generateMultifileFacadesPhase = namedIrModulePhase( context: JvmBackendContext, input: IrModuleFragment ): IrModuleFragment { - input.files.addAll(generateMultifileFacades(input.descriptor, context)) + val movedFields = mutableMapOf() + input.files.addAll(generateMultifileFacades(input.descriptor, context, movedFields)) + UpdateFieldCallSites(movedFields).lower(input) context.multifileFacadesToAdd.clear() return input } @@ -71,7 +83,11 @@ internal class MultifileFacadeFileEntry( error("Multifile facade doesn't support debug info: $className") } -private fun generateMultifileFacades(module: ModuleDescriptor, context: JvmBackendContext): List = +private fun generateMultifileFacades( + module: ModuleDescriptor, + context: JvmBackendContext, + movedFields: MutableMap +): List = context.multifileFacadesToAdd.map { (jvmClassName, partClasses) -> val fileEntry = MultifileFacadeFileEntry(jvmClassName, partClasses.map(IrClass::fileParent)) val file = IrFileImpl(fileEntry, EmptyPackageFragmentDescriptor(module, jvmClassName.packageFqName)) @@ -87,6 +103,8 @@ private fun generateMultifileFacades(module: ModuleDescriptor, context: JvmBacke for (partClass in partClasses) { context.multifileFacadeForPart[partClass.attributeOwnerId as IrClass] = jvmClassName + moveFieldsOfConstProperties(partClass, facadeClass, movedFields) + for (member in partClass.declarations) { member.createMultifileDelegateIfNeeded(context, facadeClass) } @@ -95,6 +113,28 @@ private fun generateMultifileFacades(module: ModuleDescriptor, context: JvmBacke file } +private fun moveFieldsOfConstProperties( + partClass: IrClass, + facadeClass: IrClass, + movedFields: MutableMap +) { + partClass.declarations.transformFlat { member -> + if (member is IrField && member.shouldMoveToFacade()) { + val field = member.deepCopyWithSymbols(facadeClass).also { + (it as IrFieldImpl).metadata = member.metadata + } + facadeClass.declarations.add(field) + movedFields[member.symbol] = field.symbol + emptyList() + } else null + } +} + +private fun IrField.shouldMoveToFacade(): Boolean { + val property = correspondingPropertySymbol?.owner + return property != null && property.isConst && !Visibilities.isPrivate(visibility) +} + private fun IrDeclaration.createMultifileDelegateIfNeeded(context: JvmBackendContext, facadeClass: IrClass) { if (this !is IrFunction || Visibilities.isPrivate(visibility) || @@ -135,3 +175,18 @@ private fun IrFunction.computeFunctionForCall(): IrFunction { else -> error("Property accessor must be getter or setter: ${dump()}") } } + +private class UpdateFieldCallSites( + private val movedFields: Map +) : FileLoweringPass, IrElementTransformerVoid() { + override fun lower(irFile: IrFile) { + irFile.transformChildrenVoid(this) + } + + override fun visitGetField(expression: IrGetField): IrExpression { + val newField = movedFields[expression.symbol] ?: return super.visitGetField(expression) + return expression.run { + IrGetFieldImpl(startOffset, endOffset, newField, type, receiver, origin, superQualifierSymbol) + } + } +} diff --git a/compiler/testData/compileKotlinAgainstKotlin/constPropertyReferenceFromMultifileClass.kt b/compiler/testData/compileKotlinAgainstKotlin/constPropertyReferenceFromMultifileClass.kt index e2d7069e457..e72e0a12217 100644 --- a/compiler/testData/compileKotlinAgainstKotlin/constPropertyReferenceFromMultifileClass.kt +++ b/compiler/testData/compileKotlinAgainstKotlin/constPropertyReferenceFromMultifileClass.kt @@ -16,13 +16,10 @@ import a.OK fun box(): String { val okRef = ::OK - // TODO: see KT-10892 -// val annotations = okRef.annotations -// val numAnnotations = annotations.size -// if (numAnnotations != 1) { -// throw AssertionError("Failed, annotations: $annotations") -// } + val annotations = okRef.annotations + if (annotations.size != 1) { + throw AssertionError("Failed, annotations: $annotations") + } - val result = okRef.get() - return result + return okRef.get() }