JVM IR: Merge PropertiesToFieldsLowering and JvmPropertiesLowering
This is necessary in order to fix the corresponding property symbols, since PropertiesToFieldsLowering broke them intentionally.
This commit is contained in:
committed by
Alexander Udalov
parent
d5c2707c2c
commit
c41e71b1c5
@@ -100,10 +100,11 @@ private val lateinitUsageLoweringPhase = makeIrFilePhase(
|
||||
description = "Insert checks for lateinit field references"
|
||||
)
|
||||
|
||||
private val propertiesPhase = makeIrFilePhase(
|
||||
internal val propertiesPhase = makeIrFilePhase(
|
||||
::JvmPropertiesLowering,
|
||||
name = "Properties",
|
||||
description = "Move fields and accessors for properties to their classes, and create synthetic methods for property annotations",
|
||||
description = "Move fields and accessors for properties to their classes, replace calls to default property accessors " +
|
||||
"with field accesses, remove unused accessors and create synthetic methods for property annotations",
|
||||
stickyPostconditions = setOf((PropertiesLowering)::checkNoProperties)
|
||||
)
|
||||
|
||||
@@ -294,9 +295,8 @@ private val jvmFilePhases =
|
||||
inlineCallableReferenceToLambdaPhase then
|
||||
propertyReferencePhase then
|
||||
constPhase then
|
||||
propertiesToFieldsPhase then
|
||||
remapObjectFieldAccesses then
|
||||
propertiesPhase then
|
||||
remapObjectFieldAccesses then
|
||||
anonymousObjectSuperConstructorPhase then
|
||||
tailrecPhase then
|
||||
|
||||
|
||||
+101
-41
@@ -6,76 +6,137 @@
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFieldAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.types.makeNotNull
|
||||
import org.jetbrains.kotlin.ir.types.typeWith
|
||||
import org.jetbrains.kotlin.ir.util.coerceToUnit
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.util.*
|
||||
|
||||
class JvmPropertiesLowering(private val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
|
||||
class JvmPropertiesLowering(private val backendContext: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.accept(this, null)
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass): IrStatement {
|
||||
override fun visitClassNew(declaration: IrClass): IrStatement {
|
||||
declaration.transformChildrenVoid(this)
|
||||
declaration.transformDeclarationsFlat { lowerProperty(it, declaration.kind) }
|
||||
declaration.transformDeclarationsFlat { if (it is IrProperty) lowerProperty(it, declaration.kind) else null }
|
||||
return declaration
|
||||
}
|
||||
|
||||
private fun lowerProperty(declaration: IrDeclaration, kind: ClassKind): List<IrDeclaration>? =
|
||||
if (declaration is IrProperty)
|
||||
ArrayList<IrDeclaration>(4).apply {
|
||||
val field = declaration.backingField
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
val simpleFunction = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression)
|
||||
val property = simpleFunction.correspondingPropertySymbol?.owner ?: return super.visitCall(expression)
|
||||
expression.transformChildrenVoid()
|
||||
|
||||
// JvmFields in a companion object refer to companion's owners and should not be generated within companion.
|
||||
if ((kind != ClassKind.ANNOTATION_CLASS || field?.isStatic == true) && field?.parent == declaration.parent) {
|
||||
addIfNotNull(field)
|
||||
}
|
||||
addIfNotNull(declaration.getter)
|
||||
addIfNotNull(declaration.setter)
|
||||
|
||||
if (!declaration.isFakeOverride && declaration.annotations.isNotEmpty()) {
|
||||
add(createSyntheticMethodForAnnotations(declaration))
|
||||
if (shouldSubstituteAccessorWithField(property, simpleFunction)) {
|
||||
backendContext.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).apply {
|
||||
return when (simpleFunction) {
|
||||
property.getter -> substituteGetter(property, expression)
|
||||
property.setter -> substituteSetter(property, expression)
|
||||
else -> error("Orphaned property getter/setter: ${simpleFunction.render()}")
|
||||
}
|
||||
}
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
private fun createSyntheticMethodForAnnotations(declaration: IrProperty): IrFunctionImpl {
|
||||
val descriptor = WrappedSimpleFunctionDescriptor(declaration.descriptor.annotations)
|
||||
val symbol = IrSimpleFunctionSymbolImpl(descriptor)
|
||||
val name = computeSyntheticMethodName(declaration)
|
||||
return IrFunctionImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS, symbol,
|
||||
Name.identifier(name), declaration.visibility, Modality.OPEN, context.irBuiltIns.unitType,
|
||||
isInline = false, isExternal = false, isTailrec = false, isSuspend = false, isExpect = false, isFakeOverride = false,
|
||||
isOperator = false
|
||||
).apply {
|
||||
descriptor.bind(this)
|
||||
return expression
|
||||
}
|
||||
|
||||
val extensionReceiver = declaration.getter?.extensionReceiverParameter
|
||||
if (extensionReceiver != null) {
|
||||
private fun IrBuilderWithScope.substituteSetter(irProperty: IrProperty, expression: IrCall): IrExpression =
|
||||
patchReceiver(irSetField(expression.dispatchReceiver, irProperty.backingField!!, expression.getValueArgument(0)!!))
|
||||
|
||||
private fun IrBuilderWithScope.substituteGetter(irProperty: IrProperty, expression: IrCall): IrExpression {
|
||||
val backingField = irProperty.backingField!!
|
||||
val value = irGetField(expression.dispatchReceiver, backingField)
|
||||
return if (irProperty.isLateinit) {
|
||||
irBlock {
|
||||
val tmpVal = irTemporary(value)
|
||||
+irIfNull(
|
||||
expression.type.makeNotNull(),
|
||||
irGet(tmpVal),
|
||||
backendContext.throwUninitializedPropertyAccessException(this, backingField.name.asString()),
|
||||
irGet(tmpVal)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.patchReceiver(expression: IrFieldAccessExpression): IrExpression =
|
||||
if (expression.symbol.owner.isStatic && expression.receiver != null) {
|
||||
irBlock {
|
||||
+expression.receiver!!.coerceToUnit(context.irBuiltIns)
|
||||
expression.receiver = null
|
||||
+expression
|
||||
}
|
||||
} else {
|
||||
expression
|
||||
}
|
||||
|
||||
private fun lowerProperty(declaration: IrProperty, kind: ClassKind): List<IrDeclaration>? =
|
||||
ArrayList<IrDeclaration>(4).apply {
|
||||
val field = declaration.backingField
|
||||
|
||||
// JvmFields in a companion object refer to companion's owners and should not be generated within companion.
|
||||
if ((kind != ClassKind.ANNOTATION_CLASS || field?.isStatic == true) && field?.parent == declaration.parent) {
|
||||
addIfNotNull(field)
|
||||
}
|
||||
|
||||
if (!declaration.isConst) {
|
||||
declaration.getter?.takeIf { !shouldSubstituteAccessorWithField(declaration, it) }?.let { add(it) }
|
||||
declaration.setter?.takeIf { !shouldSubstituteAccessorWithField(declaration, it) }?.let { add(it) }
|
||||
}
|
||||
|
||||
if (!declaration.isFakeOverride && declaration.annotations.isNotEmpty()) {
|
||||
add(createSyntheticMethodForAnnotations(declaration))
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldSubstituteAccessorWithField(property: IrProperty, accessor: IrSimpleFunction?): Boolean {
|
||||
if (accessor == null) return false
|
||||
|
||||
if ((property.parent as? IrClass)?.kind == ClassKind.ANNOTATION_CLASS) return false
|
||||
|
||||
if (property.backingField?.hasAnnotation(JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME) == true) return true
|
||||
|
||||
return accessor.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR && Visibilities.isPrivate(accessor.visibility)
|
||||
}
|
||||
|
||||
private fun createSyntheticMethodForAnnotations(declaration: IrProperty): IrFunctionImpl =
|
||||
buildFun {
|
||||
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS
|
||||
name = Name.identifier(computeSyntheticMethodName(declaration))
|
||||
visibility = declaration.visibility
|
||||
modality = Modality.OPEN
|
||||
returnType = backendContext.irBuiltIns.unitType
|
||||
}.apply {
|
||||
declaration.getter?.extensionReceiverParameter?.let { extensionReceiver ->
|
||||
// Use raw type of extension receiver to avoid generic signature, which would be useless for this method.
|
||||
extensionReceiverParameter = extensionReceiver.copyTo(this, type = extensionReceiver.type.classifierOrFail.typeWith())
|
||||
}
|
||||
@@ -86,13 +147,12 @@ class JvmPropertiesLowering(private val context: JvmBackendContext) : IrElementT
|
||||
annotations = declaration.annotations
|
||||
metadata = declaration.metadata
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeSyntheticMethodName(property: IrProperty): String {
|
||||
val baseName =
|
||||
if (context.state.languageVersionSettings.supportsFeature(LanguageFeature.UseGetterNameForPropertyAnnotationsMethodOnJvm)) {
|
||||
if (backendContext.state.languageVersionSettings.supportsFeature(LanguageFeature.UseGetterNameForPropertyAnnotationsMethodOnJvm)) {
|
||||
property.getter?.let { getter ->
|
||||
context.methodSignatureMapper.mapFunctionName(getter)
|
||||
backendContext.methodSignatureMapper.mapFunctionName(getter)
|
||||
} ?: JvmAbi.getterName(property.name.asString())
|
||||
} else {
|
||||
property.name.asString()
|
||||
|
||||
+5
-3
@@ -10,8 +10,8 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference
|
||||
import org.jetbrains.kotlin.backend.jvm.propertiesPhase
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addField
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addProperty
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildField
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -21,7 +21,9 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.util.isObject
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
|
||||
internal val moveOrCopyCompanionObjectFieldsPhase = makeIrFilePhase(
|
||||
@@ -34,7 +36,7 @@ internal val remapObjectFieldAccesses = makeIrFilePhase(
|
||||
::RemapObjectFieldAccesses,
|
||||
name = "RemapObjectFieldAccesses",
|
||||
description = "Make IrGetField/IrSetField to objects' fields point to the static versions",
|
||||
prerequisite = setOf(propertiesToFieldsPhase)
|
||||
prerequisite = setOf(propertiesPhase)
|
||||
)
|
||||
|
||||
private class MoveOrCopyCompanionObjectFieldsLowering(val context: JvmBackendContext) : ClassLoweringPass {
|
||||
|
||||
-142
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFieldAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.types.makeNotNull
|
||||
import org.jetbrains.kotlin.ir.util.coerceToUnit
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
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 propertiesToFieldsPhase = makeIrFilePhase(
|
||||
::PropertiesToFieldsLowering,
|
||||
name = "PropertiesToFields",
|
||||
description = "Replace calls to default property accessors with field access and remove those accessors"
|
||||
)
|
||||
|
||||
class PropertiesToFieldsLowering(val context: CommonBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitProperty(declaration: IrProperty): IrStatement {
|
||||
if (declaration.isConst || shouldSubstituteAccessorWithField(declaration, declaration.getter)) {
|
||||
declaration.getter = null
|
||||
}
|
||||
if (declaration.isConst || shouldSubstituteAccessorWithField(declaration, declaration.setter)) {
|
||||
declaration.setter = null
|
||||
}
|
||||
return super.visitProperty(declaration)
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
val simpleFunction = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression)
|
||||
val property = simpleFunction.correspondingPropertySymbol?.owner ?: return super.visitCall(expression)
|
||||
|
||||
if (shouldSubstituteAccessorWithField(property, simpleFunction)) {
|
||||
// property.getter & property.setter might be erased by the above function.
|
||||
when (simpleFunction.valueParameters.size) {
|
||||
0 -> return substituteGetter(property, expression)
|
||||
1 -> return substituteSetter(property, expression)
|
||||
}
|
||||
}
|
||||
|
||||
return super.visitCall(expression)
|
||||
}
|
||||
|
||||
private fun shouldSubstituteAccessorWithField(property: IrProperty, accessor: IrSimpleFunction?): Boolean {
|
||||
if (accessor == null) return false
|
||||
|
||||
if ((property.parent as? IrClass)?.kind == ClassKind.ANNOTATION_CLASS) return false
|
||||
|
||||
if (property.backingField?.hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) == true) return true
|
||||
|
||||
return accessor.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR && Visibilities.isPrivate(accessor.visibility)
|
||||
}
|
||||
|
||||
private fun substituteSetter(irProperty: IrProperty, expression: IrCall): IrExpression {
|
||||
val backingField = irProperty.backingField!!
|
||||
val receiver = expression.dispatchReceiver?.transform(this, null)
|
||||
val setExpr = IrSetFieldImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
backingField.symbol,
|
||||
receiver,
|
||||
expression.getValueArgument(expression.valueArgumentsCount - 1)!!.transform(this, null),
|
||||
expression.type,
|
||||
expression.origin,
|
||||
expression.superQualifierSymbol
|
||||
)
|
||||
return buildSubstitution(backingField.isStatic, setExpr, receiver)
|
||||
}
|
||||
|
||||
private fun substituteGetter(irProperty: IrProperty, expression: IrCall): IrExpression {
|
||||
val backingField = irProperty.backingField!!
|
||||
val receiver = expression.dispatchReceiver?.transform(this, null)
|
||||
val getExpr = IrGetFieldImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
backingField.symbol,
|
||||
backingField.type,
|
||||
receiver,
|
||||
expression.origin,
|
||||
expression.superQualifierSymbol
|
||||
)
|
||||
val substitution = buildSubstitution(backingField.isStatic, getExpr, receiver)
|
||||
return if (irProperty.isLateinit)
|
||||
insertLateinitCheck(substitution, backingField)
|
||||
else
|
||||
substitution
|
||||
}
|
||||
|
||||
private fun insertLateinitCheck(expression: IrExpression, field: IrField): IrExpression {
|
||||
val backendContext = context
|
||||
val startOffset = expression.startOffset
|
||||
val endOffset = expression.endOffset
|
||||
val irBuilder = context.createIrBuilder(field.symbol, startOffset, endOffset)
|
||||
irBuilder.run {
|
||||
return irBlock(expression) {
|
||||
val tmpVar = irTemporaryVar(expression)
|
||||
+irIfThenElse(
|
||||
expression.type.makeNotNull(),
|
||||
irEqualsNull(irGet(tmpVar)),
|
||||
backendContext.throwUninitializedPropertyAccessException(this, field.name.asString()),
|
||||
irGet(expression.type.makeNotNull(), tmpVar.symbol)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildSubstitution(needBlock: Boolean, setOrGetExpr: IrFieldAccessExpression, receiver: IrExpression?): IrExpression {
|
||||
if (receiver != null && needBlock) {
|
||||
// Evaluate `dispatchReceiver` for the sake of its side effects, then return `setOrGetExpr`.
|
||||
return context.createIrBuilder(setOrGetExpr.symbol, setOrGetExpr.startOffset, setOrGetExpr.endOffset).irBlock(setOrGetExpr) {
|
||||
+receiver.coerceToUnit(context.irBuiltIns)
|
||||
setOrGetExpr.receiver = null
|
||||
+setOrGetExpr
|
||||
}
|
||||
} else {
|
||||
// Just `setOrGetExpr` (`dispatchReceiver` is evaluated as a subexpression thereof)
|
||||
return setOrGetExpr
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user