MoveCompanionObjectFields: copy constants into interfaces

Fields having const attribute in companion objects are observable in
Java and should be copied to outer interfaces.
This commit is contained in:
Ting-Yuan Huang
2019-05-15 16:20:23 -07:00
committed by max-kammerer
parent 6ff776ba65
commit 75646f97a4
9 changed files with 39 additions and 19 deletions
@@ -105,7 +105,7 @@ val jvmPhases = namedIrFilePhase<JvmBackendContext>(
lateinitPhase then lateinitPhase then
moveCompanionObjectFieldsPhase then moveOrCopyCompanionObjectFieldsPhase then
propertyReferencePhase then propertyReferencePhase then
constPhase then constPhase then
propertiesToFieldsPhase then propertiesToFieldsPhase then
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.descriptors.WrappedFieldDescriptor
import org.jetbrains.kotlin.backend.common.descriptors.WrappedVariableDescriptor import org.jetbrains.kotlin.backend.common.descriptors.WrappedVariableDescriptor
import org.jetbrains.kotlin.backend.common.lower.replaceThisByStaticReference import org.jetbrains.kotlin.backend.common.lower.replaceThisByStaticReference
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
@@ -31,19 +32,21 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.load.java.JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME
internal val moveCompanionObjectFieldsPhase = makeIrFilePhase( internal val moveOrCopyCompanionObjectFieldsPhase = makeIrFilePhase(
::MoveCompanionObjectFieldsLowering, ::MoveOrCopyCompanionObjectFieldsLowering,
name = "MoveCompanionObjectFields", name = "MoveOrCopyCompanionObjectFields",
description = "Move 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"
) )
private class MoveCompanionObjectFieldsLowering(val context: CommonBackendContext) : ClassLoweringPass { private class MoveOrCopyCompanionObjectFieldsLowering(val context: CommonBackendContext) : ClassLoweringPass {
override fun lower(irClass: IrClass) { override fun lower(irClass: IrClass) {
val fieldReplacementMap = mutableMapOf<IrFieldSymbol, IrFieldSymbol>() val fieldReplacementMap = mutableMapOf<IrFieldSymbol, IrFieldSymbol>()
if (irClass.isObject && !irClass.isCompanion && irClass.visibility != Visibilities.LOCAL) { if (irClass.isObject && !irClass.isCompanion && irClass.visibility != Visibilities.LOCAL) {
handleObject(irClass, fieldReplacementMap) handleObject(irClass, fieldReplacementMap)
} else { } else {
handleClass(irClass, fieldReplacementMap) handleClass(irClass, fieldReplacementMap)
if (irClass.isJvmInterface)
copyConsts(irClass)
} }
irClass.replaceFieldReferences(fieldReplacementMap) irClass.replaceFieldReferences(fieldReplacementMap)
} }
@@ -83,28 +86,52 @@ private class MoveCompanionObjectFieldsLowering(val context: CommonBackendContex
companion.declarations.removeAll { it is IrAnonymousInitializer } companion.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 }.mapNotNullTo(irClass.declarations) {
copyPropertyFieldToStaticParent(it as IrProperty, companion, irClass)
}
}
private fun IrClass.allFieldsAreJvmField() = private fun IrClass.allFieldsAreJvmField() =
declarations.filterIsInstance<IrProperty>() declarations.filterIsInstance<IrProperty>()
.mapNotNull { it.backingField }.all { it.hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) } .mapNotNull { it.backingField }.all { it.hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) }
private fun movePropertyFieldToStaticParent( // If fieldReplacementMap is null / unspecified, keep the old field and don't update the references.
private fun moveOrCopyPropertyFieldToStaticParent(
irProperty: IrProperty, irProperty: IrProperty,
propertyParent: IrClass, propertyParent: IrClass,
fieldParent: IrClass, fieldParent: IrClass,
fieldReplacementMap: MutableMap<IrFieldSymbol, IrFieldSymbol> fieldReplacementMap: MutableMap<IrFieldSymbol, IrFieldSymbol>? = null
): IrField? { ): IrField? {
if (irProperty.origin == IrDeclarationOrigin.FAKE_OVERRIDE) return null if (irProperty.origin == IrDeclarationOrigin.FAKE_OVERRIDE) return null
val oldField = irProperty.backingField ?: return null val oldField = irProperty.backingField ?: return null
val newField = createStaticBackingField(oldField, propertyParent, fieldParent) val newField = createStaticBackingField(oldField, propertyParent, fieldParent)
irProperty.backingField = newField fieldReplacementMap?.run {
newField.correspondingPropertySymbol = irProperty.symbol irProperty.backingField = newField
newField.correspondingPropertySymbol = irProperty.symbol
fieldReplacementMap[oldField.symbol] = newField.symbol put(oldField.symbol, newField.symbol)
}
return newField 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( private fun moveAnonymousInitializerToStaticParent(
oldInitializer: IrAnonymousInitializer, oldInitializer: IrAnonymousInitializer,
oldParent: IrClass, oldParent: IrClass,
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// FILE: JavaClass.java // FILE: JavaClass.java
@@ -1,5 +1,4 @@
// !LANGUAGE: +NestedClassesInAnnotations // !LANGUAGE: +NestedClassesInAnnotations
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// FILE: Foo.java // FILE: Foo.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// WITH_RUNTIME // WITH_RUNTIME
// FILE: CompanionInitialization.java // FILE: CompanionInitialization.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
package test package test
annotation class AString(val value: String) annotation class AString(val value: String)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
package test package test
interface Test { interface Test {