JVM IR: replace unnecessary property accessor calls with field access
Split the ConstAndJvmFieldPropertiesLowering into two: ConstLowering which replaces const vals with their values, and PropertiesToFieldsLowering which removes unnecessary property accessors (such as for JvmField or private properties with default accessors) and replaces calls to those accessors with field access
This commit is contained in:
committed by
Alexander Udalov
parent
1c143310ac
commit
ce7d8a6874
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.jvm
|
package org.jetbrains.kotlin.backend.jvm
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.*
|
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||||
import org.jetbrains.kotlin.backend.common.lower.*
|
import org.jetbrains.kotlin.backend.common.lower.*
|
||||||
import org.jetbrains.kotlin.backend.common.phaser.*
|
import org.jetbrains.kotlin.backend.common.phaser.*
|
||||||
import org.jetbrains.kotlin.backend.jvm.lower.*
|
import org.jetbrains.kotlin.backend.jvm.lower.*
|
||||||
@@ -46,7 +46,8 @@ internal val jvmPhases = namedIrFilePhase(
|
|||||||
jvmLateinitPhase then
|
jvmLateinitPhase then
|
||||||
|
|
||||||
moveCompanionObjectFieldsPhase then
|
moveCompanionObjectFieldsPhase then
|
||||||
constAndJvmFieldPropertiesPhase then
|
constPhase then
|
||||||
|
propertiesToFieldsPhase then
|
||||||
propertiesPhase then
|
propertiesPhase then
|
||||||
annotationPhase then
|
annotationPhase then
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. 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.phaser.makeIrFilePhase
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
|
||||||
|
internal val constPhase = makeIrFilePhase(
|
||||||
|
::ConstLowering,
|
||||||
|
name = "Const",
|
||||||
|
description = "Substitute calls to const properties with constant values"
|
||||||
|
)
|
||||||
|
|
||||||
|
class ConstLowering(val context: CommonBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
|
||||||
|
override fun lower(irFile: IrFile) {
|
||||||
|
irFile.transformChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
|
val irSimpleFunction = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression)
|
||||||
|
val irProperty = irSimpleFunction.correspondingProperty ?: return super.visitCall(expression)
|
||||||
|
|
||||||
|
if (irProperty.isConst) {
|
||||||
|
(irProperty.backingField!!.initializer!!.expression as IrConst<*>).let { return it }
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.visitCall(expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
+34
-20
@@ -10,9 +10,14 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
|||||||
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.phaser.makeIrFilePhase
|
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.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
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.expressions.impl.*
|
||||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||||
@@ -20,45 +25,54 @@ 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 constAndJvmFieldPropertiesPhase = makeIrFilePhase(
|
internal val propertiesToFieldsPhase = makeIrFilePhase(
|
||||||
::ConstAndJvmFieldPropertiesLowering,
|
::PropertiesToFieldsLowering,
|
||||||
name = "ConstAndJvmFieldProperties",
|
name = "PropertiesToFields",
|
||||||
description = "Substitute calls to const and Jvm>Field properties with const/field access"
|
description = "Replace calls to default property accessors with field access and remove those accessors"
|
||||||
)
|
)
|
||||||
|
|
||||||
private class ConstAndJvmFieldPropertiesLowering(val context: CommonBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
|
class PropertiesToFieldsLowering(val context: CommonBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
|
||||||
override fun lower(irFile: IrFile) {
|
override fun lower(irFile: IrFile) {
|
||||||
irFile.transformChildrenVoid(this)
|
irFile.transformChildrenVoid(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitProperty(declaration: IrProperty): IrStatement {
|
override fun visitProperty(declaration: IrProperty): IrStatement {
|
||||||
if (declaration.isConst || declaration.backingField?.hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) == true) {
|
if (declaration.isConst || shouldSubstituteAccessorWithField(declaration, declaration.getter)) {
|
||||||
/*Safe or need copy?*/
|
|
||||||
declaration.getter = null
|
declaration.getter = null
|
||||||
|
}
|
||||||
|
if (declaration.isConst || shouldSubstituteAccessorWithField(declaration, declaration.setter)) {
|
||||||
declaration.setter = null
|
declaration.setter = null
|
||||||
}
|
}
|
||||||
return super.visitProperty(declaration)
|
return super.visitProperty(declaration)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall): IrExpression {
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
val irSimpleFunction = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression)
|
val simpleFunction = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression)
|
||||||
val irProperty = irSimpleFunction.correspondingProperty ?: return super.visitCall(expression)
|
val property = simpleFunction.correspondingProperty ?: return super.visitCall(expression)
|
||||||
|
|
||||||
if (irProperty.isConst) {
|
if (shouldSubstituteAccessorWithField(property, simpleFunction)) {
|
||||||
(irProperty.backingField!!.initializer!!.expression as IrConst<*>).let { return it }
|
when (expression) {
|
||||||
}
|
is IrGetterCallImpl -> return substituteGetter(property, expression)
|
||||||
|
is IrSetterCallImpl -> return substituteSetter(property, expression)
|
||||||
if (irProperty.backingField?.hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) == true) {
|
|
||||||
return if (expression is IrGetterCallImpl) {
|
|
||||||
substituteGetter(irProperty, expression)
|
|
||||||
} else {
|
|
||||||
assert(expression is IrSetterCallImpl)
|
|
||||||
substituteSetter(irProperty, expression)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.visitCall(expression)
|
return super.visitCall(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun shouldSubstituteAccessorWithField(property: IrProperty, accessor: IrSimpleFunction?): Boolean {
|
||||||
|
if (accessor == null) return false
|
||||||
|
|
||||||
|
// In contrast to the old backend, we do generate getters for lateinit properties, which fixes KT-28331
|
||||||
|
if (property.isLateinit) 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 {
|
private fun substituteSetter(irProperty: IrProperty, expression: IrCall): IrExpression {
|
||||||
val backingField = irProperty.backingField!!
|
val backingField = irProperty.backingField!!
|
||||||
val receiver = expression.dispatchReceiver?.transform(this, null)
|
val receiver = expression.dispatchReceiver?.transform(this, null)
|
||||||
@@ -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
|
|
||||||
// Checks that methods 'access$getMy$p', 'access$getMy$cp' and 'getMy' are not generated and
|
// Checks that methods 'access$getMy$p', 'access$getMy$cp' and 'getMy' are not generated and
|
||||||
// that backed field 'my' is directly used through a 'getstatic'
|
// that backed field 'my' is directly used through a 'getstatic'
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
package a
|
package a
|
||||||
|
|
||||||
class A {
|
class A {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
package a
|
package a
|
||||||
|
|
||||||
class A {
|
class A {
|
||||||
|
|||||||
Reference in New Issue
Block a user