diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CollectionStubMethodGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/CollectionStubMethodGenerator.kt index 8fdaa4bd1f5..49a3e9d752c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/CollectionStubMethodGenerator.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CollectionStubMethodGenerator.kt @@ -108,15 +108,13 @@ class CollectionStubMethodGenerator( val genericSignatureInfo = overriddenMethod.getSpecialSignatureInfo() val specialGenericSignature = - if (genericSignatureInfo != null) - genericSignatureInfo.signature - else - overriddenMethodSignature.genericsSignature + genericSignatureInfo?.replaceValueParametersIn(overriddenMethodSignature.genericsSignature) + ?: overriddenMethodSignature.genericsSignature val (asmMethod, valueParameters) = - // if remove(E) in Kotlin -> remove(Object) in Java - // so choose original signature - if (genericSignatureInfo == SpecialSignatureInfo.GENERIC_PARAMETER) + // if current method has special generic signature, + // like `Collection.remove(E): Boolean` in Kotlin, use original signature to obtain `remove(Object)` + if (genericSignatureInfo?.isObjectReplacedWithTypeParameter ?: false) Pair(originalSignature.asmMethod, originalSignature.valueParameters) else Pair(overriddenMethodSignature.asmMethod, overriddenMethodSignature.valueParameters) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt index 22a4893490e..97fd96a6ad5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt @@ -153,12 +153,7 @@ public fun isValueArgumentForCallToMethodWithTypeCheckBarrier( if (KtPsiUtil.deparenthesize(argumentExpression) !== element) return false val candidateDescriptor = parentCall.getResolvedCall(bindingContext)?.candidateDescriptor as CallableMemberDescriptor? - ?: return false + ?: return false - - if (candidateDescriptor.getSpecialSignatureInfo() == BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo.GENERIC_PARAMETER) { - return true - } - - return false + return candidateDescriptor.getSpecialSignatureInfo()?.isObjectReplacedWithTypeParameter ?: false } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt index 3150bfc799d..28c8065782b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.codegen +import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter @@ -49,3 +50,7 @@ fun generateIsCheck( generateInstanceOfInstruction(v) } } + + +public fun SpecialSignatureInfo.replaceValueParametersIn(sourceSignature: String?): String? + = valueParametersSignature?.let { sourceSignature?.replace("^\\(.*\\)".toRegex(), "($it)") } 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 3e3a91663c9..e367c486c60 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java @@ -1006,8 +1006,9 @@ public class JetTypeMapper { SpecialSignatureInfo specialSignatureInfo = BuiltinMethodsWithSpecialGenericSignature.getSpecialSignatureInfo(f); if (specialSignatureInfo != null) { - return new JvmMethodSignature( - signature.getAsmMethod(), specialSignatureInfo.getSignature(), signature.getValueParameters()); + String newGenericSignature = CodegenUtilKt.replaceValueParametersIn( + specialSignatureInfo, signature.getGenericsSignature()); + return new JvmMethodSignature(signature.getAsmMethod(), newGenericSignature, signature.getValueParameters()); } } diff --git a/compiler/testData/codegen/boxWithJava/collections/readOnlyMap/J.java b/compiler/testData/codegen/boxWithJava/collections/readOnlyMap/J.java new file mode 100644 index 00000000000..d938c43279d --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/collections/readOnlyMap/J.java @@ -0,0 +1,13 @@ +import java.util.*; + +public class J { + + private static class MyMap extends KMap {} + + public static String foo() { + Map collection = new MyMap(); + if (!collection.containsKey("ABCDE")) return "fail 1"; + if (!collection.containsValue(1)) return "fail 2"; + return "OK"; + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithJava/collections/readOnlyMap/readOnlyMap.kt b/compiler/testData/codegen/boxWithJava/collections/readOnlyMap/readOnlyMap.kt new file mode 100644 index 00000000000..2f506290bea --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/collections/readOnlyMap/readOnlyMap.kt @@ -0,0 +1,24 @@ +open class KMap : Map { + override val size: Int + get() = throw UnsupportedOperationException() + + override fun isEmpty(): Boolean { + throw UnsupportedOperationException() + } + + override fun containsKey(key: K) = true + override fun containsValue(value: V) = true + + override fun get(key: K): V? { + throw UnsupportedOperationException() + } + + override val keys: Set + get() = throw UnsupportedOperationException() + override val values: Collection + get() = throw UnsupportedOperationException() + override val entries: Set> + get() = throw UnsupportedOperationException() +} + +fun box() = J.foo() diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java index 1c84d1296d4..eca9efca2a9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java @@ -293,6 +293,12 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege doTestWithJava(fileName); } + @TestMetadata("readOnlyMap") + public void testReadOnlyMap() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/collections/readOnlyMap/"); + doTestWithJava(fileName); + } + @TestMetadata("removeAtInt") public void testRemoveAtInt() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/collections/removeAtInt/"); diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt index bd53631b93c..5a2720ab621 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt @@ -124,14 +124,15 @@ object BuiltinMethodsWithSpecialGenericSignature { val Name.sameAsBuiltinMethodWithErasedValueParameters: Boolean get () = this in ERASED_VALUE_PARAMETERS_SHORT_NAMES - enum class SpecialSignatureInfo(val signature: String?) { - ONE_COLLECTION_PARAMETER("(Ljava/util/Collection<+Ljava/lang/Object;>;)Z"), - GENERIC_PARAMETER(null) + enum class SpecialSignatureInfo(val valueParametersSignature: String?, val isObjectReplacedWithTypeParameter: Boolean) { + ONE_COLLECTION_PARAMETER("Ljava/util/Collection<+Ljava/lang/Object;>;", false), + OBJECT_PARAMETER_NON_GENERIC(null, true), + OBJECT_PARAMETER_GENERIC("Ljava/lang/Object;", true) } fun CallableMemberDescriptor.isBuiltinWithSpecialDescriptorInJvm(): Boolean { if (!isFromBuiltins()) return false - return getSpecialSignatureInfo() == SpecialSignatureInfo.GENERIC_PARAMETER || doesOverrideBuiltinWithDifferentJvmName() + return getSpecialSignatureInfo()?.isObjectReplacedWithTypeParameter ?: false || doesOverrideBuiltinWithDifferentJvmName() } @JvmStatic @@ -139,11 +140,15 @@ object BuiltinMethodsWithSpecialGenericSignature { val builtinFqName = firstOverridden { it is FunctionDescriptor && it.hasErasedValueParametersInJava }?.fqNameOrNull() ?: return null - return when (builtinFqName) { - in ERASED_COLLECTION_PARAMETER_FQ_NAMES -> SpecialSignatureInfo.ONE_COLLECTION_PARAMETER - in GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP -> SpecialSignatureInfo.GENERIC_PARAMETER - else -> error("Unexpected kind of special builtin: $builtinFqName") - } + if (builtinFqName in ERASED_COLLECTION_PARAMETER_FQ_NAMES) return SpecialSignatureInfo.ONE_COLLECTION_PARAMETER + + val defaultValue = GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP[builtinFqName]!! + + return if (defaultValue == DefaultValue.NULL) + // return type is some generic type as 'Map.get' + SpecialSignatureInfo.OBJECT_PARAMETER_GENERIC + else + SpecialSignatureInfo.OBJECT_PARAMETER_NON_GENERIC } } @@ -207,8 +212,7 @@ fun T.getOverriddenBuiltinWithDifferentJvmDescrip if (!name.sameAsBuiltinMethodWithErasedValueParameters) return null return firstOverridden { - it.isFromBuiltins() - && it.getSpecialSignatureInfo() == BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo.GENERIC_PARAMETER + it.isFromBuiltins() && it.getSpecialSignatureInfo()?.isObjectReplacedWithTypeParameter ?: false }?.original as T? }