Skip to content

Commit

Permalink
[JVM] Fix backing field visibility in LateinitLowering
Browse files Browse the repository at this point in the history
^KT-71141
  • Loading branch information
francescoo22 authored and qodana-bot committed Nov 19, 2024
1 parent 17aa734 commit 455b127
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,144 +36,142 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
@PhaseDescription(
name = "LateinitLowering",
)
class LateinitLowering(private val context: CommonBackendContext) : FileLoweringPass {
open class LateinitLowering(private val backendContext: CommonBackendContext) : FileLoweringPass, IrElementTransformerVoid() {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(Transformer(context))
irFile.transformChildrenVoid(this)
}

fun lower(irBody: IrBody) {
irBody.transformChildrenVoid(Transformer(context))
irBody.transformChildrenVoid(this)
}

private class Transformer(private val backendContext: CommonBackendContext) : IrElementTransformerVoid() {
override fun visitProperty(declaration: IrProperty): IrStatement {
if (declaration.isRealLateinit()) {
val backingField = declaration.backingField!!
transformBackingField(backingField, declaration)
override fun visitProperty(declaration: IrProperty): IrStatement {
if (declaration.isRealLateinit()) {
val backingField = declaration.backingField!!
transformLateinitBackingField(backingField, declaration)

declaration.getter?.let {
transformGetter(backingField, it)
}
declaration.getter?.let {
transformGetter(backingField, it)
}

declaration.transformChildrenVoid()
return declaration
}

override fun visitVariable(declaration: IrVariable): IrStatement {
declaration.transformChildrenVoid()
declaration.transformChildrenVoid()
return declaration
}

if (declaration.isLateinit) {
declaration.type = declaration.type.makeNullable()
declaration.isVar = true
declaration.initializer =
IrConstImpl.constNull(declaration.startOffset, declaration.endOffset, backendContext.irBuiltIns.nothingNType)
}
override fun visitVariable(declaration: IrVariable): IrStatement {
declaration.transformChildrenVoid()

return declaration
if (declaration.isLateinit) {
declaration.type = declaration.type.makeNullable()
declaration.isVar = true
declaration.initializer =
IrConstImpl.constNull(declaration.startOffset, declaration.endOffset, backendContext.irBuiltIns.nothingNType)
}

override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid()
return declaration
}

val irValue = expression.symbol.owner
if (irValue !is IrVariable || !irValue.isLateinit) {
return expression
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid()

return backendContext.createIrBuilder(
(irValue.parent as IrSymbolOwner).symbol,
expression.startOffset,
expression.endOffset
).run {
irIfThenElse(
expression.type,
irEqualsNull(irGet(irValue)),
backendContext.throwUninitializedPropertyAccessException(this, irValue.name.asString()),
irGet(irValue)
)
}
val irValue = expression.symbol.owner
if (irValue !is IrVariable || !irValue.isLateinit) {
return expression
}

override fun visitGetField(expression: IrGetField): IrExpression {
expression.transformChildrenVoid()

val irField = expression.symbol.owner
if (irField.isLateinitBackingField()) {
expression.type = expression.type.makeNullable()
}
return expression
return backendContext.createIrBuilder(
(irValue.parent as IrSymbolOwner).symbol,
expression.startOffset,
expression.endOffset
).run {
irIfThenElse(
expression.type,
irEqualsNull(irGet(irValue)),
backendContext.throwUninitializedPropertyAccessException(this, irValue.name.asString()),
irGet(irValue)
)
}
}

private fun IrField.isLateinitBackingField(): Boolean {
val property = this.correspondingPropertySymbol?.owner
return property != null && property.isRealLateinit()
override fun visitGetField(expression: IrGetField): IrExpression {
expression.transformChildrenVoid()

val irField = expression.symbol.owner
if (irField.isLateinitBackingField()) {
expression.type = expression.type.makeNullable()
}
return expression
}

private fun IrField.isLateinitBackingField(): Boolean {
val property = this.correspondingPropertySymbol?.owner
return property != null && property.isRealLateinit()
}

private fun IrProperty.isRealLateinit() =
isLateinit && !isFakeOverride

override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid()

if (!Symbols.isLateinitIsInitializedPropertyGetter(expression.symbol)) return expression

private fun IrProperty.isRealLateinit() =
isLateinit && !isFakeOverride

override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid()

if (!Symbols.isLateinitIsInitializedPropertyGetter(expression.symbol)) return expression

return expression.extensionReceiver!!.replaceTailExpression {
val irPropertyRef = it as? IrPropertyReference
?: throw AssertionError("Property reference expected: ${it.render()}")
val property = irPropertyRef.getter?.owner?.resolveFakeOverride()?.correspondingPropertySymbol?.owner
?: throw AssertionError("isInitialized cannot be invoked on ${it.render()}")
require(property.isLateinit) {
"isInitialized invoked on non-lateinit property ${property.render()}"
}
val backingField = property.backingField
?: throw AssertionError("Lateinit property is supposed to have a backing field")
transformBackingField(backingField, property)
backendContext.createIrBuilder(it.symbol, expression.startOffset, expression.endOffset).run {
irNotEquals(
irGetField(it.dispatchReceiver, backingField),
irNull()
)
}
return expression.extensionReceiver!!.replaceTailExpression {
val irPropertyRef = it as? IrPropertyReference
?: throw AssertionError("Property reference expected: ${it.render()}")
val property = irPropertyRef.getter?.owner?.resolveFakeOverride()?.correspondingPropertySymbol?.owner
?: throw AssertionError("isInitialized cannot be invoked on ${it.render()}")
require(property.isLateinit) {
"isInitialized invoked on non-lateinit property ${property.render()}"
}
val backingField = property.backingField
?: throw AssertionError("Lateinit property is supposed to have a backing field")
transformLateinitBackingField(backingField, property)
backendContext.createIrBuilder(it.symbol, expression.startOffset, expression.endOffset).run {
irNotEquals(
irGetField(it.dispatchReceiver, backingField),
irNull()
)
}
}
}

private fun transformBackingField(backingField: IrField, property: IrProperty) {
assert(backingField.initializer == null) {
"lateinit property backing field should not have an initializer:\n${property.dump()}"
}
backingField.type = backingField.type.makeNullable()
protected open fun transformLateinitBackingField(backingField: IrField, property: IrProperty) {
assert(backingField.initializer == null) {
"lateinit property backing field should not have an initializer:\n${property.dump()}"
}
backingField.type = backingField.type.makeNullable()
}

private fun transformGetter(backingField: IrField, getter: IrFunction) {
val type = backingField.type
assert(!type.isPrimitiveType()) {
"'lateinit' property type should not be primitive:\n${backingField.dump()}"
}
val startOffset = getter.startOffset
val endOffset = getter.endOffset
getter.body = backendContext.irFactory.createBlockBody(startOffset, endOffset) {
val irBuilder = backendContext.createIrBuilder(getter.symbol, startOffset, endOffset)
irBuilder.run {
val resultVar = scope.createTmpVariable(
irGetField(getter.dispatchReceiverParameter?.let { irGet(it) }, backingField, type)
)
resultVar.parent = getter
statements.add(resultVar)
val throwIfNull = irIfThenElse(
context.irBuiltIns.nothingType,
irNotEquals(irGet(resultVar), irNull()),
irReturn(irGet(resultVar)),
throwUninitializedPropertyAccessException(backingField.name.asString())
)
statements.add(throwIfNull)
}
private fun transformGetter(backingField: IrField, getter: IrFunction) {
val type = backingField.type
assert(!type.isPrimitiveType()) {
"'lateinit' property type should not be primitive:\n${backingField.dump()}"
}
val startOffset = getter.startOffset
val endOffset = getter.endOffset
getter.body = backendContext.irFactory.createBlockBody(startOffset, endOffset) {
val irBuilder = backendContext.createIrBuilder(getter.symbol, startOffset, endOffset)
irBuilder.run {
val resultVar = scope.createTmpVariable(
irGetField(getter.dispatchReceiverParameter?.let { irGet(it) }, backingField, type)
)
resultVar.parent = getter
statements.add(resultVar)
val throwIfNull = irIfThenElse(
context.irBuiltIns.nothingType,
irNotEquals(irGet(resultVar), irNull()),
irReturn(irGet(resultVar)),
throwUninitializedPropertyAccessException(backingField.name.asString())
)
statements.add(throwIfNull)
}
}

private fun IrBuilderWithScope.throwUninitializedPropertyAccessException(name: String) =
backendContext.throwUninitializedPropertyAccessException(this, name)
}

private fun IrBuilderWithScope.throwUninitializedPropertyAccessException(name: String) =
backendContext.throwUninitializedPropertyAccessException(this, name)
}

private inline fun IrExpression.replaceTailExpression(crossinline transform: (IrExpression) -> IrExpression): IrExpression {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ private val jvmFilePhases = createFilePhases<JvmBackendContext>(
::PolymorphicSignatureLowering,
::VarargLowering,

::LateinitLowering,
::JvmLateinitLowering,
::JvmInventNamesForLocalClasses,

::JvmInlineCallableReferenceToLambdaPhase,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2024 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.lower.LateinitLowering
import org.jetbrains.kotlin.backend.common.phaser.PhaseDescription
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrProperty

@PhaseDescription(name = "JvmLateinitLowering")
class JvmLateinitLowering(context: CommonBackendContext) : LateinitLowering(context) {
override fun transformLateinitBackingField(backingField: IrField, property: IrProperty) {
super.transformLateinitBackingField(backingField, property)
backingField.visibility = property.setter?.visibility ?: property.visibility
}
}

0 comments on commit 455b127

Please sign in to comment.