JVM_IR: refactor MoveCompanionObjectFieldsLowering
* Extract replacement of IrGetField/IrSetField into a separate file-level lowering (should reduce the amount of work to linear in the number of classes rather than potentially quadratic) * Extract static backing field construction into JvmDeclarationFactory and move that lowering after PropertiesToFields lowering to reduce code duplication
This commit is contained in:
@@ -225,6 +225,7 @@ private val jvmFilePhases =
|
|||||||
propertyReferencePhase then
|
propertyReferencePhase then
|
||||||
constPhase then
|
constPhase then
|
||||||
propertiesToFieldsPhase then
|
propertiesToFieldsPhase then
|
||||||
|
remapObjectFieldAccesses then
|
||||||
propertiesPhase then
|
propertiesPhase then
|
||||||
renameFieldsPhase then
|
renameFieldsPhase then
|
||||||
anonymousObjectSuperConstructorPhase then
|
anonymousObjectSuperConstructorPhase then
|
||||||
|
|||||||
+33
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.ir.*
|
|||||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper
|
import org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
|
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference
|
||||||
import org.jetbrains.kotlin.builtins.CompanionObjectMapping.isMappedIntrinsicCompanionObject
|
import org.jetbrains.kotlin.builtins.CompanionObjectMapping.isMappedIntrinsicCompanionObject
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
@@ -22,6 +23,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.*
|
|||||||
import org.jetbrains.kotlin.ir.descriptors.WrappedClassConstructorDescriptor
|
import org.jetbrains.kotlin.ir.descriptors.WrappedClassConstructorDescriptor
|
||||||
import org.jetbrains.kotlin.ir.descriptors.WrappedClassDescriptor
|
import org.jetbrains.kotlin.ir.descriptors.WrappedClassDescriptor
|
||||||
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
|
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.*
|
import org.jetbrains.kotlin.ir.symbols.impl.*
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
@@ -37,6 +39,7 @@ class JvmDeclarationFactory(
|
|||||||
private val interfaceCompanionFieldDeclarations = HashMap<IrSymbolOwner, IrField>()
|
private val interfaceCompanionFieldDeclarations = HashMap<IrSymbolOwner, IrField>()
|
||||||
private val outerThisDeclarations = HashMap<IrClass, IrField>()
|
private val outerThisDeclarations = HashMap<IrClass, IrField>()
|
||||||
private val innerClassConstructors = HashMap<IrConstructor, IrConstructor>()
|
private val innerClassConstructors = HashMap<IrConstructor, IrConstructor>()
|
||||||
|
private val staticBackingFields = HashMap<IrProperty, IrField>()
|
||||||
|
|
||||||
private val defaultImplsMethods = HashMap<IrSimpleFunction, IrSimpleFunction>()
|
private val defaultImplsMethods = HashMap<IrSimpleFunction, IrSimpleFunction>()
|
||||||
private val defaultImplsClasses = HashMap<IrClass, IrClass>()
|
private val defaultImplsClasses = HashMap<IrClass, IrClass>()
|
||||||
@@ -152,6 +155,36 @@ class JvmDeclarationFactory(
|
|||||||
else
|
else
|
||||||
getFieldForObjectInstance(singleton)
|
getFieldForObjectInstance(singleton)
|
||||||
|
|
||||||
|
fun getStaticBackingField(irProperty: IrProperty): IrField? {
|
||||||
|
// Only fields defined directly in objects should be made static.
|
||||||
|
// Fake overrides never point to those, as objects are final.
|
||||||
|
if (irProperty.origin == IrDeclarationOrigin.FAKE_OVERRIDE) return null
|
||||||
|
val oldField = irProperty.backingField ?: return null
|
||||||
|
val oldParent = irProperty.parent as? IrClass ?: return null
|
||||||
|
if (!oldParent.isObject) return null
|
||||||
|
return staticBackingFields.getOrPut(irProperty) {
|
||||||
|
buildField {
|
||||||
|
updateFrom(oldField)
|
||||||
|
name = oldField.name
|
||||||
|
isStatic = true
|
||||||
|
}.apply {
|
||||||
|
// We don't move fields to interfaces unless all fields are annotated with @JvmField.
|
||||||
|
// It is an error to annotate only some of the fields of an interface companion with
|
||||||
|
// @JvmField, so checking the current field only should be enough.
|
||||||
|
val hasJvmField = oldField.hasAnnotation(JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME)
|
||||||
|
parent = if (oldParent.isCompanion && (!oldParent.parentAsClass.isJvmInterface || hasJvmField))
|
||||||
|
oldParent.parentAsClass
|
||||||
|
else
|
||||||
|
oldParent
|
||||||
|
annotations += oldField.annotations
|
||||||
|
initializer = oldField.initializer
|
||||||
|
?.replaceThisByStaticReference(this@JvmDeclarationFactory, oldParent, oldParent.thisReceiver!!)
|
||||||
|
?.deepCopyWithSymbols(this) as IrExpressionBody?
|
||||||
|
(this as IrFieldImpl).metadata = oldField.metadata
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getDefaultImplsFunction(interfaceFun: IrSimpleFunction): IrSimpleFunction {
|
fun getDefaultImplsFunction(interfaceFun: IrSimpleFunction): IrSimpleFunction {
|
||||||
val parent = interfaceFun.parentAsClass
|
val parent = interfaceFun.parentAsClass
|
||||||
assert(parent.isJvmInterface) { "Parent of ${interfaceFun.dump()} should be interface" }
|
assert(parent.isJvmInterface) { "Parent of ${interfaceFun.dump()} should be interface" }
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.lower.IrLoweringContext
|
|||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmSymbols
|
import org.jetbrains.kotlin.backend.jvm.JvmSymbols
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
|
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.descriptors.JvmDeclarationFactory
|
||||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.hasMangledParameters
|
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.hasMangledParameters
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
@@ -152,14 +153,14 @@ fun IrExpression.isSmartcastFromHigherThanNullable(context: JvmBackendContext) =
|
|||||||
!this.argument.type.isSubtypeOf(type.makeNullable(), context.irBuiltIns)
|
!this.argument.type.isSubtypeOf(type.makeNullable(), context.irBuiltIns)
|
||||||
|
|
||||||
fun IrBody.replaceThisByStaticReference(
|
fun IrBody.replaceThisByStaticReference(
|
||||||
context: JvmBackendContext,
|
declarationFactory: JvmDeclarationFactory,
|
||||||
irClass: IrClass,
|
irClass: IrClass,
|
||||||
oldThisReceiverParameter: IrValueParameter
|
oldThisReceiverParameter: IrValueParameter
|
||||||
): IrBody =
|
): IrBody =
|
||||||
transform(object : IrElementTransformerVoid() {
|
transform(object : IrElementTransformerVoid() {
|
||||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||||
if (expression.symbol == oldThisReceiverParameter.symbol) {
|
if (expression.symbol == oldThisReceiverParameter.symbol) {
|
||||||
val instanceField = context.declarationFactory.getPrivateFieldForObjectInstance(irClass)
|
val instanceField = declarationFactory.getPrivateFieldForObjectInstance(irClass)
|
||||||
return IrGetFieldImpl(
|
return IrGetFieldImpl(
|
||||||
expression.startOffset,
|
expression.startOffset,
|
||||||
expression.endOffset,
|
expression.endOffset,
|
||||||
|
|||||||
+1
-1
@@ -181,7 +181,7 @@ private class SingletonObjectJvmStaticLowering(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun modifyBody(irFunction: IrFunction, irClass: IrClass, oldDispatchReceiverParameter: IrValueParameter) {
|
fun modifyBody(irFunction: IrFunction, irClass: IrClass, oldDispatchReceiverParameter: IrValueParameter) {
|
||||||
irFunction.body = irFunction.body?.replaceThisByStaticReference(context, irClass, oldDispatchReceiverParameter)
|
irFunction.body = irFunction.body?.replaceThisByStaticReference(context.declarationFactory, irClass, oldDispatchReceiverParameter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+76
-166
@@ -6,34 +6,20 @@
|
|||||||
package org.jetbrains.kotlin.backend.jvm.lower
|
package org.jetbrains.kotlin.backend.jvm.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
|
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference
|
import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.builders.declarations.addField
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrAnonymousInitializerImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrAnonymousInitializerImpl
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
|
||||||
import org.jetbrains.kotlin.ir.descriptors.WrappedFieldDescriptor
|
|
||||||
import org.jetbrains.kotlin.ir.descriptors.WrappedVariableDescriptor
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
|
||||||
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
|
||||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
|
||||||
import org.jetbrains.kotlin.ir.util.isObject
|
|
||||||
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME
|
|
||||||
|
|
||||||
internal val moveOrCopyCompanionObjectFieldsPhase = makeIrFilePhase(
|
internal val moveOrCopyCompanionObjectFieldsPhase = makeIrFilePhase(
|
||||||
::MoveOrCopyCompanionObjectFieldsLowering,
|
::MoveOrCopyCompanionObjectFieldsLowering,
|
||||||
@@ -41,186 +27,110 @@ internal val moveOrCopyCompanionObjectFieldsPhase = makeIrFilePhase(
|
|||||||
description = "Move and/or copy companion object fields to static fields of companion's owner"
|
description = "Move and/or copy companion object fields to static fields of companion's owner"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal val remapObjectFieldAccesses = makeIrFilePhase(
|
||||||
|
::RemapObjectFieldAccesses,
|
||||||
|
name = "RemapObjectFieldAccesses",
|
||||||
|
description = "Make IrGetField/IrSetField to objects' fields point to the static versions",
|
||||||
|
prerequisite = setOf(propertiesToFieldsPhase)
|
||||||
|
)
|
||||||
|
|
||||||
private class MoveOrCopyCompanionObjectFieldsLowering(val context: JvmBackendContext) : ClassLoweringPass {
|
private class MoveOrCopyCompanionObjectFieldsLowering(val context: JvmBackendContext) : ClassLoweringPass {
|
||||||
override fun lower(irClass: IrClass) {
|
override fun lower(irClass: IrClass) {
|
||||||
val fieldReplacementMap = mutableMapOf<IrFieldSymbol, IrFieldSymbol>()
|
if (irClass.isObject && !irClass.isCompanion) {
|
||||||
if (irClass.isObject && !irClass.isCompanion && irClass.visibility != Visibilities.LOCAL) {
|
irClass.handle()
|
||||||
handleObject(irClass, fieldReplacementMap)
|
|
||||||
} else {
|
} else {
|
||||||
handleClass(irClass, fieldReplacementMap)
|
(irClass.declarations.singleOrNull { it is IrClass && it.isCompanion } as IrClass?)?.handle()
|
||||||
if (irClass.isJvmInterface)
|
|
||||||
copyConsts(irClass)
|
|
||||||
}
|
}
|
||||||
irClass.replaceFieldReferences(fieldReplacementMap)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleObject(irObject: IrClass, fieldReplacementMap: MutableMap<IrFieldSymbol, IrFieldSymbol>) {
|
private fun IrClass.handle() {
|
||||||
irObject.declarations.replaceAll {
|
val newDeclarations = declarations.map {
|
||||||
when (it) {
|
when (it) {
|
||||||
is IrProperty -> {
|
is IrProperty -> context.declarationFactory.getStaticBackingField(it)?.also { newField ->
|
||||||
// The field is not actually moved, just replaced by a static field
|
it.backingField = newField
|
||||||
movePropertyFieldToStaticParent(it, irObject, irObject, fieldReplacementMap)
|
newField.correspondingPropertySymbol = it.symbol
|
||||||
it
|
|
||||||
}
|
}
|
||||||
is IrAnonymousInitializer -> moveAnonymousInitializerToStaticParent(it, irObject, irObject)
|
else -> null
|
||||||
else -> it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleClass(irClass: IrClass, fieldReplacementMap: MutableMap<IrFieldSymbol, IrFieldSymbol>) {
|
|
||||||
val companion = irClass.declarations.find {
|
|
||||||
it is IrClass && it.isCompanion
|
|
||||||
} as IrClass? ?: return
|
|
||||||
|
|
||||||
// We don't move fields to interfaces unless all fields are annotated with @JvmField.
|
|
||||||
// It is an error to annotate only some of the fields of an interface companion with @JvmField.
|
|
||||||
val newParent = if (irClass.isJvmInterface && !companion.allFieldsAreJvmField()) companion else irClass
|
|
||||||
|
|
||||||
val newDeclarations = companion.declarations.map {
|
|
||||||
when (it) {
|
|
||||||
is IrProperty ->
|
|
||||||
movePropertyFieldToStaticParent(it, companion, newParent, fieldReplacementMap)
|
|
||||||
is IrAnonymousInitializer ->
|
|
||||||
moveAnonymousInitializerToStaticParent(it, companion, newParent)
|
|
||||||
else ->
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newParent === companion) {
|
val companionParent = if (isCompanion) parentAsClass else null
|
||||||
// Keep fields as children of `IrProperty`, but replace anonymous initializers with static ones,
|
// In case a companion contains no fields, move the anonymous initializers to the parent
|
||||||
// preserving the relative ordering of anonymous initializers and property initializers.
|
// anyway, as otherwise there will probably be no references to the companion class at all
|
||||||
for ((i, declaration) in newDeclarations.withIndex())
|
// and therefore its class initializer will never be invoked.
|
||||||
if (declaration is IrAnonymousInitializer)
|
val newParent = newDeclarations.firstOrNull { it != null }?.parentAsClass ?: companionParent ?: this
|
||||||
companion.declarations[i] = declaration
|
if (newParent === this) {
|
||||||
|
declarations.replaceAll {
|
||||||
|
// Anonymous initializers must be made static to correctly initialize the new static
|
||||||
|
// fields when the class is loaded.
|
||||||
|
if (it is IrAnonymousInitializer) makeAnonymousInitializerStatic(it, newParent) else it
|
||||||
|
}
|
||||||
|
|
||||||
|
if (companionParent != null) {
|
||||||
|
for (declaration in declarations) {
|
||||||
|
if (declaration is IrProperty && declaration.isConst && declaration.hasPublicVisibility) {
|
||||||
|
copyConstField(declaration.backingField!!, companionParent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Move all touched declarations to the parent.
|
// Anonymous initializers must also be moved and their ordering relative to the fields
|
||||||
companion.declarations.removeAll { it is IrAnonymousInitializer }
|
// must be preserved, as the fields can have expression initializers themselves.
|
||||||
newDeclarations.filterNotNullTo(newParent.declarations)
|
for ((i, newField) in newDeclarations.withIndex()) {
|
||||||
|
if (newField != null)
|
||||||
|
newParent.declarations += newField
|
||||||
|
if (declarations[i] is IrAnonymousInitializer)
|
||||||
|
newParent.declarations += makeAnonymousInitializerStatic(declarations[i] as IrAnonymousInitializer, newParent)
|
||||||
|
}
|
||||||
|
declarations.removeAll { it is IrAnonymousInitializer }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyConsts(irClass: IrClass) {
|
|
||||||
val companion = irClass.declarations.find {
|
|
||||||
it is IrClass && it.isCompanion
|
|
||||||
} as IrClass? ?: return
|
|
||||||
companion.declarations.filter { it is IrProperty && it.isConst && it.hasPublicVisibility }
|
|
||||||
.mapNotNullTo(irClass.declarations) { copyPropertyFieldToStaticParent(it as IrProperty, companion, irClass) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private val IrProperty.hasPublicVisibility: Boolean
|
private val IrProperty.hasPublicVisibility: Boolean
|
||||||
get() = !Visibilities.isPrivate(visibility) && visibility != Visibilities.PROTECTED
|
get() = !Visibilities.isPrivate(visibility) && visibility != Visibilities.PROTECTED
|
||||||
|
|
||||||
private fun IrClass.allFieldsAreJvmField() =
|
private fun makeAnonymousInitializerStatic(oldInitializer: IrAnonymousInitializer, newParent: IrClass) =
|
||||||
declarations.filterIsInstance<IrProperty>()
|
|
||||||
.mapNotNull { it.backingField }.all { it.hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) }
|
|
||||||
|
|
||||||
// If fieldReplacementMap is null / unspecified, keep the old field and don't update the references.
|
|
||||||
private fun moveOrCopyPropertyFieldToStaticParent(
|
|
||||||
irProperty: IrProperty,
|
|
||||||
propertyParent: IrClass,
|
|
||||||
fieldParent: IrClass,
|
|
||||||
fieldReplacementMap: MutableMap<IrFieldSymbol, IrFieldSymbol>? = null
|
|
||||||
): IrField? {
|
|
||||||
if (irProperty.origin == IrDeclarationOrigin.FAKE_OVERRIDE) return null
|
|
||||||
val oldField = irProperty.backingField ?: return null
|
|
||||||
val newField = createStaticBackingField(oldField, propertyParent, fieldParent)
|
|
||||||
|
|
||||||
fieldReplacementMap?.run {
|
|
||||||
irProperty.backingField = newField
|
|
||||||
newField.correspondingPropertySymbol = irProperty.symbol
|
|
||||||
put(oldField.symbol, newField.symbol)
|
|
||||||
}
|
|
||||||
|
|
||||||
return newField
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun movePropertyFieldToStaticParent(
|
|
||||||
irProperty: IrProperty,
|
|
||||||
propertyParent: IrClass,
|
|
||||||
fieldParent: IrClass,
|
|
||||||
fieldReplacementMap: MutableMap<IrFieldSymbol, IrFieldSymbol>? = null
|
|
||||||
): IrField? = moveOrCopyPropertyFieldToStaticParent(irProperty, propertyParent, fieldParent, fieldReplacementMap)
|
|
||||||
|
|
||||||
private fun copyPropertyFieldToStaticParent(
|
|
||||||
irProperty: IrProperty,
|
|
||||||
propertyParent: IrClass,
|
|
||||||
fieldParent: IrClass
|
|
||||||
): IrField? = moveOrCopyPropertyFieldToStaticParent(irProperty, propertyParent, fieldParent)
|
|
||||||
|
|
||||||
private fun moveAnonymousInitializerToStaticParent(
|
|
||||||
oldInitializer: IrAnonymousInitializer,
|
|
||||||
oldParent: IrClass,
|
|
||||||
newParent: IrClass
|
|
||||||
): IrAnonymousInitializer =
|
|
||||||
with(oldInitializer) {
|
with(oldInitializer) {
|
||||||
IrAnonymousInitializerImpl(
|
val oldParent = parentAsClass
|
||||||
startOffset, endOffset, origin, IrAnonymousInitializerSymbolImpl(newParent.symbol),
|
val newSymbol = IrAnonymousInitializerSymbolImpl(newParent.symbol)
|
||||||
isStatic = true
|
IrAnonymousInitializerImpl(startOffset, endOffset, origin, newSymbol, isStatic = true).apply {
|
||||||
).apply {
|
|
||||||
parent = newParent
|
parent = newParent
|
||||||
body = oldInitializer.body
|
body = this@with.body
|
||||||
.replaceThisByStaticReference(context, oldParent, oldParent.thisReceiver!!)
|
.replaceThisByStaticReference(context.declarationFactory, oldParent, oldParent.thisReceiver!!)
|
||||||
.patchDeclarationParents(newParent) as IrBlockBody
|
.patchDeclarationParents(newParent) as IrBlockBody
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createStaticBackingField(oldField: IrField, propertyParent: IrClass, fieldParent: IrClass): IrField {
|
private fun copyConstField(oldField: IrField, newParent: IrClass) =
|
||||||
val descriptor = WrappedFieldDescriptor(oldField.descriptor.annotations, oldField.descriptor.source)
|
newParent.addField {
|
||||||
val field = IrFieldImpl(
|
updateFrom(oldField)
|
||||||
oldField.startOffset, oldField.endOffset,
|
name = oldField.name
|
||||||
IrDeclarationOrigin.PROPERTY_BACKING_FIELD,
|
isStatic = true
|
||||||
IrFieldSymbolImpl(descriptor),
|
}.apply {
|
||||||
oldField.name, oldField.type, oldField.visibility,
|
parent = newParent
|
||||||
isFinal = oldField.isFinal,
|
annotations += oldField.annotations
|
||||||
isExternal = oldField.isExternal,
|
initializer = oldField.initializer?.deepCopyWithSymbols(this)
|
||||||
isStatic = true,
|
|
||||||
isFakeOverride = false
|
|
||||||
).apply {
|
|
||||||
descriptor.bind(this)
|
|
||||||
parent = fieldParent
|
|
||||||
annotations.addAll(oldField.annotations)
|
|
||||||
metadata = oldField.metadata
|
|
||||||
}
|
}
|
||||||
val oldInitializer = oldField.initializer
|
|
||||||
if (oldInitializer != null) {
|
|
||||||
field.initializer = oldInitializer
|
|
||||||
.replaceThisByStaticReference(context, propertyParent, propertyParent.thisReceiver!!)
|
|
||||||
.deepCopyWithSymbols(field) as IrExpressionBody
|
|
||||||
}
|
|
||||||
|
|
||||||
return field
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrElement.replaceFieldReferences(replacementMap: Map<IrFieldSymbol, IrFieldSymbol>) {
|
private class RemapObjectFieldAccesses(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() {
|
||||||
transformChildrenVoid(FieldReplacer(replacementMap))
|
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid()
|
||||||
}
|
|
||||||
|
private fun IrField.remap(): IrField? =
|
||||||
|
correspondingPropertySymbol?.owner?.let(context.declarationFactory::getStaticBackingField)
|
||||||
|
|
||||||
private class FieldReplacer(val replacementMap: Map<IrFieldSymbol, IrFieldSymbol>) : IrElementTransformerVoid() {
|
|
||||||
override fun visitGetField(expression: IrGetField): IrExpression =
|
override fun visitGetField(expression: IrGetField): IrExpression =
|
||||||
replacementMap[expression.symbol]?.let { newSymbol ->
|
expression.symbol.owner.remap()?.let {
|
||||||
IrGetFieldImpl(
|
with(expression) {
|
||||||
expression.startOffset, expression.endOffset,
|
IrGetFieldImpl(startOffset, endOffset, it.symbol, type, /* receiver = */ null, origin, superQualifierSymbol)
|
||||||
newSymbol,
|
}
|
||||||
expression.type,
|
|
||||||
/* receiver = */ null,
|
|
||||||
expression.origin,
|
|
||||||
expression.superQualifierSymbol
|
|
||||||
)
|
|
||||||
} ?: super.visitGetField(expression)
|
} ?: super.visitGetField(expression)
|
||||||
|
|
||||||
override fun visitSetField(expression: IrSetField): IrExpression =
|
override fun visitSetField(expression: IrSetField): IrExpression =
|
||||||
replacementMap[expression.symbol]?.let { _ ->
|
expression.symbol.owner.remap()?.let {
|
||||||
IrSetFieldImpl(
|
with(expression) {
|
||||||
expression.startOffset, expression.endOffset,
|
val newValue = value.transform(this@RemapObjectFieldAccesses, null)
|
||||||
replacementMap.getValue(expression.symbol),
|
IrSetFieldImpl(startOffset, endOffset, it.symbol, /* receiver = */ null, newValue, type, origin, superQualifierSymbol)
|
||||||
/* receiver = */ null,
|
}
|
||||||
visitExpression(expression.value),
|
|
||||||
expression.type,
|
|
||||||
expression.origin,
|
|
||||||
expression.superQualifierSymbol
|
|
||||||
)
|
|
||||||
} ?: super.visitSetField(expression)
|
} ?: super.visitSetField(expression)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// FILE: box.kt
|
||||||
|
fun box(): String = Z.result
|
||||||
|
|
||||||
|
// FILE: z.kt
|
||||||
|
object Z {
|
||||||
|
@JvmField
|
||||||
|
val result: String = "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// FILE: box.kt
|
||||||
|
fun box(): String = A.result
|
||||||
|
|
||||||
|
object A {
|
||||||
|
@JvmField
|
||||||
|
val result: String
|
||||||
|
|
||||||
|
init { result = Z.result }
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: z.kt
|
||||||
|
object Z {
|
||||||
|
@JvmField
|
||||||
|
val result: String = "OK"
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: +JvmFieldInInterface +NestedClassesInAnnotations
|
// !LANGUAGE: +JvmFieldInInterface +NestedClassesInAnnotations
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: +JvmFieldInInterface
|
// !LANGUAGE: +JvmFieldInInterface
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|||||||
+10
@@ -15058,6 +15058,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/jvmField/constructorProperty.kt");
|
runTest("compiler/testData/codegen/box/jvmField/constructorProperty.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("fileOrder.kt")
|
||||||
|
public void testFileOrder() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/jvmField/fileOrder.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("fileOrderWithCopying.kt")
|
||||||
|
public void testFileOrderWithCopying() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/jvmField/fileOrderWithCopying.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("initializersOrder.kt")
|
@TestMetadata("initializersOrder.kt")
|
||||||
public void testInitializersOrder() throws Exception {
|
public void testInitializersOrder() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/jvmField/initializersOrder.kt");
|
runTest("compiler/testData/codegen/box/jvmField/initializersOrder.kt");
|
||||||
|
|||||||
+10
@@ -15058,6 +15058,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/jvmField/constructorProperty.kt");
|
runTest("compiler/testData/codegen/box/jvmField/constructorProperty.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("fileOrder.kt")
|
||||||
|
public void testFileOrder() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/jvmField/fileOrder.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("fileOrderWithCopying.kt")
|
||||||
|
public void testFileOrderWithCopying() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/jvmField/fileOrderWithCopying.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("initializersOrder.kt")
|
@TestMetadata("initializersOrder.kt")
|
||||||
public void testInitializersOrder() throws Exception {
|
public void testInitializersOrder() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/jvmField/initializersOrder.kt");
|
runTest("compiler/testData/codegen/box/jvmField/initializersOrder.kt");
|
||||||
|
|||||||
+10
@@ -13913,6 +13913,16 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/jvmField/constructorProperty.kt");
|
runTest("compiler/testData/codegen/box/jvmField/constructorProperty.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("fileOrder.kt")
|
||||||
|
public void testFileOrder() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/jvmField/fileOrder.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("fileOrderWithCopying.kt")
|
||||||
|
public void testFileOrderWithCopying() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/jvmField/fileOrderWithCopying.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("initializersOrder.kt")
|
@TestMetadata("initializersOrder.kt")
|
||||||
public void testInitializersOrder() throws Exception {
|
public void testInitializersOrder() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/jvmField/initializersOrder.kt");
|
runTest("compiler/testData/codegen/box/jvmField/initializersOrder.kt");
|
||||||
|
|||||||
+10
@@ -13913,6 +13913,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/jvmField/constructorProperty.kt");
|
runTest("compiler/testData/codegen/box/jvmField/constructorProperty.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("fileOrder.kt")
|
||||||
|
public void testFileOrder() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/jvmField/fileOrder.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("fileOrderWithCopying.kt")
|
||||||
|
public void testFileOrderWithCopying() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/jvmField/fileOrderWithCopying.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("initializersOrder.kt")
|
@TestMetadata("initializersOrder.kt")
|
||||||
public void testInitializersOrder() throws Exception {
|
public void testInitializersOrder() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/jvmField/initializersOrder.kt");
|
runTest("compiler/testData/codegen/box/jvmField/initializersOrder.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user