JVM_IR: do not generate accessors to lateinit private properties

This commit is contained in:
Georgy Bronnikov
2019-10-11 19:48:23 +03:00
parent be0516348b
commit 119a3f1306
13 changed files with 103 additions and 36 deletions
@@ -7,10 +7,12 @@ package org.jetbrains.kotlin.backend.common
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irString
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.ir.expressions.IrExpression
interface LoggingContext {
var inVerbosePhase: Boolean
@@ -24,4 +26,11 @@ interface CommonBackendContext : BackendContext, LoggingContext {
val configuration: CompilerConfiguration
val scriptMode: Boolean
fun throwUninitializedPropertyAccessException(builder: IrBuilderWithScope, name: String): IrExpression {
val throwErrorFunction = ir.symbols.ThrowUninitializedPropertyAccessException.owner
return builder.irCall(throwErrorFunction).apply {
putValueArgument(0, builder.irString(name))
}
}
}
@@ -35,9 +35,9 @@ import org.jetbrains.kotlin.ir.util.resolveFakeOverride
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPass {
class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringPass {
private val nullableFields = context.lateinitNullableFields
private val nullableFields = backendContext.lateinitNullableFields
private fun buildOrGetNullableField(originalField: IrField): IrField {
if (originalField.type.isMarkedNullable()) return originalField
return nullableFields.getOrPut(originalField) {
@@ -92,7 +92,7 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas
).also {
descriptor.bind(it)
it.parent = declaration.parent
it.initializer = IrConstImpl.constNull(declaration.startOffset, declaration.endOffset, context.irBuiltIns.nothingNType)
it.initializer = IrConstImpl.constNull(declaration.startOffset, declaration.endOffset, backendContext.irBuiltIns.nothingNType)
}
nullableVariables[declaration] = newVar
@@ -105,7 +105,7 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas
assert(!type.isPrimitiveType()) { "'lateinit' modifier is not allowed on primitive types" }
val startOffset = getter.startOffset
val endOffset = getter.endOffset
val irBuilder = context.createIrBuilder(getter.symbol, startOffset, endOffset)
val irBuilder = backendContext.createIrBuilder(getter.symbol, startOffset, endOffset)
irBuilder.run {
val body = IrBlockBodyImpl(startOffset, endOffset)
val resultVar = scope.createTemporaryVariable(
@@ -117,7 +117,7 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas
context.irBuiltIns.nothingType,
irNotEquals(irGet(resultVar), irNull()),
irReturn(irGet(resultVar)),
throwUninitializedPropertyAccessException(this, backingField.name.asString())
backendContext.throwUninitializedPropertyAccessException(this, backingField.name.asString())
)
body.statements.add(throwIfNull)
getter.body = body
@@ -131,12 +131,12 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas
val irVar = nullableVariables[expression.symbol.owner] ?: return expression
val parent = irVar.parent as IrSymbolOwner
val irBuilder = context.createIrBuilder(parent.symbol, expression.startOffset, expression.endOffset)
val irBuilder = backendContext.createIrBuilder(parent.symbol, expression.startOffset, expression.endOffset)
return irBuilder.run {
irIfThenElse(
expression.type, irEqualsNull(irGet(irVar)),
throwUninitializedPropertyAccessException(this, irVar.name.asString()),
backendContext.throwUninitializedPropertyAccessException(this, irVar.name.asString()),
irGet(irVar)
)
}
@@ -179,17 +179,10 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas
val nullableField =
buildOrGetNullableField(property.backingField ?: error("Lateinit property is supposed to have backing field"))
return expression.run { context.createIrBuilder(symbol, startOffset, endOffset) }.run {
return expression.run { backendContext.createIrBuilder(symbol, startOffset, endOffset) }.run {
irNotEquals(irGetField(receiver.dispatchReceiver, nullableField), irNull())
}
}
})
}
open fun throwUninitializedPropertyAccessException(builder: IrBuilderWithScope, name: String): IrExpression =
builder.irCall(throwErrorFunction).apply {
putValueArgument(0, builder.irString(name))
}
private val throwErrorFunction by lazy { context.ir.symbols.ThrowUninitializedPropertyAccessException.owner }
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.jvm.codegen.IrTypeMapper
import org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper
@@ -24,6 +25,9 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.irBlock
import org.jetbrains.kotlin.ir.builders.irNull
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrExpression
@@ -136,6 +140,12 @@ class JvmBackendContext(
print(message)
}
override fun throwUninitializedPropertyAccessException(builder: IrBuilderWithScope, name: String): IrExpression =
builder.irBlock {
+super.throwUninitializedPropertyAccessException(builder, name)
+irThrow(irNull())
}
inner class JvmIr(
irModuleFragment: IrModuleFragment,
symbolTable: ReferenceSymbolTable
@@ -14,11 +14,7 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.irBlock
import org.jetbrains.kotlin.ir.builders.irNull
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.util.PatchDeclarationParentsVisitor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -82,15 +78,7 @@ private val expectDeclarationsRemovingPhase = makeIrModulePhase(
)
private val lateinitPhase = makeIrFilePhase(
{ backend: JvmBackendContext ->
object : LateinitLowering(backend) {
override fun throwUninitializedPropertyAccessException(builder: IrBuilderWithScope, name: String): IrExpression =
builder.irBlock {
+super.throwUninitializedPropertyAccessException(builder, name)
+irThrow(irNull())
}
}
},
::LateinitLowering,
name = "Lateinit",
description = "Insert checks for lateinit field references"
)
@@ -13,12 +13,14 @@ import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFieldAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.types.makeNotNull
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
@@ -63,9 +65,6 @@ class PropertiesToFieldsLowering(val context: CommonBackendContext) : IrElementT
private fun shouldSubstituteAccessorWithField(property: IrProperty, accessor: IrSimpleFunction?): Boolean {
if (accessor == null) return false
// In contrast to the old backend, we do generate getters for lateinit properties, which fixes KT-28331
if (property.isLateinit) return false
if ((property.parent as? IrClass)?.kind == ClassKind.ANNOTATION_CLASS) return false
if (property.backingField?.hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) == true) return true
@@ -96,12 +95,34 @@ class PropertiesToFieldsLowering(val context: CommonBackendContext) : IrElementT
expression.startOffset,
expression.endOffset,
backingField.symbol,
expression.type,
backingField.type,
receiver,
expression.origin,
expression.superQualifierSymbol
)
return buildSubstitution(backingField.isStatic, getExpr, receiver)
val substitution = buildSubstitution(backingField.isStatic, getExpr, receiver)
return if (irProperty.isLateinit)
insertLateinitCheck(substitution, backingField)
else
substitution
}
private fun insertLateinitCheck(expression: IrExpression, field: IrField): IrExpression {
val backendContext = context
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val irBuilder = context.createIrBuilder(field.symbol, startOffset, endOffset)
irBuilder.run {
return irBlock(expression) {
val tmpVar = irTemporaryVar(expression)
+irIfThenElse(
expression.type.makeNotNull(),
irEqualsNull(irGet(tmpVar)),
backendContext.throwUninitializedPropertyAccessException(this, field.name.asString()),
irGet(expression.type.makeNotNull(), tmpVar.symbol)
)
}
}
}
private fun buildSubstitution(needBlock: Boolean, setOrGetExpr: IrFieldAccessExpression, receiver: IrExpression?): IrExpression {
@@ -1,6 +1,5 @@
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_REFLECT
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// Please make sure that this test is consistent with the diagnostic test "annotationsTargetingLateinitAccessor.kt"
@@ -0,0 +1,17 @@
// IGNORE_BACKEND_FIR: JVM_IR
class SS {
private lateinit var s: String
fun setS(s: String) {
this.s = s
}
fun getS() = s
}
fun box(): String {
val ss = SS()
ss.setS("OK")
return ss.getS()
}
@@ -19169,6 +19169,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt");
}
@TestMetadata("nameClash.kt")
public void testNameClash() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/nameClash.kt");
}
@TestMetadata("override.kt")
public void testOverride() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/override.kt");
@@ -19169,6 +19169,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt");
}
@TestMetadata("nameClash.kt")
public void testNameClash() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/nameClash.kt");
}
@TestMetadata("override.kt")
public void testOverride() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/override.kt");
@@ -17704,6 +17704,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt");
}
@TestMetadata("nameClash.kt")
public void testNameClash() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/nameClash.kt");
}
@TestMetadata("override.kt")
public void testOverride() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/override.kt");
@@ -17704,6 +17704,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt");
}
@TestMetadata("nameClash.kt")
public void testNameClash() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/nameClash.kt");
}
@TestMetadata("override.kt")
public void testOverride() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/override.kt");
@@ -14874,6 +14874,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt");
}
@TestMetadata("nameClash.kt")
public void testNameClash() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/nameClash.kt");
}
@TestMetadata("override.kt")
public void testOverride() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/override.kt");
@@ -16049,6 +16049,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt");
}
@TestMetadata("nameClash.kt")
public void testNameClash() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/nameClash.kt");
}
@TestMetadata("override.kt")
public void testOverride() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/override.kt");