Refactor LateinitLowering into DeclarationTransformer's and a BodyLoweringPass

This commit is contained in:
Anton Bannykh
2019-12-23 18:22:54 +03:00
parent 6e96e271ae
commit 0d1c6e08c3
6 changed files with 148 additions and 74 deletions
@@ -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>() {
@@ -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
@@ -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