Supported lateinit locals + tests
This commit is contained in:
+4
-3
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.backend.common.validateIrModule
|
||||
import org.jetbrains.kotlin.backend.konan.lower.*
|
||||
import org.jetbrains.kotlin.backend.konan.lower.DefaultArgumentStubGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.lower.DefaultParameterInjector
|
||||
import org.jetbrains.kotlin.backend.konan.lower.LateinitLowering
|
||||
import org.jetbrains.kotlin.backend.konan.lower.LocalDeclarationsLowering
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
@@ -95,6 +96,9 @@ internal class KonanLower(val context: Context) {
|
||||
phaser.phase(KonanPhase.LOWER_INITIALIZERS) {
|
||||
InitializersLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_LATEINIT) {
|
||||
LateinitLowering(context).lower(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_SHARED_VARIABLES) {
|
||||
SharedVariablesLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
@@ -117,9 +121,6 @@ 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)
|
||||
}
|
||||
|
||||
+2
-2
@@ -41,7 +41,8 @@ enum class KonanPhase(val description: String,
|
||||
/* ... ... */ LOWER_ENUMS("Enum classes lowering"),
|
||||
/* ... ... */ LOWER_DELEGATION("Delegation lowering"),
|
||||
/* ... ... */ LOWER_INITIALIZERS("Initializers lowering", LOWER_ENUMS),
|
||||
/* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering", LOWER_INITIALIZERS),
|
||||
/* ... ... */ LOWER_LATEINIT("Lateinit properties lowering"),
|
||||
/* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering", LOWER_INITIALIZERS, LOWER_LATEINIT),
|
||||
/* ... ... */ LOWER_CALLABLES("Callable references Lowering", LOWER_DELEGATION),
|
||||
/* ... ... */ LOWER_LOCAL_FUNCTIONS("Local Function Lowering", LOWER_SHARED_VARIABLES, LOWER_CALLABLES),
|
||||
/* ... ... */ LOWER_INTEROP_PART2("Interop lowering, part 2", LOWER_LOCAL_FUNCTIONS),
|
||||
@@ -50,7 +51,6 @@ enum class KonanPhase(val description: String,
|
||||
/* ... ... */ LOWER_FINALLY("Finally blocks lowering", LOWER_INITIALIZERS, LOWER_LOCAL_FUNCTIONS, LOWER_TAILREC),
|
||||
/* ... ... */ 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_COROUTINES("Coroutines lowering", LOWER_LOCAL_FUNCTIONS),
|
||||
/* ... ... */ LOWER_TYPE_OPERATORS("Type operators lowering", LOWER_COROUTINES),
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
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.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
|
||||
class LateinitLowering(
|
||||
val context: CommonBackendContext,
|
||||
private val generateParameterNameInAssertion: Boolean = false
|
||||
) : FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
val symbol = expression.symbol
|
||||
val descriptor = symbol.descriptor as? VariableDescriptor
|
||||
if (descriptor == null || !descriptor.isLateInit) return expression
|
||||
|
||||
assert(!KotlinBuiltIns.isPrimitiveType(descriptor.type), { "'lateinit' modifier is not allowed on primitive types" })
|
||||
val startOffset = expression.startOffset
|
||||
val endOffset = expression.endOffset
|
||||
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
|
||||
irBuilder.run {
|
||||
return irBlock(expression) {
|
||||
// TODO: do data flow analysis to check if value is proved to be not-null.
|
||||
+irIfThen(
|
||||
irEqualsNull(irGet(symbol)),
|
||||
throwUninitializedPropertyAccessException(symbol)
|
||||
)
|
||||
+irGet(symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitProperty(declaration: IrProperty): IrStatement {
|
||||
declaration.transformChildrenVoid(this)
|
||||
|
||||
if (declaration.descriptor.isLateInit && declaration.descriptor.kind.isReal)
|
||||
transformGetter(declaration.backingField!!.symbol, declaration.getter!!)
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
private fun transformGetter(backingFieldSymbol: IrFieldSymbol, getter: IrFunction) {
|
||||
val type = backingFieldSymbol.descriptor.type
|
||||
assert(!KotlinBuiltIns.isPrimitiveType(type), { "'lateinit' modifier is not allowed on primitive types" })
|
||||
val startOffset = getter.startOffset
|
||||
val endOffset = getter.endOffset
|
||||
val irBuilder = context.createIrBuilder(getter.symbol, startOffset, endOffset)
|
||||
irBuilder.run {
|
||||
getter.body = irBlockBody {
|
||||
val resultVar = irTemporary(
|
||||
irGetField(getter.dispatchReceiverParameter?.let { irGet(it.symbol) }, backingFieldSymbol)
|
||||
)
|
||||
+irIfThenElse(
|
||||
context.builtIns.nothingType,
|
||||
irNotEquals(irGet(resultVar.symbol), irNull()),
|
||||
irReturn(irGet(resultVar.symbol)),
|
||||
throwUninitializedPropertyAccessException(backingFieldSymbol)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.throwUninitializedPropertyAccessException(backingFieldSymbol: IrSymbol) =
|
||||
irCall(throwErrorFunction).apply {
|
||||
if (generateParameterNameInAssertion) {
|
||||
putValueArgument(
|
||||
0,
|
||||
IrConstImpl.string(
|
||||
startOffset,
|
||||
endOffset,
|
||||
context.builtIns.stringType,
|
||||
backingFieldSymbol.descriptor.name.asString()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val throwErrorFunction = context.ir.symbols.ThrowUninitializedPropertyAccessException
|
||||
|
||||
}
|
||||
@@ -1036,6 +1036,28 @@ task lateinit_inBaseClass(type: RunKonanTest) {
|
||||
source = "codegen/lateinit/inBaseClass.kt"
|
||||
}
|
||||
|
||||
task lateinit_localInitialized(type: RunKonanTest) {
|
||||
goldValue = "zzz\n"
|
||||
source = "codegen/lateinit/localInitialized.kt"
|
||||
}
|
||||
|
||||
task lateinit_localNotInitialized(type: RunKonanTest) {
|
||||
expectedFail = (project.testTarget == 'wasm32') // Uses exceptions.
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/lateinit/localNotInitialized.kt"
|
||||
}
|
||||
|
||||
task lateinit_localCapturedInitialized(type: RunKonanTest) {
|
||||
goldValue = "zzz\n"
|
||||
source = "codegen/lateinit/localCapturedInitialized.kt"
|
||||
}
|
||||
|
||||
task lateinit_localCapturedNotInitialized(type: RunKonanTest) {
|
||||
expectedFail = (project.testTarget == 'wasm32') // Uses exceptions.
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/lateinit/localCapturedNotInitialized.kt"
|
||||
}
|
||||
|
||||
task kclass0(type: RunKonanTest) {
|
||||
source = "codegen/kclass/kclass0.kt"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package codegen.lateinit.localCapturedInitialized
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
@Test fun runTest() {
|
||||
lateinit var s: String
|
||||
|
||||
fun foo() = s
|
||||
|
||||
s = "zzz"
|
||||
println(foo())
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package codegen.lateinit.localCapturedNotInitialized
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
@Test fun runTest() {
|
||||
lateinit var s: String
|
||||
|
||||
fun foo() = s
|
||||
|
||||
try {
|
||||
println(foo())
|
||||
}
|
||||
catch (e: RuntimeException) {
|
||||
println("OK")
|
||||
return
|
||||
}
|
||||
println("Fail")
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package codegen.lateinit.localInitialized
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
@Test fun runTest() {
|
||||
lateinit var s: String
|
||||
s = "zzz"
|
||||
println(s)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package codegen.lateinit.localNotInitialized
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
@Test fun runTest() {
|
||||
lateinit var s: String
|
||||
|
||||
try {
|
||||
println(s)
|
||||
}
|
||||
catch (e: RuntimeException) {
|
||||
println("OK")
|
||||
return
|
||||
}
|
||||
println("Fail")
|
||||
}
|
||||
Reference in New Issue
Block a user