JVM_IR: fix NPE in interface companion initializers
* When referencing the companion itself, they should use the $$INSTANCE field, not the (null until <clinit> returns) Companion field of the interface. * Interface companion init blocks should be made static.
This commit is contained in:
-30
@@ -17,7 +17,6 @@
|
|||||||
package org.jetbrains.kotlin.backend.common.lower
|
package org.jetbrains.kotlin.backend.common.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.BackendContext
|
import org.jetbrains.kotlin.backend.common.BackendContext
|
||||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
@@ -32,8 +31,6 @@ import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
|||||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||||
import org.jetbrains.kotlin.ir.util.defaultType
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
@@ -213,30 +210,3 @@ fun ParameterDescriptor.copyAsValueParameter(newOwner: CallableDescriptor, index
|
|||||||
)
|
)
|
||||||
else -> throw Error("Unexpected parameter descriptor: $this")
|
else -> throw Error("Unexpected parameter descriptor: $this")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun IrBody.replaceThisByStaticReference(
|
|
||||||
context: CommonBackendContext,
|
|
||||||
irClass: IrClass,
|
|
||||||
oldThisReceiverParameter: IrValueParameter
|
|
||||||
): IrBody =
|
|
||||||
transform(ReplaceThisByStaticReference(context, irClass, oldThisReceiverParameter), null)
|
|
||||||
|
|
||||||
private class ReplaceThisByStaticReference(
|
|
||||||
val context: CommonBackendContext,
|
|
||||||
val irClass: IrClass,
|
|
||||||
val oldThisReceiverParameter: IrValueParameter
|
|
||||||
) : IrElementTransformer<Nothing?> {
|
|
||||||
override fun visitGetValue(expression: IrGetValue, data: Nothing?): IrExpression {
|
|
||||||
val irGetValue = expression
|
|
||||||
if (irGetValue.symbol == oldThisReceiverParameter.symbol) {
|
|
||||||
val instanceField = context.declarationFactory.getFieldForObjectInstance(irClass)
|
|
||||||
return IrGetFieldImpl(
|
|
||||||
expression.startOffset,
|
|
||||||
expression.endOffset,
|
|
||||||
instanceField.symbol,
|
|
||||||
irClass.defaultType
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return super.visitGetValue(irGetValue, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -46,5 +46,6 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
|
|||||||
object GENERATED_ASSERTION_ENABLED_FIELD : IrDeclarationOriginImpl("GENERATED_ASSERTION_ENABLED_FIELD", isSynthetic = true)
|
object GENERATED_ASSERTION_ENABLED_FIELD : IrDeclarationOriginImpl("GENERATED_ASSERTION_ENABLED_FIELD", isSynthetic = true)
|
||||||
object GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD : IrDeclarationOriginImpl("GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD", isSynthetic = true)
|
object GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD : IrDeclarationOriginImpl("GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD", isSynthetic = true)
|
||||||
object SUSPEND_FUNCTION_VIEW : IrDeclarationOriginImpl("SUSPEND_FUNCTION_VIEW")
|
object SUSPEND_FUNCTION_VIEW : IrDeclarationOriginImpl("SUSPEND_FUNCTION_VIEW")
|
||||||
object SUSPEND_IMPL_STATIC_FUNCTION : IrDeclarationOriginImpl("SUSPEND_IMPL_STATIC_FUNCTION", isSynthetic = true)
|
object SUSPEND_IMPL_STATIC_FUNCTION : IrDeclarationOriginImpl("SUSPEND_IMPL_STATIC_FUNCTION", isSynthetic = true)
|
||||||
|
object INTERFACE_COMPANION_PRIVATE_INSTANCE : IrDeclarationOriginImpl("INTERFACE_COMPANION_PRIVATE_INSTANCE", isSynthetic = true)
|
||||||
}
|
}
|
||||||
|
|||||||
+18
@@ -34,6 +34,7 @@ class JvmDeclarationFactory(
|
|||||||
private val methodSignatureMapper: MethodSignatureMapper
|
private val methodSignatureMapper: MethodSignatureMapper
|
||||||
) : DeclarationFactory {
|
) : DeclarationFactory {
|
||||||
private val singletonFieldDeclarations = HashMap<IrSymbolOwner, IrField>()
|
private val singletonFieldDeclarations = 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>()
|
||||||
|
|
||||||
@@ -134,6 +135,23 @@ class JvmDeclarationFactory(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getPrivateFieldForObjectInstance(singleton: IrClass): IrField =
|
||||||
|
if (singleton.isCompanion && singleton.parentAsClass.isJvmInterface)
|
||||||
|
interfaceCompanionFieldDeclarations.getOrPut(singleton) {
|
||||||
|
buildField {
|
||||||
|
name = Name.identifier("\$\$INSTANCE")
|
||||||
|
type = singleton.defaultType
|
||||||
|
origin = JvmLoweredDeclarationOrigin.INTERFACE_COMPANION_PRIVATE_INSTANCE
|
||||||
|
isFinal = true
|
||||||
|
isStatic = true
|
||||||
|
visibility = JavaVisibilities.PACKAGE_VISIBILITY
|
||||||
|
}.apply {
|
||||||
|
parent = singleton
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
getFieldForObjectInstance(singleton)
|
||||||
|
|
||||||
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" }
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.builders.Scope
|
|||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
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.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||||
@@ -25,6 +26,7 @@ import org.jetbrains.kotlin.ir.types.*
|
|||||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||||
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
|
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_DEFAULT_FQ_NAME
|
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_DEFAULT_FQ_NAME
|
||||||
|
|
||||||
@@ -148,3 +150,23 @@ fun IrExpression.isSmartcastFromHigherThanNullable(context: JvmBackendContext) =
|
|||||||
this is IrTypeOperatorCall &&
|
this is IrTypeOperatorCall &&
|
||||||
operator == IrTypeOperator.IMPLICIT_CAST &&
|
operator == IrTypeOperator.IMPLICIT_CAST &&
|
||||||
!this.argument.type.isSubtypeOf(type.makeNullable(), context.irBuiltIns)
|
!this.argument.type.isSubtypeOf(type.makeNullable(), context.irBuiltIns)
|
||||||
|
|
||||||
|
fun IrBody.replaceThisByStaticReference(
|
||||||
|
context: JvmBackendContext,
|
||||||
|
irClass: IrClass,
|
||||||
|
oldThisReceiverParameter: IrValueParameter
|
||||||
|
): IrBody =
|
||||||
|
transform(object : IrElementTransformerVoid() {
|
||||||
|
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||||
|
if (expression.symbol == oldThisReceiverParameter.symbol) {
|
||||||
|
val instanceField = context.declarationFactory.getPrivateFieldForObjectInstance(irClass)
|
||||||
|
return IrGetFieldImpl(
|
||||||
|
expression.startOffset,
|
||||||
|
expression.endOffset,
|
||||||
|
instanceField.symbol,
|
||||||
|
irClass.defaultType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return super.visitGetValue(expression)
|
||||||
|
}
|
||||||
|
}, null)
|
||||||
|
|||||||
+1
-1
@@ -10,12 +10,12 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
|||||||
import org.jetbrains.kotlin.backend.common.ir.*
|
import org.jetbrains.kotlin.backend.common.ir.*
|
||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||||
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.common.runOnFilePostfix
|
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.isInCurrentModule
|
import org.jetbrains.kotlin.backend.jvm.ir.isInCurrentModule
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.descriptors.Visibility
|
import org.jetbrains.kotlin.descriptors.Visibility
|
||||||
|
|||||||
+17
-55
@@ -6,10 +6,10 @@
|
|||||||
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.CommonBackendContext
|
|
||||||
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.JvmBackendContext
|
||||||
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.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
|
||||||
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
|||||||
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
||||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||||
import org.jetbrains.kotlin.ir.util.isObject
|
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.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
|
||||||
@@ -40,7 +41,7 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
private class MoveOrCopyCompanionObjectFieldsLowering(val context: CommonBackendContext) : ClassLoweringPass {
|
private class MoveOrCopyCompanionObjectFieldsLowering(val context: JvmBackendContext) : 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) {
|
||||||
@@ -76,7 +77,7 @@ private class MoveOrCopyCompanionObjectFieldsLowering(val context: CommonBackend
|
|||||||
// It is an error to annotate only some of the fields of an interface companion 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 newParent = if (irClass.isJvmInterface && !companion.allFieldsAreJvmField()) companion else irClass
|
||||||
|
|
||||||
val newDeclarations = companion.declarations.mapNotNull {
|
val newDeclarations = companion.declarations.map {
|
||||||
when (it) {
|
when (it) {
|
||||||
is IrProperty ->
|
is IrProperty ->
|
||||||
movePropertyFieldToStaticParent(it, companion, newParent, fieldReplacementMap)
|
movePropertyFieldToStaticParent(it, companion, newParent, fieldReplacementMap)
|
||||||
@@ -87,10 +88,16 @@ private class MoveOrCopyCompanionObjectFieldsLowering(val context: CommonBackend
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move declarations to parent if required
|
if (newParent === companion) {
|
||||||
if (newParent !== companion) {
|
// Keep fields as children of `IrProperty`, but replace anonymous initializers with static ones,
|
||||||
|
// preserving the relative ordering of anonymous initializers and property initializers.
|
||||||
|
for ((i, declaration) in newDeclarations.withIndex())
|
||||||
|
if (declaration is IrAnonymousInitializer)
|
||||||
|
companion.declarations[i] = declaration
|
||||||
|
} else {
|
||||||
|
// Move all touched declarations to the parent.
|
||||||
companion.declarations.removeAll { it is IrAnonymousInitializer }
|
companion.declarations.removeAll { it is IrAnonymousInitializer }
|
||||||
newParent.declarations += newDeclarations
|
newDeclarations.filterNotNullTo(newParent.declarations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,57 +160,12 @@ private class MoveOrCopyCompanionObjectFieldsLowering(val context: CommonBackend
|
|||||||
isStatic = true
|
isStatic = true
|
||||||
).apply {
|
).apply {
|
||||||
parent = newParent
|
parent = newParent
|
||||||
body = oldInitializer.body.transferToNewParent(oldParent, newParent)
|
body = oldInitializer.body
|
||||||
|
.replaceThisByStaticReference(context, oldParent, oldParent.thisReceiver!!)
|
||||||
|
.patchDeclarationParents(newParent) as IrBlockBody
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrBlockBody.transferToNewParent(oldParent: IrClass, newParent: IrClass): IrBlockBody {
|
|
||||||
val objectInstanceField = context.declarationFactory.getFieldForObjectInstance(oldParent)
|
|
||||||
return transform(
|
|
||||||
data = null,
|
|
||||||
transformer = object : IrElementTransformerVoid() {
|
|
||||||
val variableMap = mutableMapOf<IrVariable, IrVariable>()
|
|
||||||
|
|
||||||
override fun visitVariable(declaration: IrVariable): IrStatement {
|
|
||||||
if (declaration.parent == oldParent) {
|
|
||||||
val newDescriptor = WrappedVariableDescriptor(declaration.descriptor.annotations, declaration.descriptor.source)
|
|
||||||
val newVariable = IrVariableImpl(
|
|
||||||
declaration.startOffset, declaration.endOffset,
|
|
||||||
declaration.origin, IrVariableSymbolImpl(newDescriptor),
|
|
||||||
declaration.name, declaration.type, declaration.isVar, declaration.isConst, declaration.isLateinit
|
|
||||||
).apply {
|
|
||||||
newDescriptor.bind(this)
|
|
||||||
parent = newParent
|
|
||||||
initializer = declaration.initializer
|
|
||||||
annotations.addAll(declaration.annotations)
|
|
||||||
}
|
|
||||||
variableMap[declaration] = newVariable
|
|
||||||
return super.visitVariable(newVariable)
|
|
||||||
}
|
|
||||||
return super.visitVariable(declaration)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
|
||||||
if (expression.symbol.owner == oldParent.thisReceiver) {
|
|
||||||
return IrGetFieldImpl(
|
|
||||||
expression.startOffset, expression.endOffset,
|
|
||||||
objectInstanceField.symbol,
|
|
||||||
expression.type
|
|
||||||
)
|
|
||||||
}
|
|
||||||
variableMap[expression.symbol.owner]?.let { newVariable ->
|
|
||||||
return IrGetValueImpl(
|
|
||||||
expression.startOffset, expression.endOffset,
|
|
||||||
expression.type,
|
|
||||||
newVariable.symbol,
|
|
||||||
expression.origin
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return super.visitGetValue(expression)
|
|
||||||
}
|
|
||||||
}) as IrBlockBody
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createStaticBackingField(oldField: IrField, propertyParent: IrClass, fieldParent: IrClass): IrField {
|
private fun createStaticBackingField(oldField: IrField, propertyParent: IrClass, fieldParent: IrClass): IrField {
|
||||||
val descriptor = WrappedFieldDescriptor(oldField.descriptor.annotations, oldField.descriptor.source)
|
val descriptor = WrappedFieldDescriptor(oldField.descriptor.annotations, oldField.descriptor.source)
|
||||||
val field = IrFieldImpl(
|
val field = IrFieldImpl(
|
||||||
|
|||||||
+9
-35
@@ -10,17 +10,11 @@ import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
|||||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
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.descriptors.*
|
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
|
||||||
import org.jetbrains.kotlin.ir.builders.irCall
|
import org.jetbrains.kotlin.ir.builders.irCall
|
||||||
import org.jetbrains.kotlin.ir.builders.irExprBody
|
import org.jetbrains.kotlin.ir.builders.irExprBody
|
||||||
import org.jetbrains.kotlin.ir.builders.irGetField
|
import org.jetbrains.kotlin.ir.builders.irGetField
|
||||||
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.descriptors.WrappedFieldDescriptor
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
|
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
|
||||||
internal val objectClassPhase = makeIrFilePhase(
|
internal val objectClassPhase = makeIrFilePhase(
|
||||||
@@ -49,39 +43,20 @@ private class ObjectClassLowering(val context: JvmBackendContext) : IrElementTra
|
|||||||
if (!irClass.isObject) return
|
if (!irClass.isObject) return
|
||||||
|
|
||||||
val publicInstanceField = context.declarationFactory.getFieldForObjectInstance(irClass)
|
val publicInstanceField = context.declarationFactory.getFieldForObjectInstance(irClass)
|
||||||
|
val privateInstanceField = context.declarationFactory.getPrivateFieldForObjectInstance(irClass)
|
||||||
|
|
||||||
val constructor = irClass.constructors.find { it.isPrimary }
|
val constructor = irClass.constructors.find { it.isPrimary }
|
||||||
?: throw AssertionError("Object should have a primary constructor: ${irClass.name}")
|
?: throw AssertionError("Object should have a primary constructor: ${irClass.name}")
|
||||||
|
|
||||||
val publicInstanceOwner = if (irClass.isCompanion) parentScope!!.irElement as IrDeclarationContainer else irClass
|
if (privateInstanceField != publicInstanceField) {
|
||||||
if (irClass.isCompanion && irClass.parentAsClass.isJvmInterface) {
|
with(context.createIrBuilder(privateInstanceField.symbol)) {
|
||||||
// TODO rename to $$INSTANCE
|
privateInstanceField.initializer = irExprBody(irCall(constructor.symbol))
|
||||||
// TODO: mark as synthesized
|
|
||||||
val privateFieldDescriptor = WrappedFieldDescriptor()
|
|
||||||
val privateField = IrFieldImpl(
|
|
||||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
|
||||||
IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE,
|
|
||||||
IrFieldSymbolImpl(privateFieldDescriptor),
|
|
||||||
publicInstanceField.name,
|
|
||||||
irClass.defaultType,
|
|
||||||
Visibilities.PROTECTED/* TODO package local */,
|
|
||||||
isFinal = true,
|
|
||||||
isExternal = false,
|
|
||||||
isStatic = true,
|
|
||||||
isFakeOverride = false
|
|
||||||
).apply {
|
|
||||||
privateFieldDescriptor.bind(this)
|
|
||||||
parent = irClass
|
|
||||||
with(context.createIrBuilder(symbol)) {
|
|
||||||
initializer = irExprBody(
|
|
||||||
irCall(constructor)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
pendingTransformations.add { parentAsClass.declarations.add(this) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
with(context.createIrBuilder(publicInstanceField.symbol)) {
|
with(context.createIrBuilder(publicInstanceField.symbol)) {
|
||||||
publicInstanceField.initializer = irExprBody(irGetField(null, privateField))
|
publicInstanceField.initializer = irExprBody(irGetField(null, privateInstanceField))
|
||||||
|
}
|
||||||
|
pendingTransformations.add {
|
||||||
|
(privateInstanceField.parent as IrDeclarationContainer).declarations.add(0, privateInstanceField)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
with(context.createIrBuilder(publicInstanceField.symbol)) {
|
with(context.createIrBuilder(publicInstanceField.symbol)) {
|
||||||
@@ -89,9 +64,8 @@ private class ObjectClassLowering(val context: JvmBackendContext) : IrElementTra
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publicInstanceField.parent = publicInstanceOwner
|
|
||||||
pendingTransformations.add {
|
pendingTransformations.add {
|
||||||
publicInstanceOwner.declarations.add(publicInstanceField)
|
(publicInstanceField.parent as IrDeclarationContainer).declarations.add(0, publicInstanceField)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+14
-4
@@ -52,18 +52,28 @@ private class SingletonReferencesLowering(val context: JvmBackendContext) : File
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
|
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
|
||||||
val instanceField = context.declarationFactory.getFieldForObjectInstance(expression.symbol.owner)
|
// When in an instance method of the object, use the dispatch receiver. Do not use the dispatch
|
||||||
|
// receiver in the constructor, as in the case of an `object` the only way it can be used is
|
||||||
|
// in a super constructor call argument (=> it has JVM type "uninitialized this" and is not usable)
|
||||||
|
// while for a `companion object` all initializers are in `<clinit>` and have no receiver at all.
|
||||||
|
thisOfClass(expression.symbol.owner, false)?.let {
|
||||||
|
return IrGetValueImpl(expression.startOffset, expression.endOffset, it.symbol)
|
||||||
|
}
|
||||||
|
val instanceField = if (allScopes.any { it.irElement == expression.symbol.owner })
|
||||||
|
context.declarationFactory.getPrivateFieldForObjectInstance(expression.symbol.owner) // Constructor or static method.
|
||||||
|
else
|
||||||
|
context.declarationFactory.getFieldForObjectInstance(expression.symbol.owner) // Not in object scope at all.
|
||||||
return IrGetFieldImpl(expression.startOffset, expression.endOffset, instanceField.symbol, expression.type)
|
return IrGetFieldImpl(expression.startOffset, expression.endOffset, instanceField.symbol, expression.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
// `this` is generally available while the reference is within the lexical scope of the containing enum entry.
|
// `this` is generally available while the reference is within the lexical scope of the containing class.
|
||||||
private fun thisOfClass(declaration: IrClass?): IrValueParameter? {
|
private fun thisOfClass(declaration: IrClass?, allowConstructorReceiver: Boolean = true): IrValueParameter? {
|
||||||
if (declaration == null) return null
|
if (declaration == null) return null
|
||||||
for (scope in allScopes.reversed()) {
|
for (scope in allScopes.reversed()) {
|
||||||
when (val element = scope.irElement) {
|
when (val element = scope.irElement) {
|
||||||
is IrFunction ->
|
is IrFunction ->
|
||||||
element.dispatchReceiverParameter?.let { if (it.type.classOrNull == declaration.symbol) return it }
|
element.dispatchReceiverParameter?.let { if (it.type.classOrNull == declaration.symbol) return it }
|
||||||
is IrClass -> if (element == declaration) return element.thisReceiver
|
is IrClass -> if (allowConstructorReceiver && element == declaration) return element.thisReceiver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// Inside of the companion we have to access the instance through the local Companion field,
|
// Inside of the companion we have to access the instance through the local Companion field,
|
||||||
// not by indirection through the Companion field of the enclosing class.
|
// not by indirection through the Companion field of the enclosing class.
|
||||||
// Class initialization might not have finished yet.
|
// Class initialization might not have finished yet.
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// !LANGUAGE: +NestedClassesInAnnotations
|
// !LANGUAGE: +NestedClassesInAnnotations
|
||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
interface Test {
|
interface Test {
|
||||||
companion object {
|
companion object {
|
||||||
fun ok() = "OK"
|
fun ok() = "OK"
|
||||||
|
|||||||
Reference in New Issue
Block a user