Refactor LateinitLowering into DeclarationTransformer's and a BodyLoweringPass
This commit is contained in:
@@ -12,6 +12,7 @@ import kotlin.reflect.KProperty
|
||||
interface Mapping {
|
||||
val defaultArgumentsDispatchFunction: Delegate<IrFunction, IrFunction>
|
||||
val defaultArgumentsOriginalFunction: Delegate<IrFunction, IrFunction>
|
||||
val lateInitFieldToNullableField: Delegate<IrField, IrField>
|
||||
val inlineClassMemberToStatic: Delegate<IrFunction, IrSimpleFunction>
|
||||
|
||||
abstract class Delegate<K : IrDeclaration, V> {
|
||||
@@ -31,6 +32,7 @@ open class DefaultMapping : Mapping {
|
||||
|
||||
override val defaultArgumentsDispatchFunction: Mapping.Delegate<IrFunction, IrFunction> = newMapping()
|
||||
override val defaultArgumentsOriginalFunction: Mapping.Delegate<IrFunction, IrFunction> = newMapping()
|
||||
override val lateInitFieldToNullableField: Mapping.Delegate<IrField, IrField> = newMapping()
|
||||
override val inlineClassMemberToStatic: Mapping.Delegate<IrFunction, IrSimpleFunction> = newMapping()
|
||||
|
||||
protected fun <K : IrDeclaration, V> newMapping() = object : Mapping.Delegate<K, V>() {
|
||||
|
||||
+89
-61
@@ -16,8 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.common.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.*
|
||||
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
@@ -35,43 +34,77 @@ import org.jetbrains.kotlin.ir.util.resolveFakeOverride
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
|
||||
class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringPass {
|
||||
class NullableFieldsForLateinitCreationLowering(val backendContext: CommonBackendContext) : DeclarationTransformer {
|
||||
|
||||
private val nullableFields = backendContext.lateinitNullableFields
|
||||
private fun buildOrGetNullableField(originalField: IrField): IrField {
|
||||
if (originalField.type.isMarkedNullable()) return originalField
|
||||
return nullableFields.getOrPut(originalField) {
|
||||
buildField {
|
||||
updateFrom(originalField)
|
||||
type = originalField.type.makeNullable()
|
||||
name = originalField.name
|
||||
}.apply {
|
||||
parent = originalField.parent
|
||||
correspondingPropertySymbol = originalField.correspondingPropertySymbol
|
||||
annotations += originalField.annotations
|
||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||
if (declaration is IrField) {
|
||||
declaration.correspondingPropertySymbol?.owner?.let { property ->
|
||||
if (property.isRealLateinit) {
|
||||
val newField = backendContext.buildOrGetNullableField(declaration)
|
||||
if (declaration != newField && declaration.parent != property.parent) return listOf(newField)
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// Transform declarations
|
||||
class NullableFieldsDeclarationLowering(val backendContext: CommonBackendContext) : DeclarationTransformer {
|
||||
|
||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||
when (declaration) {
|
||||
is IrProperty -> {
|
||||
if (declaration.isRealLateinit) {
|
||||
declaration.backingField = backendContext.buildOrGetNullableField(declaration.backingField!!)
|
||||
}
|
||||
}
|
||||
|
||||
is IrSimpleFunction -> {
|
||||
declaration.correspondingPropertySymbol?.owner?.let { property ->
|
||||
if (declaration == property.getter && property.isRealLateinit) {
|
||||
// f = buildOrGetNullableField is idempotent, i.e. f(f(x)) == f(x)
|
||||
transformGetter(backendContext.buildOrGetNullableField(property.backingField!!), declaration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun transformGetter(backingField: IrField, getter: IrFunction) {
|
||||
val type = backingField.type
|
||||
assert(!type.isPrimitiveType()) { "'lateinit' modifier is not allowed on primitive types" }
|
||||
val startOffset = getter.startOffset
|
||||
val endOffset = getter.endOffset
|
||||
getter.body = IrBlockBodyImpl(startOffset, endOffset) {
|
||||
val irBuilder = backendContext.createIrBuilder(getter.symbol, startOffset, endOffset)
|
||||
irBuilder.run {
|
||||
val resultVar = scope.createTemporaryVariable(
|
||||
irGetField(getter.dispatchReceiverParameter?.let { irGet(it) }, backingField)
|
||||
)
|
||||
resultVar.parent = getter
|
||||
statements.add(resultVar)
|
||||
val throwIfNull = irIfThenElse(
|
||||
context.irBuiltIns.nothingType,
|
||||
irNotEquals(irGet(resultVar), irNull()),
|
||||
irReturn(irGet(resultVar)),
|
||||
backendContext.throwUninitializedPropertyAccessException(this, backingField.name.asString())
|
||||
)
|
||||
statements.add(throwIfNull)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
// Transform usages
|
||||
class LateinitUsageLowering(val backendContext: CommonBackendContext) : BodyLoweringPass {
|
||||
|
||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||
val nullableVariables = mutableMapOf<IrVariable, IrVariable>()
|
||||
|
||||
// Transform declarations
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitProperty(declaration: IrProperty): IrStatement {
|
||||
declaration.transformChildrenVoid(this)
|
||||
if (declaration.isLateinit && declaration.origin != IrDeclarationOrigin.FAKE_OVERRIDE) {
|
||||
val oldField = declaration.backingField!!
|
||||
val newField = buildOrGetNullableField(oldField)
|
||||
|
||||
declaration.backingField = newField
|
||||
|
||||
transformGetter(newField, declaration.getter!!)
|
||||
}
|
||||
return declaration
|
||||
}
|
||||
|
||||
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitVariable(declaration: IrVariable): IrStatement {
|
||||
declaration.transformChildrenVoid(this)
|
||||
|
||||
@@ -92,41 +125,17 @@ class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringP
|
||||
).also {
|
||||
descriptor.bind(it)
|
||||
it.parent = declaration.parent
|
||||
it.initializer = IrConstImpl.constNull(declaration.startOffset, declaration.endOffset, backendContext.irBuiltIns.nothingNType)
|
||||
it.initializer =
|
||||
IrConstImpl.constNull(declaration.startOffset, declaration.endOffset, backendContext.irBuiltIns.nothingNType)
|
||||
}
|
||||
|
||||
nullableVariables[declaration] = newVar
|
||||
|
||||
return newVar
|
||||
}
|
||||
|
||||
private fun transformGetter(backingField: IrField, getter: IrFunction) {
|
||||
val type = backingField.type
|
||||
assert(!type.isPrimitiveType()) { "'lateinit' modifier is not allowed on primitive types" }
|
||||
val startOffset = getter.startOffset
|
||||
val endOffset = getter.endOffset
|
||||
val irBuilder = backendContext.createIrBuilder(getter.symbol, startOffset, endOffset)
|
||||
irBuilder.run {
|
||||
val body = IrBlockBodyImpl(startOffset, endOffset)
|
||||
val resultVar = scope.createTemporaryVariable(
|
||||
irGetField(getter.dispatchReceiverParameter?.let { irGet(it) }, backingField)
|
||||
)
|
||||
resultVar.parent = getter
|
||||
body.statements.add(resultVar)
|
||||
val throwIfNull = irIfThenElse(
|
||||
context.irBuiltIns.nothingType,
|
||||
irNotEquals(irGet(resultVar), irNull()),
|
||||
irReturn(irGet(resultVar)),
|
||||
backendContext.throwUninitializedPropertyAccessException(this, backingField.name.asString())
|
||||
)
|
||||
body.statements.add(throwIfNull)
|
||||
getter.body = body
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Transform usages
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
val irVar = nullableVariables[expression.symbol.owner] ?: return expression
|
||||
|
||||
@@ -152,7 +161,7 @@ class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringP
|
||||
|
||||
override fun visitGetField(expression: IrGetField): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
val newField = nullableFields[expression.symbol.owner] ?: return expression
|
||||
val newField = backendContext.mapping.lateInitFieldToNullableField[expression.symbol.owner] ?: return expression
|
||||
return with(expression) {
|
||||
IrGetFieldImpl(startOffset, endOffset, newField.symbol, newField.type, receiver, origin, superQualifierSymbol)
|
||||
}
|
||||
@@ -160,7 +169,7 @@ class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringP
|
||||
|
||||
override fun visitSetField(expression: IrSetField): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
val newField = nullableFields[expression.symbol.owner] ?: return expression
|
||||
val newField = backendContext.mapping.lateInitFieldToNullableField[expression.symbol.owner] ?: return expression
|
||||
return with(expression) {
|
||||
IrSetFieldImpl(startOffset, endOffset, newField.symbol, receiver, value, type, origin, superQualifierSymbol)
|
||||
}
|
||||
@@ -177,7 +186,9 @@ class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringP
|
||||
receiver.getter?.owner?.resolveFakeOverride()?.correspondingPropertySymbol!!.owner.also { assert(it.isLateinit) }
|
||||
|
||||
val nullableField =
|
||||
buildOrGetNullableField(property.backingField ?: error("Lateinit property is supposed to have backing field"))
|
||||
backendContext.buildOrGetNullableField(
|
||||
property.backingField ?: error("Lateinit property is supposed to have backing field")
|
||||
)
|
||||
|
||||
return expression.run { backendContext.createIrBuilder(symbol, startOffset, endOffset) }.run {
|
||||
irNotEquals(irGetField(receiver.dispatchReceiver, nullableField), irNull())
|
||||
@@ -186,3 +197,20 @@ class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringP
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun CommonBackendContext.buildOrGetNullableField(originalField: IrField): IrField {
|
||||
if (originalField.type.isMarkedNullable()) return originalField
|
||||
return mapping.lateInitFieldToNullableField.getOrPut(originalField) {
|
||||
buildField {
|
||||
updateFrom(originalField)
|
||||
type = originalField.type.makeNullable()
|
||||
name = originalField.name
|
||||
}.apply {
|
||||
parent = originalField.parent
|
||||
correspondingPropertySymbol = originalField.correspondingPropertySymbol
|
||||
annotations += originalField.annotations
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val IrProperty.isRealLateinit get() = isLateinit && origin != IrDeclarationOrigin.FAKE_OVERRIDE
|
||||
@@ -50,7 +50,8 @@ class JsIrBackendContext(
|
||||
override val transformedFunction
|
||||
get() = error("Use Mapping.inlineClassMemberToStatic instead")
|
||||
|
||||
override val lateinitNullableFields = mutableMapOf<IrField, IrField>()
|
||||
override val lateinitNullableFields
|
||||
get() = error("Use Mapping.lateInitFieldToNullableField instead")
|
||||
|
||||
val memberMap = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunction>()
|
||||
|
||||
|
||||
@@ -73,9 +73,21 @@ private val expectDeclarationsRemovingPhase = makeJsModulePhase(
|
||||
description = "Remove expect declaration from module fragment"
|
||||
)
|
||||
|
||||
private val lateinitLoweringPhase = makeJsModulePhase(
|
||||
::LateinitLowering,
|
||||
name = "LateinitLowering",
|
||||
private val lateinitNullableFieldsPhase = makeJsModulePhase(
|
||||
::NullableFieldsForLateinitCreationLowering,
|
||||
name = "LateinitNullableFields",
|
||||
description = "Create nullable fields for lateinit properties"
|
||||
)
|
||||
|
||||
private val lateinitDeclarationLoweringPhase = makeJsModulePhase(
|
||||
::NullableFieldsDeclarationLowering,
|
||||
name = "LateinitDeclarations",
|
||||
description = "Reference nullable fields from properties and getters + insert checks"
|
||||
)
|
||||
|
||||
private val lateinitUsageLoweringPhase = makeJsModulePhase(
|
||||
::LateinitUsageLowering,
|
||||
name = "LateinitUsage",
|
||||
description = "Insert checks for lateinit field references"
|
||||
)
|
||||
|
||||
@@ -422,7 +434,9 @@ val jsPhases = namedIrModulePhase(
|
||||
functionInliningPhase then
|
||||
createScriptFunctionsPhase then
|
||||
provisionalFunctionExpressionPhase then
|
||||
lateinitLoweringPhase then
|
||||
lateinitNullableFieldsPhase then
|
||||
lateinitDeclarationLoweringPhase then
|
||||
lateinitUsageLoweringPhase then
|
||||
tailrecLoweringPhase then
|
||||
enumClassConstructorLoweringPhase then
|
||||
sharedVariablesLoweringPhase then
|
||||
|
||||
@@ -79,9 +79,21 @@ private val expectDeclarationsRemovingPhase = makeIrModulePhase<JvmBackendContex
|
||||
description = "Remove expect declaration from module fragment"
|
||||
)
|
||||
|
||||
private val lateinitPhase = makeIrFilePhase(
|
||||
::LateinitLowering,
|
||||
name = "Lateinit",
|
||||
private val lateinitNullableFieldsPhase = makeIrFilePhase(
|
||||
::NullableFieldsForLateinitCreationLowering,
|
||||
name = "LateinitNullableFields",
|
||||
description = "Create nullable fields for lateinit properties"
|
||||
)
|
||||
|
||||
private val lateinitDeclarationLoweringPhase = makeIrFilePhase(
|
||||
::NullableFieldsDeclarationLowering,
|
||||
name = "LateinitDeclarations",
|
||||
description = "Reference nullable fields from properties and getters + insert checks"
|
||||
)
|
||||
|
||||
private val lateinitUsageLoweringPhase = makeIrFilePhase(
|
||||
::LateinitUsageLowering,
|
||||
name = "LateinitUsage",
|
||||
description = "Insert checks for lateinit field references"
|
||||
)
|
||||
|
||||
@@ -241,7 +253,9 @@ private val jvmFilePhases =
|
||||
arrayConstructorPhase then
|
||||
checkNotNullPhase then
|
||||
|
||||
lateinitPhase then
|
||||
lateinitNullableFieldsPhase then
|
||||
lateinitDeclarationLoweringPhase then
|
||||
lateinitUsageLoweringPhase then
|
||||
|
||||
moveOrCopyCompanionObjectFieldsPhase then
|
||||
inlineCallableReferenceToLambdaPhase then
|
||||
|
||||
+19
-4
@@ -71,12 +71,25 @@ private val expectDeclarationsRemovingPhase = makeWasmModulePhase(
|
||||
description = "Remove expect declaration from module fragment"
|
||||
)
|
||||
|
||||
private val lateinitLoweringPhase = makeWasmModulePhase(
|
||||
::LateinitLowering,
|
||||
name = "LateinitLowering",
|
||||
private val lateinitNullableFieldsPhase = makeWasmModulePhase(
|
||||
::NullableFieldsForLateinitCreationLowering,
|
||||
name = "LateinitNullableFields",
|
||||
description = "Create nullable fields for lateinit properties"
|
||||
)
|
||||
|
||||
private val lateinitDeclarationLoweringPhase = makeWasmModulePhase(
|
||||
::NullableFieldsDeclarationLowering,
|
||||
name = "LateinitDeclarations",
|
||||
description = "Reference nullable fields from properties and getters + insert checks"
|
||||
)
|
||||
|
||||
private val lateinitUsageLoweringPhase = makeWasmModulePhase(
|
||||
::LateinitUsageLowering,
|
||||
name = "LateinitUsage",
|
||||
description = "Insert checks for lateinit field references"
|
||||
)
|
||||
|
||||
|
||||
// TODO make all lambda-related stuff work with IrFunctionExpression and drop this phase
|
||||
private val provisionalFunctionExpressionPhase = makeWasmModulePhase(
|
||||
{ ProvisionalFunctionExpressionLowering() },
|
||||
@@ -329,7 +342,9 @@ val wasmPhases = namedIrModulePhase<WasmBackendContext>(
|
||||
// arrayConstructorPhase then
|
||||
|
||||
functionInliningPhase then
|
||||
lateinitLoweringPhase then
|
||||
lateinitNullableFieldsPhase then
|
||||
lateinitDeclarationLoweringPhase then
|
||||
lateinitUsageLoweringPhase then
|
||||
tailrecLoweringPhase then
|
||||
|
||||
enumClassConstructorLoweringPhase then
|
||||
|
||||
Reference in New Issue
Block a user