[JVM_IR] Fix accessibility bridges for static protected fields.

Super-qualifiers have to be taken into account. Otherwise, too
few accessibility bridges will be generated which can lead to
binary compatibility issues.
This commit is contained in:
Mads Ager
2021-01-21 16:19:59 +01:00
committed by Alexander Udalov
parent 62897a194b
commit 6a959fefd0
6 changed files with 155 additions and 19 deletions
@@ -68,9 +68,11 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
}
}
private data class FieldKey(val fieldSymbol: IrFieldSymbol, val parent: IrDeclarationParent, val superQualifierSymbol: IrClassSymbol?)
private val functionMap = mutableMapOf<Pair<IrFunctionSymbol, IrDeclarationParent>, IrFunctionSymbol>()
private val getterMap = mutableMapOf<Pair<IrFieldSymbol, IrDeclarationParent>, IrSimpleFunctionSymbol>()
private val setterMap = mutableMapOf<Pair<IrFieldSymbol, IrDeclarationParent>, IrSimpleFunctionSymbol>()
private val getterMap = mutableMapOf<FieldKey, IrSimpleFunctionSymbol>()
private val setterMap = mutableMapOf<FieldKey, IrSimpleFunctionSymbol>()
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
if (expression.usesDefaultArguments()) {
@@ -158,7 +160,9 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
symbol.owner.accessorParent(dispatchReceiverType?.classOrNull?.owner ?: symbol.owner.parent) as IrClass
modifyGetterExpression(
expression,
getterMap.getOrPut(Pair(symbol, parent)) { makeGetterAccessorSymbol(symbol, parent) }
getterMap.getOrPut(FieldKey(symbol, parent, expression.superQualifierSymbol)) {
makeGetterAccessorSymbol(symbol, parent, expression.superQualifierSymbol)
}
)
} else {
expression
@@ -175,7 +179,9 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
symbol.owner.accessorParent(dispatchReceiverType?.classOrNull?.owner ?: symbol.owner.parent) as IrClass
modifySetterExpression(
expression,
setterMap.getOrPut(Pair(symbol, parent)) { makeSetterAccessorSymbol(symbol, parent) }
setterMap.getOrPut(FieldKey(symbol, parent, expression.superQualifierSymbol)) {
makeSetterAccessorSymbol(symbol, parent, expression.superQualifierSymbol)
}
)
} else {
expression
@@ -348,12 +354,16 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
copyAllParamsToArgs(it, accessor)
}
private fun makeGetterAccessorSymbol(fieldSymbol: IrFieldSymbol, parent: IrClass): IrSimpleFunctionSymbol =
private fun makeGetterAccessorSymbol(
fieldSymbol: IrFieldSymbol,
parent: IrClass,
superQualifierSymbol: IrClassSymbol?
): IrSimpleFunctionSymbol =
context.irFactory.buildFun {
startOffset = parent.startOffset
endOffset = parent.startOffset
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR
name = fieldSymbol.owner.accessorNameForGetter()
name = fieldSymbol.owner.accessorNameForGetter(superQualifierSymbol)
visibility = DescriptorVisibilities.PUBLIC
modality = Modality.FINAL
returnType = fieldSymbol.owner.type
@@ -368,10 +378,14 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
)
}
accessor.body = createAccessorBodyForGetter(fieldSymbol.owner, accessor)
accessor.body = createAccessorBodyForGetter(fieldSymbol.owner, accessor, superQualifierSymbol)
}.symbol
private fun createAccessorBodyForGetter(targetField: IrField, accessor: IrSimpleFunction): IrBody {
private fun createAccessorBodyForGetter(
targetField: IrField,
accessor: IrSimpleFunction,
superQualifierSymbol: IrClassSymbol?
): IrBody {
val maybeDispatchReceiver =
if (targetField.isStatic) null
else IrGetValueImpl(accessor.startOffset, accessor.endOffset, accessor.valueParameters[0].symbol)
@@ -381,17 +395,22 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
accessor.startOffset, accessor.endOffset,
targetField.symbol,
targetField.type,
maybeDispatchReceiver
maybeDispatchReceiver,
superQualifierSymbol = superQualifierSymbol
)
)
}
private fun makeSetterAccessorSymbol(fieldSymbol: IrFieldSymbol, parent: IrClass): IrSimpleFunctionSymbol =
private fun makeSetterAccessorSymbol(
fieldSymbol: IrFieldSymbol,
parent: IrClass,
superQualifierSymbol: IrClassSymbol?
): IrSimpleFunctionSymbol =
context.irFactory.buildFun {
startOffset = parent.startOffset
endOffset = parent.startOffset
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR
name = fieldSymbol.owner.accessorNameForSetter()
name = fieldSymbol.owner.accessorNameForSetter(superQualifierSymbol)
visibility = DescriptorVisibilities.PUBLIC
modality = Modality.FINAL
returnType = context.irBuiltIns.unitType
@@ -408,10 +427,14 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
accessor.addValueParameter("<set-?>", fieldSymbol.owner.type, JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR)
accessor.body = createAccessorBodyForSetter(fieldSymbol.owner, accessor)
accessor.body = createAccessorBodyForSetter(fieldSymbol.owner, accessor, superQualifierSymbol)
}.symbol
private fun createAccessorBodyForSetter(targetField: IrField, accessor: IrSimpleFunction): IrBody {
private fun createAccessorBodyForSetter(
targetField: IrField,
accessor: IrSimpleFunction,
superQualifierSymbol: IrClassSymbol?
): IrBody {
val maybeDispatchReceiver =
if (targetField.isStatic) null
else IrGetValueImpl(accessor.startOffset, accessor.endOffset, accessor.valueParameters[0].symbol)
@@ -426,7 +449,8 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
targetField.symbol,
maybeDispatchReceiver,
value,
context.irBuiltIns.unitType
context.irBuiltIns.unitType,
superQualifierSymbol = superQualifierSymbol
)
)
}
@@ -565,22 +589,26 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
return Name.identifier("access\$$jvmName$suffix")
}
private fun IrField.accessorNameForGetter(): Name {
private fun IrField.accessorNameForGetter(superQualifierSymbol: IrClassSymbol?): Name {
val getterName = JvmAbi.getterName(name.asString())
return Name.identifier("access\$$getterName\$${fieldAccessorSuffix()}")
return Name.identifier("access\$$getterName\$${fieldAccessorSuffix(superQualifierSymbol)}")
}
private fun IrField.accessorNameForSetter(): Name {
private fun IrField.accessorNameForSetter(superQualifierSymbol: IrClassSymbol?): Name {
val setterName = JvmAbi.setterName(name.asString())
return Name.identifier("access\$$setterName\$${fieldAccessorSuffix()}")
return Name.identifier("access\$$setterName\$${fieldAccessorSuffix(superQualifierSymbol)}")
}
private fun IrField.fieldAccessorSuffix(): String {
private fun IrField.fieldAccessorSuffix(superQualifierSymbol: IrClassSymbol?): String {
// Special _c_ompanion _p_roperty suffix for accessing companion backing field moved to outer
if (origin == JvmLoweredDeclarationOrigin.COMPANION_PROPERTY_BACKING_FIELD && !parentAsClass.isCompanion) {
return "cp"
}
if (superQualifierSymbol != null) {
return "p\$s${superQualifierSymbol.owner.syntheticAccessorToSuperSuffix()}"
}
// Accesses to static protected fields that need an accessor must be due to being inherited, hence accessed on a
// _s_upertype. If the field is static, the super class the access is on can be different and therefore
// we generate a suffix to distinguish access to field with different receiver types in the super hierarchy.
@@ -0,0 +1,22 @@
// FILE: C.kt
package test
class C: A.B() {
// For binary compatibility, two accessibility bridges should be generated in C:
// one for A.x and one for A.B.x.
// Otherwise, if a static 'x' field is added to A.B either A.x or A.B.x will be ignored.
// The JVM backend generates accessibility bridges for setters as well which is not necessary.
fun f() = ({ A.x + x })()
// Similarly for static functions. Two bridges should be generated for binary compatibility.
fun g() = ({ A.h() + h() })
}
// FILE: A.java
public class A {
protected static String x = "O";
protected static String h() { return "O"; }
public static class B extends A {
}
}
@@ -0,0 +1,39 @@
@kotlin.Metadata
final class test/C$f$1 {
// source: 'C.kt'
enclosing method test/C.f()Ljava/lang/String;
public final static field INSTANCE: test.C$f$1
inner (anonymous) class test/C$f$1
static method <clinit>(): void
method <init>(): void
public synthetic bridge method invoke(): java.lang.Object
public final @org.jetbrains.annotations.NotNull method invoke(): java.lang.String
}
@kotlin.Metadata
final class test/C$g$1 {
// source: 'C.kt'
enclosing method test/C.g()Lkotlin/jvm/functions/Function0;
public final static field INSTANCE: test.C$g$1
inner (anonymous) class test/C$g$1
static method <clinit>(): void
method <init>(): void
public synthetic bridge method invoke(): java.lang.Object
public final @org.jetbrains.annotations.NotNull method invoke(): java.lang.String
}
@kotlin.Metadata
public final class test/C {
// source: 'C.kt'
inner (anonymous) class test/C$f$1
inner (anonymous) class test/C$g$1
public method <init>(): void
public synthetic final static method access$getX$p$s65(): java.lang.String
public synthetic final static method access$getX$p$s66(): java.lang.String
public synthetic final static method access$h$s65(): java.lang.String
public synthetic final static method access$h$s66(): java.lang.String
public synthetic final static method access$setX$p$s65(p0: java.lang.String): void
public synthetic final static method access$setX$p$s66(p0: java.lang.String): void
public final @org.jetbrains.annotations.NotNull method f(): java.lang.String
public final @org.jetbrains.annotations.NotNull method g(): kotlin.jvm.functions.Function0
}
@@ -0,0 +1,37 @@
@kotlin.Metadata
final class test/C$f$1 {
// source: 'C.kt'
enclosing method test/C.f()Ljava/lang/String;
public final static field INSTANCE: test.C$f$1
inner (anonymous) class test/C$f$1
static method <clinit>(): void
method <init>(): void
public synthetic bridge method invoke(): java.lang.Object
public final @org.jetbrains.annotations.NotNull method invoke(): java.lang.String
}
@kotlin.Metadata
final class test/C$g$1 {
// source: 'C.kt'
enclosing method test/C.g()Lkotlin/jvm/functions/Function0;
public final static field INSTANCE: test.C$g$1
inner (anonymous) class test/C$g$1
static method <clinit>(): void
method <init>(): void
public synthetic bridge method invoke(): java.lang.Object
public final @org.jetbrains.annotations.NotNull method invoke(): java.lang.String
}
@kotlin.Metadata
public final class test/C {
// source: 'C.kt'
inner (anonymous) class test/C$f$1
inner (anonymous) class test/C$g$1
public method <init>(): void
public synthetic final static method access$getX$p$s65(): java.lang.String
public synthetic final static method access$getX$p$s66(): java.lang.String
public synthetic final static method access$h$s65(): java.lang.String
public synthetic final static method access$h$s66(): java.lang.String
public final @org.jetbrains.annotations.NotNull method f(): java.lang.String
public final @org.jetbrains.annotations.NotNull method g(): kotlin.jvm.functions.Function0
}
@@ -41,6 +41,11 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
runTest("compiler/testData/codegen/bytecodeListing/accessorForTopLevelMembers.kt");
}
@TestMetadata("accessorsForProtectedStaticJavaFieldInOtherPackage.kt")
public void testAccessorsForProtectedStaticJavaFieldInOtherPackage() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/accessorsForProtectedStaticJavaFieldInOtherPackage.kt");
}
public void testAllFilesPresentInBytecodeListing() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@@ -41,6 +41,11 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes
runTest("compiler/testData/codegen/bytecodeListing/accessorForTopLevelMembers.kt");
}
@TestMetadata("accessorsForProtectedStaticJavaFieldInOtherPackage.kt")
public void testAccessorsForProtectedStaticJavaFieldInOtherPackage() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/accessorsForProtectedStaticJavaFieldInOtherPackage.kt");
}
public void testAllFilesPresentInBytecodeListing() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}