diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 5bdb363b652..ea068617c73 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -937,13 +937,19 @@ public class FunctionCodegen { @NotNull Type returnType, @Nullable Type[] delegateParameterTypes ) { - BuiltinMethodsWithSpecialGenericSignature.DefaultValue defaultValue = + BuiltinMethodsWithSpecialGenericSignature.TypeSafeBarrierDescription typeSafeBarrierDescription = BuiltinMethodsWithSpecialGenericSignature.getDefaultValueForOverriddenBuiltinFunction(descriptor); - if (defaultValue == null) return; + if (typeSafeBarrierDescription == null) return; + + FunctionDescriptor overriddenBuiltin = + BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(descriptor); + + assert overriddenBuiltin != null : "Overridden built-in method should not be null for " + descriptor; Label defaultBranch = new Label(); for (int i = 0; i < descriptor.getValueParameters().size(); i++) { + if (!typeSafeBarrierDescription.checkParameter(i)) continue; boolean isCheckForAny = delegateParameterTypes == null || OBJECT_TYPE.equals(delegateParameterTypes[i]); KotlinType kotlinType = descriptor.getValueParameters().get(i).getType(); @@ -967,7 +973,13 @@ public class FunctionCodegen { iv.goTo(afterDefaultBranch); iv.visitLabel(defaultBranch); - StackValue.constant(defaultValue.getValue(), returnType).put(returnType, iv); + + if (typeSafeBarrierDescription.equals(BuiltinMethodsWithSpecialGenericSignature.TypeSafeBarrierDescription.MAP_GET_OR_DEFAULT)) { + iv.load(2, returnType); + } + else { + StackValue.constant(typeSafeBarrierDescription.getDefaultValue(), returnType).put(returnType, iv); + } iv.areturn(returnType); iv.visitLabel(afterDefaultBranch); diff --git a/compiler/testData/builtin-classes/default/kotlin-collections.txt b/compiler/testData/builtin-classes/default/kotlin-collections.txt index bee2ec9b57d..471113614a6 100644 --- a/compiler/testData/builtin-classes/default/kotlin-collections.txt +++ b/compiler/testData/builtin-classes/default/kotlin-collections.txt @@ -103,6 +103,7 @@ public interface Map { public abstract fun containsKey(/*0*/ key: K): kotlin.Boolean public abstract fun containsValue(/*0*/ value: @kotlin.UnsafeVariance() V): kotlin.Boolean public abstract operator fun get(/*0*/ key: K): V? + @kotlin.internal.PlatformDependent() public open fun getOrDefault(/*0*/ key: K, /*1*/ defaultValue: @kotlin.UnsafeVariance() V): V public abstract fun isEmpty(): kotlin.Boolean public interface Entry { @@ -188,6 +189,7 @@ public interface MutableMap : kotlin.collections.Map { public abstract override /*1*/ /*fake_override*/ fun containsKey(/*0*/ key: K): kotlin.Boolean public abstract override /*1*/ /*fake_override*/ fun containsValue(/*0*/ value: V): kotlin.Boolean public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ key: K): V? + @kotlin.internal.PlatformDependent() public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ key: K, /*1*/ defaultValue: V): V public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean public abstract fun put(/*0*/ key: K, /*1*/ value: V): V? public abstract fun putAll(/*0*/ from: kotlin.collections.Map): kotlin.Unit diff --git a/compiler/testData/builtin-classes/java8/kotlin-collections.txt b/compiler/testData/builtin-classes/java8/kotlin-collections.txt index 00be14dcffc..576c3bd498c 100644 --- a/compiler/testData/builtin-classes/java8/kotlin-collections.txt +++ b/compiler/testData/builtin-classes/java8/kotlin-collections.txt @@ -123,7 +123,7 @@ public interface Map { public abstract fun containsValue(/*0*/ value: @kotlin.UnsafeVariance() V): kotlin.Boolean public open fun forEach(/*0*/ p0: java.util.function.BiConsumer!): kotlin.Unit public abstract operator fun get(/*0*/ key: K): V? - public open fun getOrDefault(/*0*/ p0: kotlin.Any!, /*1*/ p1: @kotlin.UnsafeVariance() V!): V! + @kotlin.internal.PlatformDependent() public open fun getOrDefault(/*0*/ key: K, /*1*/ defaultValue: @kotlin.UnsafeVariance() V): V public abstract fun isEmpty(): kotlin.Boolean public interface Entry { @@ -229,7 +229,7 @@ public interface MutableMap : kotlin.collections.Map { public abstract override /*1*/ /*fake_override*/ fun containsValue(/*0*/ value: V): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun forEach(/*0*/ p0: java.util.function.BiConsumer!): kotlin.Unit public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ key: K): V? - public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ p0: kotlin.Any!, /*1*/ p1: V!): V! + @kotlin.internal.PlatformDependent() public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ key: K, /*1*/ defaultValue: V): V public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean public open fun merge(/*0*/ p0: K!, /*1*/ p1: V!, /*2*/ p2: java.util.function.BiFunction!): V! public abstract fun put(/*0*/ key: K, /*1*/ value: V): V? diff --git a/compiler/testData/codegen/java8/box/mapGetOrDefault/noTypeSafeBridge.kt b/compiler/testData/codegen/java8/box/mapGetOrDefault/noTypeSafeBridge.kt new file mode 100644 index 00000000000..ac44d60cbf2 --- /dev/null +++ b/compiler/testData/codegen/java8/box/mapGetOrDefault/noTypeSafeBridge.kt @@ -0,0 +1,66 @@ +// FULL_JDK +// WITH_RUNTIME + +class A : MutableMap { + override val entries: MutableSet> + get() = throw UnsupportedOperationException() + override val keys: MutableSet + get() = throw UnsupportedOperationException() + override val values: MutableCollection + get() = throw UnsupportedOperationException() + + override fun clear() { + throw UnsupportedOperationException() + } + + override fun put(key: Any, value: Any?): Any? { + throw UnsupportedOperationException() + } + + override fun putAll(from: Map) { + throw UnsupportedOperationException() + } + + override fun remove(key: Any): Any? { + throw UnsupportedOperationException() + } + + override val size: Int + get() = throw UnsupportedOperationException() + + override fun containsKey(key: Any): Boolean { + throw UnsupportedOperationException() + } + + override fun containsValue(value: Any?): Boolean { + throw UnsupportedOperationException() + } + + override fun get(key: Any): Any? { + throw UnsupportedOperationException() + } + + override fun isEmpty(): Boolean { + throw UnsupportedOperationException() + } + + override fun getOrDefault(key: Any, defaultValue: Any?): Any? { + if (key == "abc") return "cde" + return defaultValue + } +} + +fun box(): String { + val a = A() + if (a.getOrDefault("abc", "xyz") != "cde") return "fail 1" + if (a.getOrDefault("56", "123") != "123") return "fail 2" + + val mm = a as MutableMap + if (mm.getOrDefault("abc", "xyz") != "cde") return "fail 3" + if (mm.getOrDefault("56", 123) != 123) return "fail 4" + if (mm.getOrDefault(1, "456") != "456") return "fail 5" + if (mm.getOrDefault(null, "qwe") != "qwe") return "fail 6" + if (mm.getOrDefault("abc", null) != "cde") return "fail 7" + + return "OK" +} diff --git a/compiler/testData/codegen/java8/box/mapGetOrDefault/typeSafeBridge.kt b/compiler/testData/codegen/java8/box/mapGetOrDefault/typeSafeBridge.kt new file mode 100644 index 00000000000..f3b520793d2 --- /dev/null +++ b/compiler/testData/codegen/java8/box/mapGetOrDefault/typeSafeBridge.kt @@ -0,0 +1,74 @@ +// FULL_JDK +// WITH_RUNTIME + +class A : MutableMap { + override val entries: MutableSet> + get() = throw UnsupportedOperationException() + override val keys: MutableSet + get() = throw UnsupportedOperationException() + override val values: MutableCollection + get() = throw UnsupportedOperationException() + + override fun clear() { + throw UnsupportedOperationException() + } + + override fun put(key: String, value: String): String? { + throw UnsupportedOperationException() + } + + override fun putAll(from: Map) { + throw UnsupportedOperationException() + } + + override fun remove(key: String): String? { + throw UnsupportedOperationException() + } + + override val size: Int + get() = throw UnsupportedOperationException() + + override fun containsKey(key: String): Boolean { + throw UnsupportedOperationException() + } + + override fun containsValue(value: String): Boolean { + throw UnsupportedOperationException() + } + + override fun get(key: String): String? { + throw UnsupportedOperationException() + } + + override fun isEmpty(): Boolean { + throw UnsupportedOperationException() + } + + override fun getOrDefault(key: String, defaultValue: String): String { + if (key == "abc") return "cde" + return defaultValue + } +} + +fun box(): String { + val a = A() + if (a.getOrDefault("abc", "xyz") != "cde") return "fail 1" + if (a.getOrDefault("56", "123") != "123") return "fail 2" + + val mm = a as MutableMap + if (mm.getOrDefault("abc", "xyz") != "cde") return "fail 3" + if (mm.getOrDefault("56", "123") != "123") return "fail 4" + if (mm.getOrDefault(1, "456") != "456") return "fail 5" + if (mm.getOrDefault(null, "qwe") != "qwe") return "fail 6" + + try { + // This is a known problem, there's no way to implement type-safe bridge/barrier properly: + // 'override fun getOrDefault(key: String, defaultValue: String): String' expects two strings, + // and returning defaultValue if Int was received seems incorrect here + mm.getOrDefault("abc", 123) + return "fail 7" + } catch (e: java.lang.ClassCastException) { + } + + return "OK" +} diff --git a/compiler/testData/codegen/java8/box/mapGetOrDefault/typeSafeBridgeNotNullAny.kt b/compiler/testData/codegen/java8/box/mapGetOrDefault/typeSafeBridgeNotNullAny.kt new file mode 100644 index 00000000000..790eeaa6754 --- /dev/null +++ b/compiler/testData/codegen/java8/box/mapGetOrDefault/typeSafeBridgeNotNullAny.kt @@ -0,0 +1,80 @@ +// FULL_JDK +// WITH_RUNTIME + +class A : MutableMap { + override val entries: MutableSet> + get() = throw UnsupportedOperationException() + override val keys: MutableSet + get() = throw UnsupportedOperationException() + override val values: MutableCollection + get() = throw UnsupportedOperationException() + + override fun clear() { + throw UnsupportedOperationException() + } + + override fun put(key: Any, value: Any): Any? { + throw UnsupportedOperationException() + } + + override fun putAll(from: Map) { + throw UnsupportedOperationException() + } + + override fun remove(key: Any): Any? { + throw UnsupportedOperationException() + } + + override val size: Int + get() = throw UnsupportedOperationException() + + override fun containsKey(key: Any): Boolean { + throw UnsupportedOperationException() + } + + override fun containsValue(value: Any): Boolean { + throw UnsupportedOperationException() + } + + override fun get(key: Any): Any? { + throw UnsupportedOperationException() + } + + override fun isEmpty(): Boolean { + throw UnsupportedOperationException() + } + + override fun getOrDefault(key: Any, defaultValue: Any): Any { + // this condition can not be true because of checkParameterIsNotNull checks in the begin of every method, but it's left here + // to emphasize that we expect these parameters are not null + if (key == null || defaultValue == null) { + throw IllegalArgumentException("fail") + } + if (key == "abc") return "cde" + return defaultValue + } +} + +fun box(): String { + val a = A() + if (a.getOrDefault("abc", "xyz") != "cde") return "fail 1" + if (a.getOrDefault("56", "123") != "123") return "fail 2" + + val mm = a as MutableMap + if (mm.getOrDefault("abc", "xyz") != "cde") return "fail 3" + if (mm.getOrDefault("56", 123) != 123) return "fail 4" + if (mm.getOrDefault(1, "456") != "456") return "fail 5" + if (mm.getOrDefault(null, "qwe") != "qwe") return "fail 6" + + try { + // This is a known problem, there's no way to implement type-safe bridge/barrier properly: + // 'override fun getOrDefault(key: Any, defaultValue: Any): Any' expects two not-nullable values, + // and returning defaultValue if null was received seems incorrect here + mm.getOrDefault("abc", null) + return "fail 7" + } catch (e: java.lang.IllegalArgumentException) { + // Parameter specified as non-null is null + } + + return "OK" +} diff --git a/compiler/testData/diagnostics/testsWithJava8/targetedBuiltIns/getOrDefault.kt b/compiler/testData/diagnostics/testsWithJava8/targetedBuiltIns/getOrDefault.kt new file mode 100644 index 00000000000..5497af5215c --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava8/targetedBuiltIns/getOrDefault.kt @@ -0,0 +1,18 @@ +abstract class A : Map + +fun foo(x: Map, a: A, b: java.util.HashMap) { + x.getOrDefault(1, "") + x.getOrDefault("", "") + x.getOrDefault(1, 2) + x.getOrDefault("", 2) + + a.getOrDefault(1, "") + a.getOrDefault("", "") + a.getOrDefault(1, 2) + a.getOrDefault("", 2) + + b.getOrDefault(1, "") + b.getOrDefault("", "") + b.getOrDefault(1, 2) + b.getOrDefault("", 2) +} diff --git a/compiler/testData/diagnostics/testsWithJava8/targetedBuiltIns/getOrDefault.txt b/compiler/testData/diagnostics/testsWithJava8/targetedBuiltIns/getOrDefault.txt new file mode 100644 index 00000000000..912beb5211c --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava8/targetedBuiltIns/getOrDefault.txt @@ -0,0 +1,20 @@ +package + +public fun foo(/*0*/ x: kotlin.collections.Map, /*1*/ a: A, /*2*/ b: java.util.HashMap): kotlin.Unit + +public abstract class A : kotlin.collections.Map { + public constructor A() + public abstract override /*1*/ /*fake_override*/ val entries: kotlin.collections.Set> + public abstract override /*1*/ /*fake_override*/ val keys: kotlin.collections.Set + public abstract override /*1*/ /*fake_override*/ val size: kotlin.Int + public abstract override /*1*/ /*fake_override*/ val values: kotlin.collections.Collection + public abstract override /*1*/ /*fake_override*/ fun containsKey(/*0*/ key: kotlin.Int): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun containsValue(/*0*/ value: kotlin.String): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun forEach(/*0*/ p0: java.util.function.BiConsumer!): kotlin.Unit + public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ key: kotlin.Int): kotlin.String? + @kotlin.internal.PlatformDependent() public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ key: kotlin.Int, /*1*/ defaultValue: kotlin.String): kotlin.String + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/testsWithJava8/targetedBuiltIns/mutableMapRemove.txt b/compiler/testData/diagnostics/testsWithJava8/targetedBuiltIns/mutableMapRemove.txt index 89e62ef3387..fb646b86bb9 100644 --- a/compiler/testData/diagnostics/testsWithJava8/targetedBuiltIns/mutableMapRemove.txt +++ b/compiler/testData/diagnostics/testsWithJava8/targetedBuiltIns/mutableMapRemove.txt @@ -20,7 +20,7 @@ public final class KotlinMap1 : java.util.AbstractMap { public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun forEach(/*0*/ p0: java.util.function.BiConsumer!): kotlin.Unit public open override /*1*/ /*fake_override*/ fun get(/*0*/ key: K!): V? - public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ p0: kotlin.Any!, /*1*/ p1: V!): V! + @kotlin.internal.PlatformDependent() public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ key: K!, /*1*/ defaultValue: V!): V! public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun merge(/*0*/ p0: K!, /*1*/ p1: V!, /*2*/ p2: java.util.function.BiFunction!): V! @@ -53,7 +53,7 @@ public final class KotlinMap2 : java.util.AbstractMap public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun forEach(/*0*/ p0: java.util.function.BiConsumer!): kotlin.Unit public open override /*1*/ /*fake_override*/ fun get(/*0*/ key: kotlin.String!): kotlin.Int? - public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ p0: kotlin.Any!, /*1*/ p1: kotlin.Int!): kotlin.Int! + @kotlin.internal.PlatformDependent() public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ key: kotlin.String!, /*1*/ defaultValue: kotlin.Int!): kotlin.Int! public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun merge(/*0*/ p0: kotlin.String!, /*1*/ p1: kotlin.Int!, /*2*/ p2: java.util.function.BiFunction!): kotlin.Int! diff --git a/compiler/testData/loadJava8/compiledJava/MapRemove.txt b/compiler/testData/loadJava8/compiledJava/MapRemove.txt index 175b41d8d79..50f367b2af5 100644 --- a/compiler/testData/loadJava8/compiledJava/MapRemove.txt +++ b/compiler/testData/loadJava8/compiledJava/MapRemove.txt @@ -17,7 +17,7 @@ public open class MapRemove { public abstract override /*1*/ /*fake_override*/ fun containsValue(/*0*/ value: V!): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun forEach(/*0*/ p0: java.util.function.BiConsumer!): kotlin.Unit public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ key: K!): V? - public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ p0: kotlin.Any!, /*1*/ p1: V!): V! + @kotlin.internal.PlatformDependent() public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ key: K!, /*1*/ defaultValue: V!): V! public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun merge(/*0*/ p0: K!, /*1*/ p1: V!, /*2*/ p2: java.util.function.BiFunction!): V! public abstract override /*1*/ /*fake_override*/ fun put(/*0*/ key: K!, /*1*/ value: V!): V? @@ -44,7 +44,7 @@ public open class MapRemove { public abstract override /*1*/ /*fake_override*/ fun containsValue(/*0*/ value: kotlin.Int!): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun forEach(/*0*/ p0: java.util.function.BiConsumer!): kotlin.Unit public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ key: kotlin.String!): kotlin.Int? - public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ p0: kotlin.Any!, /*1*/ p1: kotlin.Int!): kotlin.Int! + @kotlin.internal.PlatformDependent() public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ key: kotlin.String!, /*1*/ defaultValue: kotlin.Int!): kotlin.Int! public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun merge(/*0*/ p0: kotlin.String!, /*1*/ p1: kotlin.Int!, /*2*/ p2: java.util.function.BiFunction!): kotlin.Int! public abstract override /*1*/ /*fake_override*/ fun put(/*0*/ key: kotlin.String!, /*1*/ value: kotlin.Int!): kotlin.Int? @@ -71,7 +71,7 @@ public open class MapRemove { public abstract override /*1*/ /*fake_override*/ fun containsValue(/*0*/ value: kotlin.Int!): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun forEach(/*0*/ p0: java.util.function.BiConsumer!): kotlin.Unit public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ key: kotlin.String!): kotlin.Int? - public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ p0: kotlin.Any!, /*1*/ p1: kotlin.Int!): kotlin.Int! + @kotlin.internal.PlatformDependent() public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ key: kotlin.String!, /*1*/ defaultValue: kotlin.Int!): kotlin.Int! public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun merge(/*0*/ p0: kotlin.String!, /*1*/ p1: kotlin.Int!, /*2*/ p2: java.util.function.BiFunction!): kotlin.Int! public abstract override /*1*/ /*fake_override*/ fun put(/*0*/ key: kotlin.String!, /*1*/ value: kotlin.Int!): kotlin.Int? diff --git a/compiler/testData/loadJava8/sourceJava/MapRemove.txt b/compiler/testData/loadJava8/sourceJava/MapRemove.txt index 06c4c3334e7..ea998c934d1 100644 --- a/compiler/testData/loadJava8/sourceJava/MapRemove.txt +++ b/compiler/testData/loadJava8/sourceJava/MapRemove.txt @@ -17,7 +17,7 @@ public open class MapRemove { public abstract override /*1*/ /*fake_override*/ fun containsValue(/*0*/ value: V!): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun forEach(/*0*/ p0: java.util.function.BiConsumer!): kotlin.Unit public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ key: K!): V? - public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ p0: kotlin.Any!, /*1*/ p1: V!): V! + @kotlin.internal.PlatformDependent() public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ key: K!, /*1*/ defaultValue: V!): V! public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun merge(/*0*/ p0: K!, /*1*/ p1: V!, /*2*/ p2: java.util.function.BiFunction!): V! public abstract override /*1*/ /*fake_override*/ fun put(/*0*/ key: K!, /*1*/ value: V!): V? @@ -44,7 +44,7 @@ public open class MapRemove { public abstract override /*1*/ /*fake_override*/ fun containsValue(/*0*/ value: kotlin.Int!): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun forEach(/*0*/ p0: java.util.function.BiConsumer!): kotlin.Unit public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ key: kotlin.String!): kotlin.Int? - public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ p0: kotlin.Any!, /*1*/ p1: kotlin.Int!): kotlin.Int! + @kotlin.internal.PlatformDependent() public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ key: kotlin.String!, /*1*/ defaultValue: kotlin.Int!): kotlin.Int! public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun merge(/*0*/ p0: kotlin.String!, /*1*/ p1: kotlin.Int!, /*2*/ p2: java.util.function.BiFunction!): kotlin.Int! public abstract override /*1*/ /*fake_override*/ fun put(/*0*/ key: kotlin.String!, /*1*/ value: kotlin.Int!): kotlin.Int? @@ -71,7 +71,7 @@ public open class MapRemove { public abstract override /*1*/ /*fake_override*/ fun containsValue(/*0*/ value: kotlin.Int!): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun forEach(/*0*/ p0: java.util.function.BiConsumer!): kotlin.Unit public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ key: kotlin.String!): kotlin.Int? - public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ p0: kotlin.Any!, /*1*/ p1: kotlin.Int!): kotlin.Int! + @kotlin.internal.PlatformDependent() public open override /*1*/ /*fake_override*/ fun getOrDefault(/*0*/ key: kotlin.String!, /*1*/ defaultValue: kotlin.Int!): kotlin.Int! public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun merge(/*0*/ p0: kotlin.String!, /*1*/ p1: kotlin.Int!, /*2*/ p2: java.util.function.BiFunction!): kotlin.Int! public abstract override /*1*/ /*fake_override*/ fun put(/*0*/ key: kotlin.String!, /*1*/ value: kotlin.Int!): kotlin.Int? diff --git a/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJava8TestGenerated.java b/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJava8TestGenerated.java index 7353ae2ed95..64f1664d095 100644 --- a/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJava8TestGenerated.java +++ b/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJava8TestGenerated.java @@ -139,6 +139,12 @@ public class DiagnosticsWithJava8TestGenerated extends AbstractDiagnosticsWithFu doTest(fileName); } + @TestMetadata("getOrDefault.kt") + public void testGetOrDefault() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJava8/targetedBuiltIns/getOrDefault.kt"); + doTest(fileName); + } + @TestMetadata("mutableMapRemove.kt") public void testMutableMapRemove() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJava8/targetedBuiltIns/mutableMapRemove.kt"); diff --git a/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java b/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java index 40c88388395..c6ddbfdd80d 100644 --- a/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java +++ b/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java @@ -179,6 +179,33 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg } } + @TestMetadata("compiler/testData/codegen/java8/box/mapGetOrDefault") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class MapGetOrDefault extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInMapGetOrDefault() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/java8/box/mapGetOrDefault"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("noTypeSafeBridge.kt") + public void testNoTypeSafeBridge() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/mapGetOrDefault/noTypeSafeBridge.kt"); + doTest(fileName); + } + + @TestMetadata("typeSafeBridge.kt") + public void testTypeSafeBridge() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/mapGetOrDefault/typeSafeBridge.kt"); + doTest(fileName); + } + + @TestMetadata("typeSafeBridgeNotNullAny.kt") + public void testTypeSafeBridgeNotNullAny() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/mapGetOrDefault/typeSafeBridgeNotNullAny.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/java8/box/mapRemove") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/builtins/native/kotlin/Collections.kt b/core/builtins/native/kotlin/Collections.kt index 8054aca2686..4f39294b1d8 100644 --- a/core/builtins/native/kotlin/Collections.kt +++ b/core/builtins/native/kotlin/Collections.kt @@ -298,6 +298,17 @@ public interface Map { */ public operator fun get(key: K): V? + /** + * Returns the value corresponding to the given [key], or [defaultValue] if such a key is not present in the map. + * + * @since JDK 1.8 + */ + @PlatformDependent + public fun getOrDefault(key: K, defaultValue: @UnsafeVariance V): V { + // See default implementation in JDK sources + return null as V + } + // Views /** * Returns a [Set] of all keys in this map. 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 fc7dd8d4ed8..9480b5ffeb3 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 @@ -97,34 +97,45 @@ object BuiltinMethodsWithSpecialGenericSignature { private val ERASED_COLLECTION_PARAMETER_SIGNATURES = ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES.map { it.signature } - enum class DefaultValue(val value: Any?) { - NULL(null), INDEX(-1), FALSE(false) + enum class TypeSafeBarrierDescription(val defaultValue: Any?) { + NULL(null), INDEX(-1), FALSE(false), + + MAP_GET_OR_DEFAULT(null) { + override fun checkParameter(index: Int) = index != 1 + }; + + open fun checkParameter(index: Int) = true } private val GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP = signatures { mapOf( javaUtil("Collection") - .method("contains", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to DefaultValue.FALSE, + .method("contains", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE, javaUtil("Collection") - .method("remove", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to DefaultValue.FALSE, + .method("remove", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE, + javaUtil("Map") - .method("containsKey", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to DefaultValue.FALSE, + .method("containsKey", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE, javaUtil("Map") - .method("containsValue", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to DefaultValue.FALSE, + .method("containsValue", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE, javaUtil("Map") .method("remove", "Ljava/lang/Object;Ljava/lang/Object;", - JvmPrimitiveType.BOOLEAN.desc) to DefaultValue.FALSE, + JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE, javaUtil("Map") - .method("get", "Ljava/lang/Object;", "Ljava/lang/Object;") to DefaultValue.NULL, + .method("getOrDefault", "Ljava/lang/Object;Ljava/lang/Object;", + "Ljava/lang/Object;") to TypeSafeBarrierDescription.MAP_GET_OR_DEFAULT, + javaUtil("Map") - .method("remove", "Ljava/lang/Object;", "Ljava/lang/Object;") to DefaultValue.NULL, + .method("get", "Ljava/lang/Object;", "Ljava/lang/Object;") to TypeSafeBarrierDescription.NULL, + javaUtil("Map") + .method("remove", "Ljava/lang/Object;", "Ljava/lang/Object;") to TypeSafeBarrierDescription.NULL, javaUtil("List") - .method("indexOf", "Ljava/lang/Object;", JvmPrimitiveType.INT.desc) to DefaultValue.INDEX, + .method("indexOf", "Ljava/lang/Object;", JvmPrimitiveType.INT.desc) to TypeSafeBarrierDescription.INDEX, javaUtil("List") - .method("lastIndexOf", "Ljava/lang/Object;", JvmPrimitiveType.INT.desc) to DefaultValue.INDEX + .method("lastIndexOf", "Ljava/lang/Object;", JvmPrimitiveType.INT.desc) to TypeSafeBarrierDescription.INDEX ) } @@ -150,7 +161,7 @@ object BuiltinMethodsWithSpecialGenericSignature { } @JvmStatic - fun getDefaultValueForOverriddenBuiltinFunction(functionDescriptor: FunctionDescriptor): DefaultValue? { + fun getDefaultValueForOverriddenBuiltinFunction(functionDescriptor: FunctionDescriptor): TypeSafeBarrierDescription? { if (functionDescriptor.name !in ERASED_VALUE_PARAMETERS_SHORT_NAMES) return null return functionDescriptor.firstOverridden { it.computeJvmSignature() in SIGNATURE_TO_DEFAULT_VALUES_MAP.keys @@ -182,7 +193,7 @@ object BuiltinMethodsWithSpecialGenericSignature { val defaultValue = SIGNATURE_TO_DEFAULT_VALUES_MAP[builtinSignature]!! - return if (defaultValue == DefaultValue.NULL) + return if (defaultValue == TypeSafeBarrierDescription.NULL) // return type is some generic type as 'Map.get' SpecialSignatureInfo.OBJECT_PARAMETER_GENERIC else