[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 functionMap = mutableMapOf<Pair<IrFunctionSymbol, IrDeclarationParent>, IrFunctionSymbol>()
|
||||||
private val getterMap = mutableMapOf<Pair<IrFieldSymbol, IrDeclarationParent>, IrSimpleFunctionSymbol>()
|
private val getterMap = mutableMapOf<FieldKey, IrSimpleFunctionSymbol>()
|
||||||
private val setterMap = mutableMapOf<Pair<IrFieldSymbol, IrDeclarationParent>, IrSimpleFunctionSymbol>()
|
private val setterMap = mutableMapOf<FieldKey, IrSimpleFunctionSymbol>()
|
||||||
|
|
||||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
|
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
|
||||||
if (expression.usesDefaultArguments()) {
|
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
|
symbol.owner.accessorParent(dispatchReceiverType?.classOrNull?.owner ?: symbol.owner.parent) as IrClass
|
||||||
modifyGetterExpression(
|
modifyGetterExpression(
|
||||||
expression,
|
expression,
|
||||||
getterMap.getOrPut(Pair(symbol, parent)) { makeGetterAccessorSymbol(symbol, parent) }
|
getterMap.getOrPut(FieldKey(symbol, parent, expression.superQualifierSymbol)) {
|
||||||
|
makeGetterAccessorSymbol(symbol, parent, expression.superQualifierSymbol)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
expression
|
expression
|
||||||
@@ -175,7 +179,9 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
|
|||||||
symbol.owner.accessorParent(dispatchReceiverType?.classOrNull?.owner ?: symbol.owner.parent) as IrClass
|
symbol.owner.accessorParent(dispatchReceiverType?.classOrNull?.owner ?: symbol.owner.parent) as IrClass
|
||||||
modifySetterExpression(
|
modifySetterExpression(
|
||||||
expression,
|
expression,
|
||||||
setterMap.getOrPut(Pair(symbol, parent)) { makeSetterAccessorSymbol(symbol, parent) }
|
setterMap.getOrPut(FieldKey(symbol, parent, expression.superQualifierSymbol)) {
|
||||||
|
makeSetterAccessorSymbol(symbol, parent, expression.superQualifierSymbol)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
expression
|
expression
|
||||||
@@ -348,12 +354,16 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
|
|||||||
copyAllParamsToArgs(it, accessor)
|
copyAllParamsToArgs(it, accessor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeGetterAccessorSymbol(fieldSymbol: IrFieldSymbol, parent: IrClass): IrSimpleFunctionSymbol =
|
private fun makeGetterAccessorSymbol(
|
||||||
|
fieldSymbol: IrFieldSymbol,
|
||||||
|
parent: IrClass,
|
||||||
|
superQualifierSymbol: IrClassSymbol?
|
||||||
|
): IrSimpleFunctionSymbol =
|
||||||
context.irFactory.buildFun {
|
context.irFactory.buildFun {
|
||||||
startOffset = parent.startOffset
|
startOffset = parent.startOffset
|
||||||
endOffset = parent.startOffset
|
endOffset = parent.startOffset
|
||||||
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR
|
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR
|
||||||
name = fieldSymbol.owner.accessorNameForGetter()
|
name = fieldSymbol.owner.accessorNameForGetter(superQualifierSymbol)
|
||||||
visibility = DescriptorVisibilities.PUBLIC
|
visibility = DescriptorVisibilities.PUBLIC
|
||||||
modality = Modality.FINAL
|
modality = Modality.FINAL
|
||||||
returnType = fieldSymbol.owner.type
|
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
|
}.symbol
|
||||||
|
|
||||||
private fun createAccessorBodyForGetter(targetField: IrField, accessor: IrSimpleFunction): IrBody {
|
private fun createAccessorBodyForGetter(
|
||||||
|
targetField: IrField,
|
||||||
|
accessor: IrSimpleFunction,
|
||||||
|
superQualifierSymbol: IrClassSymbol?
|
||||||
|
): IrBody {
|
||||||
val maybeDispatchReceiver =
|
val maybeDispatchReceiver =
|
||||||
if (targetField.isStatic) null
|
if (targetField.isStatic) null
|
||||||
else IrGetValueImpl(accessor.startOffset, accessor.endOffset, accessor.valueParameters[0].symbol)
|
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,
|
accessor.startOffset, accessor.endOffset,
|
||||||
targetField.symbol,
|
targetField.symbol,
|
||||||
targetField.type,
|
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 {
|
context.irFactory.buildFun {
|
||||||
startOffset = parent.startOffset
|
startOffset = parent.startOffset
|
||||||
endOffset = parent.startOffset
|
endOffset = parent.startOffset
|
||||||
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR
|
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR
|
||||||
name = fieldSymbol.owner.accessorNameForSetter()
|
name = fieldSymbol.owner.accessorNameForSetter(superQualifierSymbol)
|
||||||
visibility = DescriptorVisibilities.PUBLIC
|
visibility = DescriptorVisibilities.PUBLIC
|
||||||
modality = Modality.FINAL
|
modality = Modality.FINAL
|
||||||
returnType = context.irBuiltIns.unitType
|
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.addValueParameter("<set-?>", fieldSymbol.owner.type, JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR)
|
||||||
|
|
||||||
accessor.body = createAccessorBodyForSetter(fieldSymbol.owner, accessor)
|
accessor.body = createAccessorBodyForSetter(fieldSymbol.owner, accessor, superQualifierSymbol)
|
||||||
}.symbol
|
}.symbol
|
||||||
|
|
||||||
private fun createAccessorBodyForSetter(targetField: IrField, accessor: IrSimpleFunction): IrBody {
|
private fun createAccessorBodyForSetter(
|
||||||
|
targetField: IrField,
|
||||||
|
accessor: IrSimpleFunction,
|
||||||
|
superQualifierSymbol: IrClassSymbol?
|
||||||
|
): IrBody {
|
||||||
val maybeDispatchReceiver =
|
val maybeDispatchReceiver =
|
||||||
if (targetField.isStatic) null
|
if (targetField.isStatic) null
|
||||||
else IrGetValueImpl(accessor.startOffset, accessor.endOffset, accessor.valueParameters[0].symbol)
|
else IrGetValueImpl(accessor.startOffset, accessor.endOffset, accessor.valueParameters[0].symbol)
|
||||||
@@ -426,7 +449,8 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
|
|||||||
targetField.symbol,
|
targetField.symbol,
|
||||||
maybeDispatchReceiver,
|
maybeDispatchReceiver,
|
||||||
value,
|
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")
|
return Name.identifier("access\$$jvmName$suffix")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrField.accessorNameForGetter(): Name {
|
private fun IrField.accessorNameForGetter(superQualifierSymbol: IrClassSymbol?): Name {
|
||||||
val getterName = JvmAbi.getterName(name.asString())
|
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())
|
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
|
// Special _c_ompanion _p_roperty suffix for accessing companion backing field moved to outer
|
||||||
if (origin == JvmLoweredDeclarationOrigin.COMPANION_PROPERTY_BACKING_FIELD && !parentAsClass.isCompanion) {
|
if (origin == JvmLoweredDeclarationOrigin.COMPANION_PROPERTY_BACKING_FIELD && !parentAsClass.isCompanion) {
|
||||||
return "cp"
|
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
|
// 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
|
// _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.
|
// 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");
|
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 {
|
public void testAllFilesPresentInBytecodeListing() throws Exception {
|
||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
|
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");
|
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 {
|
public void testAllFilesPresentInBytecodeListing() throws Exception {
|
||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
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