diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt index 62e5b8d0a60..bccad36e16c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt @@ -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, IrFunctionSymbol>() - private val getterMap = mutableMapOf, IrSimpleFunctionSymbol>() - private val setterMap = mutableMapOf, IrSimpleFunctionSymbol>() + private val getterMap = mutableMapOf() + private val setterMap = mutableMapOf() 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("", 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. diff --git a/compiler/testData/codegen/bytecodeListing/accessorsForProtectedStaticJavaFieldInOtherPackage.kt b/compiler/testData/codegen/bytecodeListing/accessorsForProtectedStaticJavaFieldInOtherPackage.kt new file mode 100644 index 00000000000..1b4cc64ff53 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/accessorsForProtectedStaticJavaFieldInOtherPackage.kt @@ -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 { + + } +} + diff --git a/compiler/testData/codegen/bytecodeListing/accessorsForProtectedStaticJavaFieldInOtherPackage.txt b/compiler/testData/codegen/bytecodeListing/accessorsForProtectedStaticJavaFieldInOtherPackage.txt new file mode 100644 index 00000000000..df53b286d3c --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/accessorsForProtectedStaticJavaFieldInOtherPackage.txt @@ -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 (): void + method (): 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 (): void + method (): 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 (): 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 +} diff --git a/compiler/testData/codegen/bytecodeListing/accessorsForProtectedStaticJavaFieldInOtherPackage_ir.txt b/compiler/testData/codegen/bytecodeListing/accessorsForProtectedStaticJavaFieldInOtherPackage_ir.txt new file mode 100644 index 00000000000..757bed47689 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/accessorsForProtectedStaticJavaFieldInOtherPackage_ir.txt @@ -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 (): void + method (): 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 (): void + method (): 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 (): 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 +} diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index ff9c69c972a..a1e5951d85e 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -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); } diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java index 096199f57f7..3538229668d 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java @@ -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); }