Refactor LateinitLowering into DeclarationTransformer's and a BodyLoweringPass
This commit is contained in:
@@ -12,6 +12,7 @@ import kotlin.reflect.KProperty
|
|||||||
interface Mapping {
|
interface Mapping {
|
||||||
val defaultArgumentsDispatchFunction: Delegate<IrFunction, IrFunction>
|
val defaultArgumentsDispatchFunction: Delegate<IrFunction, IrFunction>
|
||||||
val defaultArgumentsOriginalFunction: Delegate<IrFunction, IrFunction>
|
val defaultArgumentsOriginalFunction: Delegate<IrFunction, IrFunction>
|
||||||
|
val lateInitFieldToNullableField: Delegate<IrField, IrField>
|
||||||
val inlineClassMemberToStatic: Delegate<IrFunction, IrSimpleFunction>
|
val inlineClassMemberToStatic: Delegate<IrFunction, IrSimpleFunction>
|
||||||
|
|
||||||
abstract class Delegate<K : IrDeclaration, V> {
|
abstract class Delegate<K : IrDeclaration, V> {
|
||||||
@@ -31,6 +32,7 @@ open class DefaultMapping : Mapping {
|
|||||||
|
|
||||||
override val defaultArgumentsDispatchFunction: Mapping.Delegate<IrFunction, IrFunction> = newMapping()
|
override val defaultArgumentsDispatchFunction: Mapping.Delegate<IrFunction, IrFunction> = newMapping()
|
||||||
override val defaultArgumentsOriginalFunction: 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()
|
override val inlineClassMemberToStatic: Mapping.Delegate<IrFunction, IrSimpleFunction> = newMapping()
|
||||||
|
|
||||||
protected fun <K : IrDeclaration, V> newMapping() = object : Mapping.Delegate<K, V>() {
|
protected fun <K : IrDeclaration, V> newMapping() = object : Mapping.Delegate<K, V>() {
|
||||||
|
|||||||
+89
-61
@@ -16,8 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.common.lower
|
package org.jetbrains.kotlin.backend.common.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
import org.jetbrains.kotlin.backend.common.*
|
||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
|
||||||
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
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.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
|
||||||
class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringPass {
|
class NullableFieldsForLateinitCreationLowering(val backendContext: CommonBackendContext) : DeclarationTransformer {
|
||||||
|
|
||||||
private val nullableFields = backendContext.lateinitNullableFields
|
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||||
private fun buildOrGetNullableField(originalField: IrField): IrField {
|
if (declaration is IrField) {
|
||||||
if (originalField.type.isMarkedNullable()) return originalField
|
declaration.correspondingPropertySymbol?.owner?.let { property ->
|
||||||
return nullableFields.getOrPut(originalField) {
|
if (property.isRealLateinit) {
|
||||||
buildField {
|
val newField = backendContext.buildOrGetNullableField(declaration)
|
||||||
updateFrom(originalField)
|
if (declaration != newField && declaration.parent != property.parent) return listOf(newField)
|
||||||
type = originalField.type.makeNullable()
|
}
|
||||||
name = originalField.name
|
}
|
||||||
}.apply {
|
}
|
||||||
parent = originalField.parent
|
return null
|
||||||
correspondingPropertySymbol = originalField.correspondingPropertySymbol
|
}
|
||||||
annotations += originalField.annotations
|
}
|
||||||
|
|
||||||
|
// 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>()
|
val nullableVariables = mutableMapOf<IrVariable, IrVariable>()
|
||||||
|
|
||||||
// Transform declarations
|
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitVariable(declaration: IrVariable): IrStatement {
|
override fun visitVariable(declaration: IrVariable): IrStatement {
|
||||||
declaration.transformChildrenVoid(this)
|
declaration.transformChildrenVoid(this)
|
||||||
|
|
||||||
@@ -92,41 +125,17 @@ class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringP
|
|||||||
).also {
|
).also {
|
||||||
descriptor.bind(it)
|
descriptor.bind(it)
|
||||||
it.parent = declaration.parent
|
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
|
nullableVariables[declaration] = newVar
|
||||||
|
|
||||||
return 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
|
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
|
||||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||||
val irVar = nullableVariables[expression.symbol.owner] ?: return expression
|
val irVar = nullableVariables[expression.symbol.owner] ?: return expression
|
||||||
|
|
||||||
@@ -152,7 +161,7 @@ class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringP
|
|||||||
|
|
||||||
override fun visitGetField(expression: IrGetField): IrExpression {
|
override fun visitGetField(expression: IrGetField): IrExpression {
|
||||||
expression.transformChildrenVoid(this)
|
expression.transformChildrenVoid(this)
|
||||||
val newField = nullableFields[expression.symbol.owner] ?: return expression
|
val newField = backendContext.mapping.lateInitFieldToNullableField[expression.symbol.owner] ?: return expression
|
||||||
return with(expression) {
|
return with(expression) {
|
||||||
IrGetFieldImpl(startOffset, endOffset, newField.symbol, newField.type, receiver, origin, superQualifierSymbol)
|
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 {
|
override fun visitSetField(expression: IrSetField): IrExpression {
|
||||||
expression.transformChildrenVoid(this)
|
expression.transformChildrenVoid(this)
|
||||||
val newField = nullableFields[expression.symbol.owner] ?: return expression
|
val newField = backendContext.mapping.lateInitFieldToNullableField[expression.symbol.owner] ?: return expression
|
||||||
return with(expression) {
|
return with(expression) {
|
||||||
IrSetFieldImpl(startOffset, endOffset, newField.symbol, receiver, value, type, origin, superQualifierSymbol)
|
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) }
|
receiver.getter?.owner?.resolveFakeOverride()?.correspondingPropertySymbol!!.owner.also { assert(it.isLateinit) }
|
||||||
|
|
||||||
val nullableField =
|
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 {
|
return expression.run { backendContext.createIrBuilder(symbol, startOffset, endOffset) }.run {
|
||||||
irNotEquals(irGetField(receiver.dispatchReceiver, nullableField), irNull())
|
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
|
override val transformedFunction
|
||||||
get() = error("Use Mapping.inlineClassMemberToStatic instead")
|
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>()
|
val memberMap = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunction>()
|
||||||
|
|
||||||
|
|||||||
@@ -73,9 +73,21 @@ private val expectDeclarationsRemovingPhase = makeJsModulePhase(
|
|||||||
description = "Remove expect declaration from module fragment"
|
description = "Remove expect declaration from module fragment"
|
||||||
)
|
)
|
||||||
|
|
||||||
private val lateinitLoweringPhase = makeJsModulePhase(
|
private val lateinitNullableFieldsPhase = makeJsModulePhase(
|
||||||
::LateinitLowering,
|
::NullableFieldsForLateinitCreationLowering,
|
||||||
name = "LateinitLowering",
|
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"
|
description = "Insert checks for lateinit field references"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -422,7 +434,9 @@ val jsPhases = namedIrModulePhase(
|
|||||||
functionInliningPhase then
|
functionInliningPhase then
|
||||||
createScriptFunctionsPhase then
|
createScriptFunctionsPhase then
|
||||||
provisionalFunctionExpressionPhase then
|
provisionalFunctionExpressionPhase then
|
||||||
lateinitLoweringPhase then
|
lateinitNullableFieldsPhase then
|
||||||
|
lateinitDeclarationLoweringPhase then
|
||||||
|
lateinitUsageLoweringPhase then
|
||||||
tailrecLoweringPhase then
|
tailrecLoweringPhase then
|
||||||
enumClassConstructorLoweringPhase then
|
enumClassConstructorLoweringPhase then
|
||||||
sharedVariablesLoweringPhase then
|
sharedVariablesLoweringPhase then
|
||||||
|
|||||||
@@ -79,9 +79,21 @@ private val expectDeclarationsRemovingPhase = makeIrModulePhase<JvmBackendContex
|
|||||||
description = "Remove expect declaration from module fragment"
|
description = "Remove expect declaration from module fragment"
|
||||||
)
|
)
|
||||||
|
|
||||||
private val lateinitPhase = makeIrFilePhase(
|
private val lateinitNullableFieldsPhase = makeIrFilePhase(
|
||||||
::LateinitLowering,
|
::NullableFieldsForLateinitCreationLowering,
|
||||||
name = "Lateinit",
|
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"
|
description = "Insert checks for lateinit field references"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -241,7 +253,9 @@ private val jvmFilePhases =
|
|||||||
arrayConstructorPhase then
|
arrayConstructorPhase then
|
||||||
checkNotNullPhase then
|
checkNotNullPhase then
|
||||||
|
|
||||||
lateinitPhase then
|
lateinitNullableFieldsPhase then
|
||||||
|
lateinitDeclarationLoweringPhase then
|
||||||
|
lateinitUsageLoweringPhase then
|
||||||
|
|
||||||
moveOrCopyCompanionObjectFieldsPhase then
|
moveOrCopyCompanionObjectFieldsPhase then
|
||||||
inlineCallableReferenceToLambdaPhase then
|
inlineCallableReferenceToLambdaPhase then
|
||||||
|
|||||||
+19
-4
@@ -71,12 +71,25 @@ private val expectDeclarationsRemovingPhase = makeWasmModulePhase(
|
|||||||
description = "Remove expect declaration from module fragment"
|
description = "Remove expect declaration from module fragment"
|
||||||
)
|
)
|
||||||
|
|
||||||
private val lateinitLoweringPhase = makeWasmModulePhase(
|
private val lateinitNullableFieldsPhase = makeWasmModulePhase(
|
||||||
::LateinitLowering,
|
::NullableFieldsForLateinitCreationLowering,
|
||||||
name = "LateinitLowering",
|
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"
|
description = "Insert checks for lateinit field references"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// TODO make all lambda-related stuff work with IrFunctionExpression and drop this phase
|
// TODO make all lambda-related stuff work with IrFunctionExpression and drop this phase
|
||||||
private val provisionalFunctionExpressionPhase = makeWasmModulePhase(
|
private val provisionalFunctionExpressionPhase = makeWasmModulePhase(
|
||||||
{ ProvisionalFunctionExpressionLowering() },
|
{ ProvisionalFunctionExpressionLowering() },
|
||||||
@@ -329,7 +342,9 @@ val wasmPhases = namedIrModulePhase<WasmBackendContext>(
|
|||||||
// arrayConstructorPhase then
|
// arrayConstructorPhase then
|
||||||
|
|
||||||
functionInliningPhase then
|
functionInliningPhase then
|
||||||
lateinitLoweringPhase then
|
lateinitNullableFieldsPhase then
|
||||||
|
lateinitDeclarationLoweringPhase then
|
||||||
|
lateinitUsageLoweringPhase then
|
||||||
tailrecLoweringPhase then
|
tailrecLoweringPhase then
|
||||||
|
|
||||||
enumClassConstructorLoweringPhase then
|
enumClassConstructorLoweringPhase then
|
||||||
|
|||||||
Reference in New Issue
Block a user