From 0d1c6e08c31bc7edce3003ad4281ac274b5c63e7 Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Mon, 23 Dec 2019 18:22:54 +0300 Subject: [PATCH] Refactor LateinitLowering into DeclarationTransformer's and a BodyLoweringPass --- .../kotlin/backend/common/Mappings.kt | 2 + .../backend/common/lower/LateinitLowering.kt | 150 +++++++++++------- .../ir/backend/js/JsIrBackendContext.kt | 3 +- .../kotlin/ir/backend/js/JsLoweringPhases.kt | 22 ++- .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 22 ++- .../kotlin/backend/wasm/WasmLoweringPhases.kt | 23 ++- 6 files changed, 148 insertions(+), 74 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt index 3c6293e77a1..58fd88aa754 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt @@ -12,6 +12,7 @@ import kotlin.reflect.KProperty interface Mapping { val defaultArgumentsDispatchFunction: Delegate val defaultArgumentsOriginalFunction: Delegate + val lateInitFieldToNullableField: Delegate val inlineClassMemberToStatic: Delegate abstract class Delegate { @@ -31,6 +32,7 @@ open class DefaultMapping : Mapping { override val defaultArgumentsDispatchFunction: Mapping.Delegate = newMapping() override val defaultArgumentsOriginalFunction: Mapping.Delegate = newMapping() + override val lateInitFieldToNullableField: Mapping.Delegate = newMapping() override val inlineClassMemberToStatic: Mapping.Delegate = newMapping() protected fun newMapping() = object : Mapping.Delegate() { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LateinitLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LateinitLowering.kt index 4d8ee688d15..3e035ceb511 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LateinitLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LateinitLowering.kt @@ -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? { + 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? { + 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() - // 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 \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index d109356a7d3..80b69e98882 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -50,7 +50,8 @@ class JsIrBackendContext( override val transformedFunction get() = error("Use Mapping.inlineClassMemberToStatic instead") - override val lateinitNullableFields = mutableMapOf() + override val lateinitNullableFields + get() = error("Use Mapping.lateInitFieldToNullableField instead") val memberMap = mutableMapOf() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index b971d72e17c..48e30aa068f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -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 diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index f28f2f1889f..ad1732704eb 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -79,9 +79,21 @@ private val expectDeclarationsRemovingPhase = makeIrModulePhase( // arrayConstructorPhase then functionInliningPhase then - lateinitLoweringPhase then + lateinitNullableFieldsPhase then + lateinitDeclarationLoweringPhase then + lateinitUsageLoweringPhase then tailrecLoweringPhase then enumClassConstructorLoweringPhase then