[JVM_IR] Use direct field access to backing fields on current class.

The current backend uses direct field access to the backing field
instead of calling the companion object accessor, which calls
an accessibility bridge, which then gets the field for code such as:

```
class A {
  companion object {
    val s: String = "OK"
  }

  // f uses direct access to the A.s backing field.
  fun f() = s
}
```

This change does the same for the IR backend.
This commit is contained in:
Mads Ager
2020-11-25 14:36:40 +01:00
committed by max-kammerer
parent 1d14926444
commit c922484758
19 changed files with 524 additions and 6 deletions
@@ -43,7 +43,7 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
object FOR_INLINE_STATE_MACHINE_TEMPLATE : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE")
object FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE_CROSSINLINE")
object CONTINUATION_CLASS_RESULT_FIELD: IrDeclarationOriginImpl("CONTINUATION_CLASS_RESULT_FIELD", isSynthetic = true)
object COMPANION_PROPERTY_BACKING_FIELD : IrDeclarationOriginImpl("COMPANION_MOVED_PROPERTY_BACKING_FIELD")
object COMPANION_PROPERTY_BACKING_FIELD : IrDeclarationOriginImpl("COMPANION_PROPERTY_BACKING_FIELD")
object FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE : IrDeclarationOriginImpl("FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE")
object ABSTRACT_BRIDGE_STUB : IrDeclarationOriginImpl("ABSTRACT_BRIDGE_STUB")
}
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.requiresMangling
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
@@ -28,10 +29,7 @@ import org.jetbrains.kotlin.ir.expressions.IrFieldAccessExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.coerceToUnit
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
@@ -54,7 +52,9 @@ class JvmPropertiesLowering(private val backendContext: JvmBackendContext) : IrE
val property = simpleFunction.correspondingPropertySymbol?.owner ?: return super.visitCall(expression)
expression.transformChildrenVoid()
if (shouldSubstituteAccessorWithField(property, simpleFunction)) {
if (shouldSubstituteAccessorWithField(property, simpleFunction) ||
isDefaultAccessorForCompanionPropertyBackingFieldOnCurrentClass(property, simpleFunction)
) {
backendContext.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).apply {
return when (simpleFunction) {
property.getter -> substituteGetter(property, expression)
@@ -67,6 +67,24 @@ class JvmPropertiesLowering(private val backendContext: JvmBackendContext) : IrE
return expression
}
private fun isDefaultAccessorForCompanionPropertyBackingFieldOnCurrentClass(
property: IrProperty,
function: IrSimpleFunction
): Boolean {
if (function.origin != IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) return false
if (property.isLateinit) return false
// If this code could end up inlined in another class (either an inline function or an
// inlined lambda in an inline function) use the companion object accessor. Otherwise,
// we could break binary compatibility if we only recompile the class with the companion
// object and change to non-default field accessors. The inlined code would still attempt
// to get the backing field which would no longer exist.
val inInlineFunctionScope = allScopes.any { scope -> (scope.irElement as? IrFunction)?.isInline ?: false }
if (inInlineFunctionScope) return false
val backingField = property.resolveFakeOverride()!!.backingField
return backingField?.parent == currentClass?.irElement &&
backingField?.origin == JvmLoweredDeclarationOrigin.COMPANION_PROPERTY_BACKING_FIELD
}
private fun IrBuilderWithScope.substituteSetter(irProperty: IrProperty, expression: IrCall): IrExpression =
patchReceiver(
irSetField(