From 55d3b23cfe146d761e3f57fd79413b694586087b Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 21 Mar 2017 16:38:30 +0300 Subject: [PATCH] Implemented 'lateinit' properties If property is lateinit, then on each access we need to test its value and throw UninitializedPropertyAccessException if it is null. --- .../kotlin/backend/konan/KonanLower.kt | 3 + .../kotlin/backend/konan/KonanPhases.kt | 32 ++++++----- .../backend/konan/lower/LateinitLowering.kt | 57 +++++++++++++++++++ .../kotlin/konan/internal/RuntimeUtils.kt | 4 ++ runtime/src/main/kotlin/kotlin/Exceptions.kt | 9 +++ 5 files changed, 90 insertions(+), 15 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/LateinitLowering.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt index 9b7188069b7..68c1aad2485 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt @@ -48,6 +48,9 @@ internal class KonanLower(val context: Context) { DefaultArgumentStubGenerator(context).runOnFilePostfix(irFile) DefaultParameterInjector(context).runOnFilePostfix(irFile) } + phaser.phase(KonanPhase.LOWER_LATEINIT) { + LateinitLowering(context).lower(irFile) + } phaser.phase(KonanPhase.LOWER_BUILTIN_OPERATORS) { BuiltinOperatorLowering(context).runOnFilePostfix(irFile) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt index d39be6c8281..0e8bba96ee6 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt @@ -3,7 +3,7 @@ package org.jetbrains.kotlin.backend.konan import org.jetbrains.kotlin.backend.konan.util.* enum class KonanPhase(val description: String, - val prerequisite: Set = setOf(), + vararg prerequisite: KonanPhase, var enabled: Boolean = true, var verbose: Boolean = false) { /* */ FRONTEND("Frontend builds AST"), @@ -14,27 +14,29 @@ enum class KonanPhase(val description: String, /* ... ... */ LOWER_INTEROP("Interop lowering"), /* ... ... */ LOWER_ENUMS("Enum classes lowering"), /* ... ... */ LOWER_DELEGATION("Delegation lowering"), - /* ... ... */ LOWER_INITIALIZERS("Initializers lowering", setOf(LOWER_ENUMS)), - /* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering", setOf(LOWER_INITIALIZERS)), - /* ... ... */ LOWER_CALLABLES("Callable references Lowering", setOf( - LOWER_INTEROP, LOWER_INITIALIZERS, LOWER_DELEGATION)), - /* ... ... */ LOWER_VARARG("Vararg lowering", setOf(LOWER_CALLABLES)), - /* ... ... */ LOWER_LOCAL_FUNCTIONS("Local Function Lowering", setOf(LOWER_SHARED_VARIABLES)), - /* ... ... */ LOWER_TAILREC("tailrec lowering", setOf(LOWER_LOCAL_FUNCTIONS)), - /* ... ... */ LOWER_DEFAULT_PARAMETER_EXTENT("Default Parameter Extent Lowering", setOf( - LOWER_TAILREC, LOWER_ENUMS)), - /* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering", setOf(LOWER_DEFAULT_PARAMETER_EXTENT)), - /* ... ... */ LOWER_BUILTIN_OPERATORS("BuiltIn Operators Lowering", setOf( - LOWER_DEFAULT_PARAMETER_EXTENT)), + /* ... ... */ LOWER_INITIALIZERS("Initializers lowering", LOWER_ENUMS), + /* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering", LOWER_INITIALIZERS), + /* ... ... */ LOWER_CALLABLES("Callable references Lowering", + LOWER_INTEROP, LOWER_INITIALIZERS, LOWER_DELEGATION), + /* ... ... */ LOWER_VARARG("Vararg lowering", LOWER_CALLABLES), + /* ... ... */ LOWER_LOCAL_FUNCTIONS("Local Function Lowering", LOWER_SHARED_VARIABLES), + /* ... ... */ LOWER_TAILREC("tailrec lowering", LOWER_LOCAL_FUNCTIONS), + /* ... ... */ LOWER_DEFAULT_PARAMETER_EXTENT("Default Parameter Extent Lowering", + LOWER_TAILREC, LOWER_ENUMS), + /* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering", LOWER_DEFAULT_PARAMETER_EXTENT), + /* ... ... */ LOWER_LATEINIT("Lateinit properties lowering"), + /* ... ... */ LOWER_BUILTIN_OPERATORS("BuiltIn Operators Lowering", LOWER_DEFAULT_PARAMETER_EXTENT, LOWER_LATEINIT), /* ... ... */ LOWER_TYPE_OPERATORS("Type operators lowering"), /* ... ... */ BRIDGES_BUILDING("Bridges building"), /* ... ... */ LOWER_STRING_CONCAT("String concatenation lowering"), - /* ... ... */ AUTOBOX("Autoboxing of primitive types", setOf(BRIDGES_BUILDING)), + /* ... ... */ AUTOBOX("Autoboxing of primitive types", BRIDGES_BUILDING), /* ... */ BITCODE("LLVM BitCode Generation"), /* ... ... */ RTTI("RTTI Generation"), /* ... ... */ CODEGEN("Code Generation"), /* ... ... */ METADATOR("Metadata Generation"), - /* */ LINKER("Link Stage") + /* */ LINKER("Link Stage"); + + val prerequisite = prerequisite.toSet() } object KonanPhases { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/LateinitLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/LateinitLowering.kt new file mode 100644 index 00000000000..c9935a65ed5 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/LateinitLowering.kt @@ -0,0 +1,57 @@ +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.expressions.IrBlock +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.types.KotlinType + +internal class LateinitLowering(val context: Context): FileLoweringPass { + override fun lower(irFile: IrFile) { + irFile.transformChildrenVoid(object: IrElementTransformerVoid() { + override fun visitProperty(declaration: IrProperty): IrStatement { + if (declaration.descriptor.isLateInit) + transformGetter(declaration.descriptor, declaration.getter!!) + return declaration + } + + private fun transformGetter(propertyDescriptor: PropertyDescriptor, getter: IrFunction) { + val startOffset = getter.startOffset + val endOffset = getter.endOffset + val irBuilder = context.createIrBuilder(getter.descriptor, startOffset, endOffset) + irBuilder.run { + val block = irBlock(propertyDescriptor.type) + val resultVar = scope.createTemporaryVariable(irGetField(irThis(), propertyDescriptor)) + block.statements.add(resultVar) + val throwIfNull = irIfThenElse(context.builtIns.nothingType, + irNotEquals(irGet(resultVar.descriptor), irNull()), + irReturn(irGet(resultVar.descriptor)), + irCall(throwErrorFunction)) + block.statements.add(throwIfNull) + getter.body = IrExpressionBodyImpl(startOffset, endOffset, block) + } + } + }) + } + + private val throwErrorFunction = context.builtIns.getKonanInternalFunctions("ThrowUninitializedPropertyAccessException").single() + + private fun IrBuilderWithScope.irBlock(type: KotlinType): IrBlock + = IrBlockImpl(startOffset, endOffset, type) + + private fun IrBuilderWithScope.irGetField(receiver: IrExpression?, property: PropertyDescriptor): IrExpression + = IrGetFieldImpl(startOffset, endOffset, property, receiver) +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt index 4066932e358..e70a0f26e23 100644 --- a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt +++ b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt @@ -24,6 +24,10 @@ fun ThrowNoWhenBranchMatchedException(): Nothing { throw NoWhenBranchMatchedException() } +fun ThrowUninitializedPropertyAccessException(): Nothing { + throw UninitializedPropertyAccessException() +} + @ExportForCppRuntime internal fun TheEmptyString() = "" diff --git a/runtime/src/main/kotlin/kotlin/Exceptions.kt b/runtime/src/main/kotlin/kotlin/Exceptions.kt index 789b118e55b..9de433a99d0 100644 --- a/runtime/src/main/kotlin/kotlin/Exceptions.kt +++ b/runtime/src/main/kotlin/kotlin/Exceptions.kt @@ -176,6 +176,15 @@ public open class NoWhenBranchMatchedException : RuntimeException { } } +public open class UninitializedPropertyAccessException : RuntimeException { + + constructor() : super() { + } + + constructor(s: String) : super(s) { + } +} + public open class OutOfMemoryError : Error { constructor() : super() {