[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:
committed by
Alexander Udalov
parent
62897a194b
commit
6a959fefd0
+47
-19
@@ -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.
|
||||
|
||||
Vendored
+22
@@ -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 {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+39
@@ -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
|
||||
}
|
||||
Vendored
+37
@@ -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
|
||||
}
|
||||
+5
@@ -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);
|
||||
}
|
||||
|
||||
+5
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user