JVM IR: Create local classes in PropertyReferenceLowering

In particular, we should not cache classes or instances for property
references, since they may be used inside of inline funtions. This also
allows us to mark the $$delegatedProperties array as package private.
This commit is contained in:
Steven Schäfer
2019-09-02 16:02:30 +02:00
committed by Alexander Udalov
parent 626f4c94f6
commit ee45933e33
3 changed files with 29 additions and 28 deletions
@@ -82,6 +82,16 @@ private class InterfaceLowering(val context: JvmBackendContext) : IrElementTrans
context.localDelegatedProperties[defaultImplsIrClass.attributeOwnerId as IrClass] = localDelegatedProperties context.localDelegatedProperties[defaultImplsIrClass.attributeOwnerId as IrClass] = localDelegatedProperties
context.localDelegatedProperties[irClass.attributeOwnerId as IrClass] = emptyList<IrLocalDelegatedPropertySymbol>() context.localDelegatedProperties[irClass.attributeOwnerId as IrClass] = emptyList<IrLocalDelegatedPropertySymbol>()
} }
// Move $$delegatedProperties array
val delegatedPropertyArray = irClass.declarations.filterIsInstance<IrField>()
.singleOrNull { it.origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE }
if (delegatedPropertyArray != null) {
irClass.declarations.remove(delegatedPropertyArray)
defaultImplsIrClass.declarations.add(0, delegatedPropertyArray)
delegatedPropertyArray.parent = defaultImplsIrClass
delegatedPropertyArray.initializer?.patchDeclarationParents(defaultImplsIrClass)
}
} }
private fun shouldRemoveFunction(function: IrFunction): Boolean = private fun shouldRemoveFunction(function: IrFunction): Boolean =
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.irArrayOf import org.jetbrains.kotlin.backend.jvm.ir.irArrayOf
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.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addFunction import org.jetbrains.kotlin.ir.builders.declarations.addFunction
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.JavaVisibilities
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
@@ -135,20 +137,17 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
propertyReferenceKind(!it.isFinal, if (it.isStatic || expression.dispatchReceiver != null) 0 else 1) propertyReferenceKind(!it.isFinal, if (it.isStatic || expression.dispatchReceiver != null) 0 else 1)
} ?: throw AssertionError("property has no getter and no field: ${expression.dump()}") } ?: throw AssertionError("property has no getter and no field: ${expression.dump()}")
private data class PropertyCacheKey(val symbol: IrSymbol, val reflected: Boolean)
private data class PropertyClassCacheKey(val symbol: IrSymbol, val boundReceiver: Boolean)
private data class PropertyInstance(val initializer: IrExpression, val index: Int) private data class PropertyInstance(val initializer: IrExpression, val index: Int)
override fun lower(irClass: IrClass) { override fun lower(irClass: IrClass) {
val kProperties = mutableMapOf<PropertyCacheKey, PropertyInstance>() val kProperties = mutableMapOf<IrSymbol, PropertyInstance>()
val kPropertyClasses = mutableMapOf<PropertyClassCacheKey, IrClass>()
val kPropertiesField = buildField { val kPropertiesField = buildField {
name = Name.identifier(JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME) name = Name.identifier(JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME)
type = kPropertiesFieldType type = kPropertiesFieldType
origin = JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE origin = JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE
isFinal = true isFinal = true
isStatic = true isStatic = true
// TODO: make the visibility package-local. Currently it's more permissive to allow access from inline functions. visibility = JavaVisibilities.PACKAGE_VISIBILITY
} }
var localPropertiesInClass = 0 var localPropertiesInClass = 0
@@ -165,20 +164,14 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
cachedKProperty(expression) cachedKProperty(expression)
private fun cachedKProperty(expression: IrCallableReference): IrExpression { private fun cachedKProperty(expression: IrCallableReference): IrExpression {
// Reflected implementation does not support partial application; also, cannot cache instances with arguments. if (expression.origin != IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE)
if (expression.dispatchReceiver != null || expression.extensionReceiver != null)
return createSpecializedKProperty(expression) return createSpecializedKProperty(expression)
// For delegated properties, the getter and setter contain a reference each as the second argument to getValue // For delegated properties, the getter and setter contain a reference each as the second argument to getValue
// and setValue. Since it's highly unlikely that anyone will call get/set on these, optimize for space. // and setValue. Since it's highly unlikely that anyone will call get/set on these, optimize for space.
val useReflectedImpl = expression.origin == IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE
return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run { return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run {
val (_, index) = kProperties.getOrPut(PropertyCacheKey(expression.symbol, useReflectedImpl)) { val (_, index) = kProperties.getOrPut(expression.symbol) {
val kProperty = if (useReflectedImpl) PropertyInstance(createReflectedKProperty(expression), kProperties.size)
createReflectedKProperty(expression)
else
createSpecializedKProperty(expression)
PropertyInstance(kProperty, kProperties.size)
} }
irCall(arrayItemGetter).apply { irCall(arrayItemGetter).apply {
dispatchReceiver = irGetField(null, kPropertiesField) dispatchReceiver = irGetField(null, kPropertiesField)
@@ -218,17 +211,16 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
// and then `C()::property` -> `C$property$0(C())`. // and then `C()::property` -> `C$property$0(C())`.
// //
private fun createSpecializedKProperty(expression: IrCallableReference): IrExpression { private fun createSpecializedKProperty(expression: IrCallableReference): IrExpression {
val bound = expression.dispatchReceiver != null || expression.extensionReceiver != null val referenceClass = createKPropertySubclass(expression)
val referenceClass = kPropertyClasses.getOrPut(PropertyClassCacheKey(expression.symbol, bound)) { return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset)
createKPropertySubclass(expression) .irBlock {
} +referenceClass
return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run { +irCall(referenceClass.constructors.single()).apply {
irCall(referenceClass.constructors.single()).apply { var index = 0
var index = 0 expression.dispatchReceiver?.let { putValueArgument(index++, it) }
expression.dispatchReceiver?.let { putValueArgument(index++, it) } expression.extensionReceiver?.let { putValueArgument(index++, it) }
expression.extensionReceiver?.let { putValueArgument(index++, it) } }
} }
}
} }
private fun createKPropertySubclass(expression: IrCallableReference): IrClass { private fun createKPropertySubclass(expression: IrCallableReference): IrClass {
@@ -237,6 +229,7 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
setSourceRange(expression) setSourceRange(expression)
name = Name.special("<property reference to ${(expression.symbol.owner as IrDeclarationWithName).fqNameWhenAvailable}>") name = Name.special("<property reference to ${(expression.symbol.owner as IrDeclarationWithName).fqNameWhenAvailable}>")
origin = JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE origin = JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE
visibility = Visibilities.LOCAL
}.apply { }.apply {
parent = irClass parent = irClass
superTypes += IrSimpleTypeImpl(superClass.symbol, false, listOf(), listOf()) superTypes += IrSimpleTypeImpl(superClass.symbol, false, listOf(), listOf())
@@ -317,7 +310,7 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
}) })
// Put the new field at the beginning so that static delegated properties with initializers work correctly. // Put the new field at the beginning so that static delegated properties with initializers work correctly.
// Since we do not cache property references with bound receivers, the new field does not reference anything else. // Since we do not cache property references, the new field does not reference anything else.
if (kProperties.isNotEmpty()) { if (kProperties.isNotEmpty()) {
irClass.declarations.add(0, kPropertiesField.apply { irClass.declarations.add(0, kPropertiesField.apply {
parent = irClass parent = irClass
@@ -328,8 +321,7 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
}) })
context.localDelegatedProperties[irClass.attributeOwnerId as IrClass] = context.localDelegatedProperties[irClass.attributeOwnerId as IrClass] =
kProperties.mapNotNull { it.key.symbol as? IrLocalDelegatedPropertySymbol } kProperties.keys.filterIsInstance<IrLocalDelegatedPropertySymbol>()
} }
irClass.declarations.addAll(0, kPropertyClasses.values)
} }
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class A { class A {
val foo = "" val foo = ""