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 37a510e5119..d7e18236fe9 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java @@ -934,6 +934,7 @@ public class JetTypeMapper { } for (ValueParameterDescriptor parameter : valueParameters) { + if (writeCustomParameter(f, parameter, sw)) continue; writeParameter(sw, parameter.getType()); } @@ -957,6 +958,23 @@ public class JetTypeMapper { return signature; } + private boolean writeCustomParameter( + @NotNull FunctionDescriptor f, + @NotNull ValueParameterDescriptor parameter, + @NotNull BothSignatureWriter sw + ) { + FunctionDescriptor overridden = BuiltinsPropertiesUtilKt.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(f); + if (overridden == null) return false; + if (BuiltinsPropertiesUtilKt.isFromJavaOrBuiltins(f)) return false; + + if (overridden.getName().asString().equals("remove") && mapType(parameter.getType()).getSort() == Type.INT) { + writeParameter(sw, TypeUtils.makeNullable(parameter.getType())); + return true; + } + + return false; + } + @NotNull public static String getDefaultDescriptor(@NotNull Method method, @Nullable String dispatchReceiverDescriptor, boolean isExtension) { String descriptor = method.getDescriptor(); diff --git a/compiler/testData/codegen/boxWithJava/collections/charSequence/J.java b/compiler/testData/codegen/boxWithJava/collections/charSequence/J.java new file mode 100644 index 00000000000..5694c12077b --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/collections/charSequence/J.java @@ -0,0 +1,24 @@ +import java.util.*; + +public class J { + + public static class B extends A { + public char get(int index) { + if (index == 1) return 'a'; + return super.get(index); + } + } + + public static String foo() { + B b = new B(); + CharSequence cs = (CharSequence) b; + + if (cs.charAt(0) != 'z') return "fail 1"; + if (b.get(0) != 'z') return "fail 2"; + + if (cs.charAt(1) != 'a') return "fail 3"; + if (b.get(1) != 'a') return "fail 4"; + + return "OK"; + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithJava/collections/charSequence/charSequence.kt b/compiler/testData/codegen/boxWithJava/collections/charSequence/charSequence.kt new file mode 100644 index 00000000000..8d94d1aedbf --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/collections/charSequence/charSequence.kt @@ -0,0 +1,28 @@ +open class A : CharSequence { + override fun length(): Int { + throw UnsupportedOperationException() + } + + override fun get(index: Int) = 'z'; + + override fun subSequence(start: Int, end: Int): CharSequence { + throw UnsupportedOperationException() + } +} + +fun box(): String { + val b = J.B() + val a = A() + + if (b[0] != 'z') return "fail 6" + if (a[0] != 'z') return "fail 7" + if (b[1] != 'a') return "fail 8" + if (a[0] != 'z') return "fail 9" + + if (b.get(0) != 'z') return "fail 10" + if (a.get(0) != 'z') return "fail 11" + if (b.get(1) != 'a') return "fail 12" + if (a.get(1) != 'z') return "fail 13" + + return J.foo(); +} diff --git a/compiler/testData/codegen/boxWithJava/collections/removeAtInt/J.java b/compiler/testData/codegen/boxWithJava/collections/removeAtInt/J.java new file mode 100644 index 00000000000..de6211e82e9 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/collections/removeAtInt/J.java @@ -0,0 +1,20 @@ +import java.util.*; + +public class J { + + private static class MyList extends A {} + + public static String foo() { + MyList myList = new MyList(); + List list = (List) myList; + + if (!list.remove((Integer) 1)) return "fail 1"; + if (list.remove((int) 1) != 123) return "fail 2"; + + if (!myList.remove((Integer) 1)) return "fail 3"; + if (myList.remove((int) 1) != 123) return "fail 4"; + + if (myList.removeAt(1) != 123) return "fail 5"; + return "OK"; + } +} diff --git a/compiler/testData/codegen/boxWithJava/collections/removeAtInt/removeAtInt.kt b/compiler/testData/codegen/boxWithJava/collections/removeAtInt/removeAtInt.kt new file mode 100644 index 00000000000..a4becb5b04a --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/collections/removeAtInt/removeAtInt.kt @@ -0,0 +1,80 @@ +open class A : MutableList { + override val size: Int + get() = throw UnsupportedOperationException() + override val isEmpty: Boolean + get() = throw UnsupportedOperationException() + + override fun contains(o: Int): Boolean { + throw UnsupportedOperationException() + } + + override fun containsAll(c: Collection): Boolean { + throw UnsupportedOperationException() + } + + override fun get(index: Int): Int { + throw UnsupportedOperationException() + } + + override fun indexOf(o: Any?): Int { + throw UnsupportedOperationException() + } + + override fun lastIndexOf(o: Any?): Int { + throw UnsupportedOperationException() + } + + override fun add(e: Int): Boolean { + throw UnsupportedOperationException() + } + + override fun remove(o: Int) = true + + override fun removeAt(index: Int): Int = 123 + + override fun addAll(c: Collection): Boolean { + throw UnsupportedOperationException() + } + + override fun addAll(index: Int, c: Collection): Boolean { + throw UnsupportedOperationException() + } + + override fun removeAll(c: Collection): Boolean { + throw UnsupportedOperationException() + } + + override fun retainAll(c: Collection): Boolean { + throw UnsupportedOperationException() + } + + override fun clear() { + throw UnsupportedOperationException() + } + + override fun set(index: Int, element: Int): Int { + throw UnsupportedOperationException() + } + + override fun add(index: Int, element: Int) { + throw UnsupportedOperationException() + } + + override fun listIterator(): MutableListIterator { + throw UnsupportedOperationException() + } + + override fun listIterator(index: Int): MutableListIterator { + throw UnsupportedOperationException() + } + + override fun subList(fromIndex: Int, toIndex: Int): MutableList { + throw UnsupportedOperationException() + } + + override fun iterator(): MutableIterator { + throw UnsupportedOperationException() + } +} + +fun box() = J.foo() \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/builtinFunctions/charSequence.kt b/compiler/testData/codegen/bytecodeText/builtinFunctions/charSequence.kt new file mode 100644 index 00000000000..5a2cd850bf4 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/builtinFunctions/charSequence.kt @@ -0,0 +1,7 @@ +abstract class A1 : CharSequence {} + +abstract class A2 : CharSequence { + override fun get(index: Int) = 'z'; +} + +// 2 public final bridge charAt \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/builtinFunctions/removeAt.kt b/compiler/testData/codegen/bytecodeText/builtinFunctions/removeAt.kt new file mode 100644 index 00000000000..3be8d660cf8 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/builtinFunctions/removeAt.kt @@ -0,0 +1,116 @@ +abstract class A1 : MutableList { + override fun remove(x: T): Boolean = true + override fun removeAt(index: Int): T = null!! +} + +abstract class A2 : MutableList { + override fun remove(x: String): Boolean = true + override fun removeAt(index: Int): String = null!! +} + +abstract class A3 : java.util.AbstractList() { + override fun remove(x: String): Boolean = true + override fun removeAt(index: Int): String = null!! +} + +abstract class A4 : java.util.AbstractList() { + override abstract fun remove(x: String): Boolean + override abstract fun removeAt(index: Int): String +} + +abstract class A5 : java.util.ArrayList() { + override fun remove(x: String): Boolean = true + override fun removeAt(index: Int): String = null!! +} + +abstract class A6 : java.util.ArrayList() { + override abstract fun remove(x: String): Boolean + override abstract fun removeAt(index: Int): String +} + +abstract class A7 : MutableList +abstract class A8 : java.util.ArrayList() + +interface A9 : MutableList {} + +abstract class A10 : MutableList { + override fun remove(x: Int): Boolean = true + override fun removeAt(index: Int): Int = 1 +} + +fun box( + a1: A1, + a2: A2, + a3: A3, + a4: A4, + a5: A5, + a6: A6, + a7: A7, + a8: A8, + a9: A9, + a10: A10, + c1: MutableList, + c2: MutableList +) { + a1.removeAt(1) + a1.remove("") + + a2.removeAt(1) + a2.remove("") + + a3.removeAt(1) + a3.remove("") + + a4.removeAt(1) + a4.remove("") + + a5.removeAt(1) + a5.remove("") + + a6.removeAt(1) + a6.remove("") + + a7.removeAt(1) + a7.remove("") + + a8.removeAt(1) + a8.remove("") + + a9.removeAt(1) + a9.remove("") + + a10.removeAt(1) + a10.remove(2) + + c1.removeAt(1) + c1.remove("") + + c2.removeAt(1) + c2.remove(2) +} + +/* +9 public final bridge remove\(I\) -> Bridges for removeAt from A2-A10 +7 public synthetic bridge remove\(I\)Ljava/lang/Object; -> Synthetic bridges from A2-A9 +16 INVOKEVIRTUAL A[0-9]+.removeAt \(I\) -> calls in bridges +1 public remove\(Ljava/lang/Integer;\)Z -> implementation in A10 +3 public abstract removeAt\(I\) -> A4, A6, A7 +1 INVOKEINTERFACE A9\.remove \(I\) -> call A9.removeAt +1 INVOKEINTERFACE A9\.remove \(Ljava/lang/Object;\) -> call A9.remove +9 INVOKEVIRTUAL A[0-9]+\.remove \(I\) -> calls to A1-A9.removeAt +*/ + +// 9 public final bridge remove\(I\) +// 7 public synthetic bridge remove\(I\)Ljava/lang/Object; +// 1 public remove\(Ljava/lang/Integer;\)Z +// 3 public abstract removeAt\(I\) +// 16 INVOKEVIRTUAL A[0-9]+.removeAt \(I\) +// 1 INVOKEINTERFACE A9\.remove \(I\) +// 1 INVOKEINTERFACE A9\.remove \(Ljava/lang/Object;\) +// 9 INVOKEVIRTUAL A[0-9]+\.remove \(I\) +// 1 INVOKEVIRTUAL A10\.remove \(I\) +// 9 INVOKEVIRTUAL A[0-9]+\.remove \(I\) +// 1 INVOKEVIRTUAL A10\.remove \(I\) +// 2 INVOKEINTERFACE java\/util\/List.remove \(I\) +// 2 INVOKEINTERFACE java\/util\/List.remove \(Ljava/lang/Object;\) + diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 64556859050..4c5a7ea27a4 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -367,12 +367,24 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/builtinFunctions"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("charSequence.kt") + public void testCharSequence() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/builtinFunctions/charSequence.kt"); + doTest(fileName); + } + @TestMetadata("contains.kt") public void testContains() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/builtinFunctions/contains.kt"); doTest(fileName); } + @TestMetadata("removeAt.kt") + public void testRemoveAt() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/builtinFunctions/removeAt.kt"); + doTest(fileName); + } + @TestMetadata("size.kt") public void testSize() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/builtinFunctions/size.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java index a58bba23eb1..004ccd3f7a4 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java @@ -179,12 +179,24 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithJava/collections"), Pattern.compile("^([^\\.]+)$"), true); } + @TestMetadata("charSequence") + public void testCharSequence() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/collections/charSequence/"); + doTestWithJava(fileName); + } + @TestMetadata("mutableList") public void testMutableList() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/collections/mutableList/"); doTestWithJava(fileName); } + @TestMetadata("removeAtInt") + public void testRemoveAtInt() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/collections/removeAtInt/"); + doTestWithJava(fileName); + } + @TestMetadata("strList") public void testStrList() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/collections/strList/"); diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/builtinsPropertiesUtil.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/builtinsPropertiesUtil.kt index 27f2e3b6963..b734f452da2 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/builtinsPropertiesUtil.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/builtinsPropertiesUtil.kt @@ -115,11 +115,13 @@ public fun CallableMemberDescriptor.getJvmMethodNameIfSpecial(): String? { private fun CallableMemberDescriptor.getBuiltinOverriddenThatAffectsJvmName(): CallableMemberDescriptor? { val overriddenBuiltin = getBuiltinSpecialOverridden() ?: return null - if (isFromJava || isFromBuiltins()) return overriddenBuiltin + if (isFromJavaOrBuiltins()) return overriddenBuiltin return null } +fun CallableMemberDescriptor.isFromJavaOrBuiltins() = isFromJava || isFromBuiltins() + private fun CallableMemberDescriptor.specialJvmName(): Name? { return BuiltinSpecialMethods.FQ_NAMES_TO_JVM_MAP[fqNameOrNull() ?: return null] }