diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForFunctionDescriptor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForFunctionDescriptor.java index 141b3c12cde..1b65ba54d33 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForFunctionDescriptor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForFunctionDescriptor.java @@ -41,7 +41,7 @@ public class AccessorForFunctionDescriptor extends SimpleFunctionDescriptorImpl int index ) { super(containingDeclaration, null, Annotations.EMPTY, - Name.identifier((descriptor instanceof ConstructorDescriptor ? "$init" : descriptor.getName()) + "$b$" + index), + Name.identifier("access$" + (descriptor instanceof ConstructorDescriptor ? "init" : descriptor.getName()) + "$" + index), Kind.DECLARATION, SourceElement.NO_SOURCE); this.calleeDescriptor = descriptor; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForPropertyDescriptor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForPropertyDescriptor.java index 9d0f31093cd..2408a0c4df7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForPropertyDescriptor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForPropertyDescriptor.java @@ -32,6 +32,7 @@ import java.util.Collections; public class AccessorForPropertyDescriptor extends PropertyDescriptorImpl implements AccessorForCallableDescriptor { private final PropertyDescriptor calleeDescriptor; + private final int accessorIndex; public AccessorForPropertyDescriptor(@NotNull PropertyDescriptor pd, @NotNull DeclarationDescriptor containingDeclaration, int index) { this(pd, pd.getType(), DescriptorUtils.getReceiverParameterType(pd.getExtensionReceiverParameter()), pd.getDispatchReceiverParameter(), containingDeclaration, index); @@ -46,10 +47,11 @@ public class AccessorForPropertyDescriptor extends PropertyDescriptorImpl implem int index ) { super(containingDeclaration, null, Annotations.EMPTY, Modality.FINAL, Visibilities.LOCAL, - original.isVar(), Name.identifier(original.getName() + "$b$" + index), + original.isVar(), Name.identifier("access$" + getIndexedAccessorSuffix(original, index)), Kind.DECLARATION, SourceElement.NO_SOURCE); this.calleeDescriptor = original; + this.accessorIndex = index; setType(propertyType, Collections.emptyList(), dispatchReceiverParameter, receiverType); initialize(new Getter(this), new Setter(this)); } @@ -89,4 +91,14 @@ public class AccessorForPropertyDescriptor extends PropertyDescriptorImpl implem public PropertyDescriptor getCalleeDescriptor() { return calleeDescriptor; } + + @NotNull + public String getIndexedAccessorSuffix() { + return getIndexedAccessorSuffix(calleeDescriptor, accessorIndex); + } + + @NotNull + private static String getIndexedAccessorSuffix(@NotNull PropertyDescriptor original, int index) { + return original.getName() + "$" + index; + } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java index 8f0b2c9c83f..6c0925ea2a2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java @@ -37,6 +37,7 @@ import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; import org.jetbrains.kotlin.load.kotlin.nativeDeclarations.NativeDeclarationsPackage; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqNameUnsafe; +import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.name.SpecialNames; import org.jetbrains.kotlin.psi.JetExpression; import org.jetbrains.kotlin.psi.JetFile; @@ -590,12 +591,16 @@ public class JetTypeMapper { return property.getName().asString(); } - if (descriptor instanceof PropertyGetterDescriptor) { - return PropertyCodegen.getterName(property.getName()); - } - else { - return PropertyCodegen.setterName(property.getName()); - } + boolean isAccessor = property instanceof AccessorForPropertyDescriptor; + Name propertyName = isAccessor + ? Name.identifier(((AccessorForPropertyDescriptor) property).getIndexedAccessorSuffix()) + : property.getName(); + + String accessorName = descriptor instanceof PropertyGetterDescriptor + ? PropertyCodegen.getterName(propertyName) + : PropertyCodegen.setterName(propertyName); + + return isAccessor ? "access$" + accessorName : accessorName; } else if (isLocalNamedFun(descriptor)) { return "invoke"; diff --git a/compiler/testData/cli/jvm/signatureClash.kt b/compiler/testData/cli/jvm/signatureClash.kt index 5243e6c9342..e7e5eafa6d1 100644 --- a/compiler/testData/cli/jvm/signatureClash.kt +++ b/compiler/testData/cli/jvm/signatureClash.kt @@ -23,7 +23,7 @@ class SubTr : Tr { // Clashing synthetic accessors are only reported in compiler, IDE doesn't see them class C { private fun f() {} - fun `f$b$0`(c: C) {} + fun `access$f$0`(c: C) {} class Nested { @@ -31,4 +31,4 @@ class C { C().f() } } -} \ No newline at end of file +} diff --git a/compiler/testData/cli/jvm/signatureClash.out b/compiler/testData/cli/jvm/signatureClash.out index 7600e656965..c810f8bc4e0 100644 --- a/compiler/testData/cli/jvm/signatureClash.out +++ b/compiler/testData/cli/jvm/signatureClash.out @@ -1,4 +1,4 @@ -WARNING: compiler/testData/cli/jvm/signatureClash.kt: (26, 17) Parameter 'c' is never used +WARNING: compiler/testData/cli/jvm/signatureClash.kt: (26, 22) Parameter 'c' is never used ERROR: compiler/testData/cli/jvm/signatureClash.kt: (6, 5) Accidental override: The following declarations have the same JVM signature (getX()I): fun getX(): kotlin.Int fun (): kotlin.Int @@ -20,10 +20,10 @@ ERROR: compiler/testData/cli/jvm/signatureClash.kt: (19, 7) Platform declaration ERROR: compiler/testData/cli/jvm/signatureClash.kt: (20, 5) Platform declaration clash: The following declarations have the same JVM signature (getTr()I): fun (): kotlin.Int fun getTr(): kotlin.Int -ERROR: compiler/testData/cli/jvm/signatureClash.kt: (24, 7) Platform declaration clash: The following declarations have the same JVM signature (f$b$0(LC;)V): - fun `f$b$0`(c: C): kotlin.Unit +ERROR: compiler/testData/cli/jvm/signatureClash.kt: (24, 7) Platform declaration clash: The following declarations have the same JVM signature (access$f$0(LC;)V): + fun `access$f$0`(c: C): kotlin.Unit fun f(): kotlin.Unit -ERROR: compiler/testData/cli/jvm/signatureClash.kt: (26, 5) Platform declaration clash: The following declarations have the same JVM signature (f$b$0(LC;)V): - fun `f$b$0`(c: C): kotlin.Unit +ERROR: compiler/testData/cli/jvm/signatureClash.kt: (26, 5) Platform declaration clash: The following declarations have the same JVM signature (access$f$0(LC;)V): + fun `access$f$0`(c: C): kotlin.Unit fun f(): kotlin.Unit COMPILATION_ERROR \ No newline at end of file diff --git a/compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt b/compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt index 8cf9f52cc53..286cdef16be 100644 --- a/compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt +++ b/compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt @@ -1,14 +1,14 @@ open class Base { - open fun `foo$b$0`(d: Derived) {} + open fun `access$foo$0`(d: Derived) {} - open fun `getBar$b$1`(d: Derived): Int = 1 - open fun `setBar$b$1`(d: Derived, i: Int) {} + open fun `access$getBar$1`(d: Derived): Int = 1 + open fun `access$setBar$1`(d: Derived, i: Int) {} - open fun `getBaz$b$2`(d: Derived): Int = 1 + open fun `access$getBaz$2`(d: Derived): Int = 1 - open fun `getBoo$b$3`(d: Derived): Int = 1 + open fun `access$getBoo$3`(d: Derived): Int = 1 - open fun `setBar1$b$4`(d: Derived, i: Int) {} + open fun `access$setBar1$4`(d: Derived, i: Int) {} } class Derived : Base() { diff --git a/compiler/testData/cli/jvm/syntheticAccessorSignatureClash.out b/compiler/testData/cli/jvm/syntheticAccessorSignatureClash.out index 417347ffb81..22c692f2cf4 100644 --- a/compiler/testData/cli/jvm/syntheticAccessorSignatureClash.out +++ b/compiler/testData/cli/jvm/syntheticAccessorSignatureClash.out @@ -1,20 +1,20 @@ WARNING: compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt: (34, 17) Variable 's' is never used -ERROR: compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt: (15, 5) Accidental override: The following declarations have the same JVM signature (foo$b$0(LDerived;)V): - fun `foo$b$0`(d: Derived): kotlin.Unit +ERROR: compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt: (15, 5) Accidental override: The following declarations have the same JVM signature (access$foo$0(LDerived;)V): + fun `access$foo$0`(d: Derived): kotlin.Unit fun foo(): kotlin.Unit -ERROR: compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt: (18, 9) Accidental override: The following declarations have the same JVM signature (getBar$b$1(LDerived;)I): - fun `getBar$b$1`(d: Derived): kotlin.Int +ERROR: compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt: (18, 9) Accidental override: The following declarations have the same JVM signature (access$getBar$1(LDerived;)I): + fun `access$getBar$1`(d: Derived): kotlin.Int fun (): kotlin.Int -ERROR: compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt: (19, 9) Accidental override: The following declarations have the same JVM signature (setBar$b$1(LDerived;I)V): - fun `setBar$b$1`(d: Derived, i: kotlin.Int): kotlin.Unit +ERROR: compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt: (19, 9) Accidental override: The following declarations have the same JVM signature (access$setBar$1(LDerived;I)V): + fun `access$setBar$1`(d: Derived, i: kotlin.Int): kotlin.Unit fun (: kotlin.Int): kotlin.Unit -ERROR: compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt: (21, 5) Accidental override: The following declarations have the same JVM signature (getBaz$b$2(LDerived;)I): - fun `getBaz$b$2`(d: Derived): kotlin.Int +ERROR: compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt: (21, 5) Accidental override: The following declarations have the same JVM signature (access$getBaz$2(LDerived;)I): + fun `access$getBaz$2`(d: Derived): kotlin.Int fun (): kotlin.Int -ERROR: compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt: (23, 5) Accidental override: The following declarations have the same JVM signature (getBoo$b$3(LDerived;)I): - fun `getBoo$b$3`(d: Derived): kotlin.Int +ERROR: compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt: (23, 5) Accidental override: The following declarations have the same JVM signature (access$getBoo$3(LDerived;)I): + fun `access$getBoo$3`(d: Derived): kotlin.Int fun (): kotlin.Int -ERROR: compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt: (27, 9) Accidental override: The following declarations have the same JVM signature (setBar1$b$4(LDerived;I)V): - fun `setBar1$b$4`(d: Derived, i: kotlin.Int): kotlin.Unit +ERROR: compiler/testData/cli/jvm/syntheticAccessorSignatureClash.kt: (27, 9) Accidental override: The following declarations have the same JVM signature (access$setBar1$4(LDerived;I)V): + fun `access$setBar1$4`(d: Derived, i: kotlin.Int): kotlin.Unit fun (: kotlin.Int): kotlin.Unit COMPILATION_ERROR \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/synthetic/syntheticAccessorNames.kt b/compiler/testData/codegen/boxWithStdlib/synthetic/syntheticAccessorNames.kt new file mode 100644 index 00000000000..612c68b7a68 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/synthetic/syntheticAccessorNames.kt @@ -0,0 +1,48 @@ +// This test checks that synthetic accessors generated by Kotlin compiler have names starting with "access$" +// This is crucial for some JVM frameworks like Quasar which rely on the bytecode being similar to the one generated by javac +// See https://youtrack.jetbrains.com/issue/KT-6870 + +class PrivateConstructor private() { + class Nested { val a = PrivateConstructor() } +} + +class PrivatePropertyGet { + private val x = 42 + + inner class Inner { val a = x } +} + +class PrivatePropertySet { + private var x = "a" + + inner class Inner { { x = "b" } } +} + +class PrivateMethod { + private fun foo() = "" + + inner class Inner { val a = foo() } +} + +fun check(klass: Class<*>) { + for (method in klass.getDeclaredMethods()) { + if (method.isSynthetic() && method.getName().startsWith("access$")) return + } + + throw AssertionError("No synthetic methods starting with 'access$' found in class $klass") +} + +fun box(): String { + check(javaClass()) + check(javaClass()) + check(javaClass()) + check(javaClass()) + + // Also check that synthetic accessors really work + PrivateConstructor.Nested() + PrivatePropertyGet().Inner() + PrivatePropertySet().Inner() + PrivateMethod().Inner() + + return "OK" +} diff --git a/compiler/testData/codegen/bytecodeText/staticFields/classObjectSyntheticAccessor.kt b/compiler/testData/codegen/bytecodeText/staticFields/classObjectSyntheticAccessor.kt index 81cc1387809..34c92450825 100644 --- a/compiler/testData/codegen/bytecodeText/staticFields/classObjectSyntheticAccessor.kt +++ b/compiler/testData/codegen/bytecodeText/staticFields/classObjectSyntheticAccessor.kt @@ -5,4 +5,4 @@ class A { } // A and class object constructor call // 2 ALOAD 0 -// 1 synthetic getR \ No newline at end of file +// 1 synthetic access\$getR diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/MethodOrderTest.kt b/compiler/tests/org/jetbrains/kotlin/codegen/MethodOrderTest.kt index 2a6774c007b..c5562148dd6 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/MethodOrderTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/codegen/MethodOrderTest.kt @@ -96,7 +96,15 @@ public class MethodOrderTest: CodegenTestCase() { } """, "Outer", - listOf("()V", "c()V", "(ILjava/lang/String;)V", "getB\$b$0(LOuter;)Ljava/lang/String;", "setB\$b$0(LOuter;Ljava/lang/String;)V", "getA\$b$1(LOuter;)I", "c\$b$2(LOuter;)V") + listOf( + "()V", + "c()V", + "(ILjava/lang/String;)V", + "access\$getB$0(LOuter;)Ljava/lang/String;", + "access\$setB$0(LOuter;Ljava/lang/String;)V", + "access\$getA$1(LOuter;)I", + "access\$c$2(LOuter;)V" + ) ) } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index a05d585cfa3..0a234871a95 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -60,6 +60,7 @@ import java.util.regex.Pattern; BlackBoxWithStdlibCodegenTestGenerated.Reified.class, BlackBoxWithStdlibCodegenTestGenerated.StoreStackBeforeInline.class, BlackBoxWithStdlibCodegenTestGenerated.Strings.class, + BlackBoxWithStdlibCodegenTestGenerated.Synthetic.class, BlackBoxWithStdlibCodegenTestGenerated.ToArray.class, BlackBoxWithStdlibCodegenTestGenerated.Vararg.class, BlackBoxWithStdlibCodegenTestGenerated.When.class, @@ -3201,6 +3202,21 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode } } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/synthetic") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Synthetic extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInSynthetic() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/synthetic"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("syntheticAccessorNames.kt") + public void testSyntheticAccessorNames() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/synthetic/syntheticAccessorNames.kt"); + doTestWithStdlib(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/toArray") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)