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.backend.common.ir.Ir
import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.ir.IrElement 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.ir.declarations.IrFile
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.ir.expressions.IrExpression
interface LoggingContext { interface LoggingContext {
var inVerbosePhase: Boolean var inVerbosePhase: Boolean
@@ -24,4 +26,11 @@ interface CommonBackendContext : BackendContext, LoggingContext {
val configuration: CompilerConfiguration val configuration: CompilerConfiguration
val scriptMode: Boolean 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.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid 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 { private fun buildOrGetNullableField(originalField: IrField): IrField {
if (originalField.type.isMarkedNullable()) return originalField if (originalField.type.isMarkedNullable()) return originalField
return nullableFields.getOrPut(originalField) { return nullableFields.getOrPut(originalField) {
@@ -92,7 +92,7 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas
).also { ).also {
descriptor.bind(it) descriptor.bind(it)
it.parent = declaration.parent 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 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" } assert(!type.isPrimitiveType()) { "'lateinit' modifier is not allowed on primitive types" }
val startOffset = getter.startOffset val startOffset = getter.startOffset
val endOffset = getter.endOffset val endOffset = getter.endOffset
val irBuilder = context.createIrBuilder(getter.symbol, startOffset, endOffset) val irBuilder = backendContext.createIrBuilder(getter.symbol, startOffset, endOffset)
irBuilder.run { irBuilder.run {
val body = IrBlockBodyImpl(startOffset, endOffset) val body = IrBlockBodyImpl(startOffset, endOffset)
val resultVar = scope.createTemporaryVariable( val resultVar = scope.createTemporaryVariable(
@@ -117,7 +117,7 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas
context.irBuiltIns.nothingType, context.irBuiltIns.nothingType,
irNotEquals(irGet(resultVar), irNull()), irNotEquals(irGet(resultVar), irNull()),
irReturn(irGet(resultVar)), irReturn(irGet(resultVar)),
throwUninitializedPropertyAccessException(this, backingField.name.asString()) backendContext.throwUninitializedPropertyAccessException(this, backingField.name.asString())
) )
body.statements.add(throwIfNull) body.statements.add(throwIfNull)
getter.body = body getter.body = body
@@ -131,12 +131,12 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas
val irVar = nullableVariables[expression.symbol.owner] ?: return expression val irVar = nullableVariables[expression.symbol.owner] ?: return expression
val parent = irVar.parent as IrSymbolOwner 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 { return irBuilder.run {
irIfThenElse( irIfThenElse(
expression.type, irEqualsNull(irGet(irVar)), expression.type, irEqualsNull(irGet(irVar)),
throwUninitializedPropertyAccessException(this, irVar.name.asString()), backendContext.throwUninitializedPropertyAccessException(this, irVar.name.asString()),
irGet(irVar) irGet(irVar)
) )
} }
@@ -179,17 +179,10 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas
val nullableField = val nullableField =
buildOrGetNullableField(property.backingField ?: error("Lateinit property is supposed to have backing field")) 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()) 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.CommonBackendContext
import org.jetbrains.kotlin.backend.common.ir.Ir 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.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.jvm.codegen.IrTypeMapper import org.jetbrains.kotlin.backend.jvm.codegen.IrTypeMapper
import org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper 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.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.ir.IrElement 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.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
@@ -136,6 +140,12 @@ class JvmBackendContext(
print(message) print(message)
} }
override fun throwUninitializedPropertyAccessException(builder: IrBuilderWithScope, name: String): IrExpression =
builder.irBlock {
+super.throwUninitializedPropertyAccessException(builder, name)
+irThrow(irNull())
}
inner class JvmIr( inner class JvmIr(
irModuleFragment: IrModuleFragment, irModuleFragment: IrModuleFragment,
symbolTable: ReferenceSymbolTable symbolTable: ReferenceSymbolTable
@@ -14,11 +14,7 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.IrElement 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.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.util.PatchDeclarationParentsVisitor import org.jetbrains.kotlin.ir.util.PatchDeclarationParentsVisitor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -82,15 +78,7 @@ private val expectDeclarationsRemovingPhase = makeIrModulePhase(
) )
private val lateinitPhase = makeIrFilePhase( private val lateinitPhase = makeIrFilePhase(
{ backend: JvmBackendContext -> ::LateinitLowering,
object : LateinitLowering(backend) {
override fun throwUninitializedPropertyAccessException(builder: IrBuilderWithScope, name: String): IrExpression =
builder.irBlock {
+super.throwUninitializedPropertyAccessException(builder, name)
+irThrow(irNull())
}
}
},
name = "Lateinit", name = "Lateinit",
description = "Insert checks for lateinit field references" 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.ClassKind
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFieldAccessExpression import org.jetbrains.kotlin.ir.expressions.IrFieldAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.impl.* 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.util.hasAnnotation
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
@@ -63,9 +65,6 @@ class PropertiesToFieldsLowering(val context: CommonBackendContext) : IrElementT
private fun shouldSubstituteAccessorWithField(property: IrProperty, accessor: IrSimpleFunction?): Boolean { private fun shouldSubstituteAccessorWithField(property: IrProperty, accessor: IrSimpleFunction?): Boolean {
if (accessor == null) return false 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.parent as? IrClass)?.kind == ClassKind.ANNOTATION_CLASS) return false
if (property.backingField?.hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) == true) return true 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.startOffset,
expression.endOffset, expression.endOffset,
backingField.symbol, backingField.symbol,
expression.type, backingField.type,
receiver, receiver,
expression.origin, expression.origin,
expression.superQualifierSymbol 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 { private fun buildSubstitution(needBlock: Boolean, setOrGetExpr: IrFieldAccessExpression, receiver: IrExpression?): IrExpression {
@@ -1,6 +1,5 @@
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// WITH_REFLECT // WITH_REFLECT
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// Please make sure that this test is consistent with the diagnostic test "annotationsTargetingLateinitAccessor.kt" // 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"); 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") @TestMetadata("override.kt")
public void testOverride() throws Exception { public void testOverride() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/override.kt"); 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"); 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") @TestMetadata("override.kt")
public void testOverride() throws Exception { public void testOverride() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/override.kt"); 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"); 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") @TestMetadata("override.kt")
public void testOverride() throws Exception { public void testOverride() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/override.kt"); 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"); 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") @TestMetadata("override.kt")
public void testOverride() throws Exception { public void testOverride() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/override.kt"); 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"); 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") @TestMetadata("override.kt")
public void testOverride() throws Exception { public void testOverride() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/override.kt"); 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"); 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") @TestMetadata("override.kt")
public void testOverride() throws Exception { public void testOverride() throws Exception {
runTest("compiler/testData/codegen/box/properties/lateinit/override.kt"); runTest("compiler/testData/codegen/box/properties/lateinit/override.kt");