JVM IR: move non-private const properties from multifile part to facade

This commit is contained in:
Alexander Udalov
2019-07-24 14:53:46 +02:00
parent f01b0f6467
commit 49b534e894
3 changed files with 81 additions and 20 deletions
@@ -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.ir.createParameterDeclarations
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen 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.CompilationErrorHandler
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.ir.builders.declarations.buildClass import org.jetbrains.kotlin.ir.builders.declarations.buildClass
@@ -85,18 +86,26 @@ object JvmBackendFacade {
errorHandler.reportException(e, null) errorHandler.reportException(e, null)
} }
for (irFile in irModuleFragment.files) { for (generateMultifileFacade in listOf(true, false)) {
try { for (irFile in irModuleFragment.files) {
for (loweredClass in irFile.declarations) { // Generate multifile facades first, to compute and store JVM signatures of const properties which are later used
if (loweredClass !is IrClass) { // when serializing metadata in the multifile parts.
throw AssertionError("File-level declaration should be IrClass after JvmLower, got: " + loweredClass.render()) // 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
} }
} }
} }
@@ -5,8 +5,10 @@
package org.jetbrains.kotlin.backend.jvm.lower 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.deepCopyWithWrappedDescriptors
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor 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.InitializersLowering
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig 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.irGet
import org.jetbrains.kotlin.ir.builders.irReturn import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.* 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.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.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.dump 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 import org.jetbrains.kotlin.resolve.jvm.JvmClassName
internal val generateMultifileFacadesPhase = namedIrModulePhase( internal val generateMultifileFacadesPhase = namedIrModulePhase(
@@ -44,7 +54,9 @@ internal val generateMultifileFacadesPhase = namedIrModulePhase(
context: JvmBackendContext, context: JvmBackendContext,
input: IrModuleFragment input: IrModuleFragment
): IrModuleFragment { ): IrModuleFragment {
input.files.addAll(generateMultifileFacades(input.descriptor, context)) val movedFields = mutableMapOf<IrFieldSymbol, IrFieldSymbol>()
input.files.addAll(generateMultifileFacades(input.descriptor, context, movedFields))
UpdateFieldCallSites(movedFields).lower(input)
context.multifileFacadesToAdd.clear() context.multifileFacadesToAdd.clear()
return input return input
} }
@@ -71,7 +83,11 @@ internal class MultifileFacadeFileEntry(
error("Multifile facade doesn't support debug info: $className") error("Multifile facade doesn't support debug info: $className")
} }
private fun generateMultifileFacades(module: ModuleDescriptor, context: JvmBackendContext): List<IrFile> = private fun generateMultifileFacades(
module: ModuleDescriptor,
context: JvmBackendContext,
movedFields: MutableMap<IrFieldSymbol, IrFieldSymbol>
): List<IrFile> =
context.multifileFacadesToAdd.map { (jvmClassName, partClasses) -> context.multifileFacadesToAdd.map { (jvmClassName, partClasses) ->
val fileEntry = MultifileFacadeFileEntry(jvmClassName, partClasses.map(IrClass::fileParent)) val fileEntry = MultifileFacadeFileEntry(jvmClassName, partClasses.map(IrClass::fileParent))
val file = IrFileImpl(fileEntry, EmptyPackageFragmentDescriptor(module, jvmClassName.packageFqName)) val file = IrFileImpl(fileEntry, EmptyPackageFragmentDescriptor(module, jvmClassName.packageFqName))
@@ -87,6 +103,8 @@ private fun generateMultifileFacades(module: ModuleDescriptor, context: JvmBacke
for (partClass in partClasses) { for (partClass in partClasses) {
context.multifileFacadeForPart[partClass.attributeOwnerId as IrClass] = jvmClassName context.multifileFacadeForPart[partClass.attributeOwnerId as IrClass] = jvmClassName
moveFieldsOfConstProperties(partClass, facadeClass, movedFields)
for (member in partClass.declarations) { for (member in partClass.declarations) {
member.createMultifileDelegateIfNeeded(context, facadeClass) member.createMultifileDelegateIfNeeded(context, facadeClass)
} }
@@ -95,6 +113,28 @@ private fun generateMultifileFacades(module: ModuleDescriptor, context: JvmBacke
file file
} }
private fun moveFieldsOfConstProperties(
partClass: IrClass,
facadeClass: IrClass,
movedFields: MutableMap<IrFieldSymbol, IrFieldSymbol>
) {
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) { private fun IrDeclaration.createMultifileDelegateIfNeeded(context: JvmBackendContext, facadeClass: IrClass) {
if (this !is IrFunction || if (this !is IrFunction ||
Visibilities.isPrivate(visibility) || Visibilities.isPrivate(visibility) ||
@@ -135,3 +175,18 @@ private fun IrFunction.computeFunctionForCall(): IrFunction {
else -> error("Property accessor must be getter or setter: ${dump()}") else -> error("Property accessor must be getter or setter: ${dump()}")
} }
} }
private class UpdateFieldCallSites(
private val movedFields: Map<IrFieldSymbol, IrFieldSymbol>
) : 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)
}
}
}
@@ -16,13 +16,10 @@ import a.OK
fun box(): String { fun box(): String {
val okRef = ::OK val okRef = ::OK
// TODO: see KT-10892 val annotations = okRef.annotations
// val annotations = okRef.annotations if (annotations.size != 1) {
// val numAnnotations = annotations.size throw AssertionError("Failed, annotations: $annotations")
// if (numAnnotations != 1) { }
// throw AssertionError("Failed, annotations: $annotations")
// }
val result = okRef.get() return okRef.get()
return result
} }