Skip to content

Commit 55d1fdf

Browse files
dnpetrovSpace
authored and
Space
committed
JVM_IR prevent clash of unsubstituted special bridges
1 parent 1e98748 commit 55d1fdf

File tree

12 files changed

+230
-13
lines changed

12 files changed

+230
-13
lines changed

compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt

+24-13
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
219219
if (specialBridge != null) {
220220
// If the current function overrides a special bridge then it's possible that we already generated a final
221221
// bridge methods in a superclass.
222-
blacklist += irFunction.overriddenFinalSpecialBridgeSignatures()
222+
blacklist += irFunction.allOverridden().flatMapTo(arrayListOf()) { it.getSpecialBridgeSignatures() }
223223

224224
fun getSpecialBridgeTargetAddingExtraBridges(): IrSimpleFunction {
225225
// We only generate a special bridge method if it does not clash with a final method in a superclass or the current method
@@ -279,6 +279,7 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
279279
// (where a Kotlin class implements a read-only collection interface and extends a Java collection class).
280280
val unsubstitutedSpecialBridge = specialBridge.unsubstitutedSpecialBridge
281281
if (unsubstitutedSpecialBridge != null &&
282+
unsubstitutedSpecialBridge.signature !in blacklist &&
282283
irClass.functions.none { it.isClashingWithPotentialBridge(irFunction.name, unsubstitutedSpecialBridge.signature) }
283284
) {
284285
blacklist += unsubstitutedSpecialBridge.signature
@@ -364,19 +365,29 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
364365
private val IrSimpleFunction.specialBridgeOrNull: SpecialBridge?
365366
get() = context.bridgeLoweringCache.computeSpecialBridge(this)
366367

367-
private fun IrSimpleFunction.overriddenFinalSpecialBridgeSignatures(): List<Method> =
368-
allOverridden().mapNotNullTo(mutableListOf()) {
369-
// Ignore special bridges in interfaces or Java classes. While we never generate special bridges in Java
370-
// classes, we may generate special bridges in interfaces for methods annotated with @JvmDefault.
371-
// However, these bridges are not final and are thus safe to override.
372-
//
373-
// This matches the behavior of the JVM backend, but it's probably a bad idea since this is an
374-
// opportunity for a Java and Kotlin implementation of the same interface to go out of sync.
375-
if (it.parentAsClass.isInterface || it.isFromJava())
376-
null
377-
else
378-
it.specialBridgeOrNull?.signature?.takeIf { bridgeSignature -> bridgeSignature != it.jvmMethod }
368+
private fun IrSimpleFunction.getSpecialBridgeSignatures(): List<Method> {
369+
// Ignore special bridges in interfaces or Java classes. While we never generate special bridges in Java
370+
// classes, we may generate special bridges in interfaces for methods annotated with @JvmDefault.
371+
// However, these bridges are not final and are thus safe to override.
372+
// This matches the behavior of the JVM backend, but it's probably a bad idea since this is an
373+
// opportunity for a Java and Kotlin implementation of the same interface to go out of sync.
374+
375+
if (this.parentAsClass.isInterface || this.isFromJava())
376+
return emptyList()
377+
val specialBridge = this.specialBridgeOrNull
378+
?: return emptyList()
379+
380+
val result = SmartList<Method>()
381+
val jvmMethod = this.jvmMethod
382+
if (specialBridge.signature != jvmMethod) {
383+
result.add(specialBridge.signature)
379384
}
385+
val unsubstitutedSpecialBridge = specialBridge.unsubstitutedSpecialBridge
386+
if (unsubstitutedSpecialBridge != null && unsubstitutedSpecialBridge.signature != jvmMethod) {
387+
result.add(unsubstitutedSpecialBridge.signature)
388+
}
389+
return result
390+
}
380391

381392
// List of special bridge methods which were not implemented in Kotlin superclasses.
382393
private fun IrSimpleFunction.overriddenSpecialBridges(): List<SpecialBridge> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// TARGET_BACKEND: JVM
2+
// WITH_STDLIB
3+
// FULL_JDK
4+
5+
abstract class AMap1<K1, V1>(private val m: Map<K1, V1>) : Map<K1, V1> by m
6+
7+
interface Value2
8+
9+
abstract class AMap2<V2 : Value2>(m: Map<String, V2>) : AMap1<String, V2>(m)
10+
11+
class C(val value: String): Value2
12+
13+
class CMap(m: Map<String, C>) : AMap2<C>(m)
14+
15+
fun box(): String {
16+
val cmap = CMap(mapOf("1" to C("OK")))
17+
return cmap["1"]!!.value
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// TARGET_BACKEND: JVM
2+
// WITH_STDLIB
3+
// FULL_JDK
4+
5+
abstract class AMap1<K1, V1>(private val m: Map<K1, V1>) : Map<K1, V1> by m
6+
7+
interface Value2
8+
9+
abstract class AMap2<V2 : Value2>(m: Map<String, V2>) : AMap1<String, V2>(m)
10+
11+
class C(val value: String): Value2
12+
13+
class Map3(m: Map<String, C>) : AMap2<C>(m)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
@kotlin.Metadata
2+
public abstract class AMap1 {
3+
// source: 'specialBridgeForGet.kt'
4+
private final field m: java.util.Map
5+
public method <init>(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
6+
public method clear(): void
7+
public method compute(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object
8+
public method computeIfAbsent(p0: java.lang.Object, p1: java.util.function.Function): java.lang.Object
9+
public method computeIfPresent(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object
10+
public method containsKey(p0: java.lang.Object): boolean
11+
public method containsValue(p0: java.lang.Object): boolean
12+
public bridge final method entrySet(): java.util.Set
13+
public @org.jetbrains.annotations.Nullable method get(p0: java.lang.Object): java.lang.Object
14+
public @org.jetbrains.annotations.NotNull method getEntries(): java.util.Set
15+
public @org.jetbrains.annotations.NotNull method getKeys(): java.util.Set
16+
public method getSize(): int
17+
public @org.jetbrains.annotations.NotNull method getValues(): java.util.Collection
18+
public method isEmpty(): boolean
19+
public bridge final method keySet(): java.util.Set
20+
public method merge(p0: java.lang.Object, p1: java.lang.Object, p2: java.util.function.BiFunction): java.lang.Object
21+
public method put(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
22+
public method putAll(p0: java.util.Map): void
23+
public method putIfAbsent(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
24+
public method remove(p0: java.lang.Object): java.lang.Object
25+
public method remove(p0: java.lang.Object, p1: java.lang.Object): boolean
26+
public method replace(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
27+
public method replace(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object): boolean
28+
public method replaceAll(p0: java.util.function.BiFunction): void
29+
public bridge final method size(): int
30+
public bridge final method values(): java.util.Collection
31+
}
32+
33+
@kotlin.Metadata
34+
public abstract class AMap2 {
35+
// source: 'specialBridgeForGet.kt'
36+
public method <init>(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
37+
public bridge final method containsKey(p0: java.lang.Object): boolean
38+
public bridge method containsKey(p0: java.lang.String): boolean
39+
public bridge method containsValue(p0: Value2): boolean
40+
public bridge final method containsValue(p0: java.lang.Object): boolean
41+
public bridge final method get(p0: java.lang.Object): java.lang.Object
42+
public bridge method get(p0: java.lang.String): Value2
43+
public bridge final method getOrDefault(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
44+
public bridge method getOrDefault(p0: java.lang.String, p1: Value2): Value2
45+
}
46+
47+
@kotlin.Metadata
48+
public final class C {
49+
// source: 'specialBridgeForGet.kt'
50+
private final @org.jetbrains.annotations.NotNull field value: java.lang.String
51+
public method <init>(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
52+
public final @org.jetbrains.annotations.NotNull method getValue(): java.lang.String
53+
}
54+
55+
@kotlin.Metadata
56+
public final class Map3 {
57+
// source: 'specialBridgeForGet.kt'
58+
public method <init>(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
59+
}
60+
61+
@kotlin.Metadata
62+
public interface Value2 {
63+
// source: 'specialBridgeForGet.kt'
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
@kotlin.Metadata
2+
public abstract class AMap1 {
3+
// source: 'specialBridgeForGet.kt'
4+
private final @org.jetbrains.annotations.NotNull field m: java.util.Map
5+
public method <init>(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
6+
public method clear(): void
7+
public method compute(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object
8+
public method computeIfAbsent(p0: java.lang.Object, p1: java.util.function.Function): java.lang.Object
9+
public method computeIfPresent(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object
10+
public method containsKey(p0: java.lang.Object): boolean
11+
public method containsValue(p0: java.lang.Object): boolean
12+
public bridge final method entrySet(): java.util.Set
13+
public @org.jetbrains.annotations.Nullable method get(p0: java.lang.Object): java.lang.Object
14+
public @org.jetbrains.annotations.NotNull method getEntries(): java.util.Set
15+
public @org.jetbrains.annotations.NotNull method getKeys(): java.util.Set
16+
public method getSize(): int
17+
public @org.jetbrains.annotations.NotNull method getValues(): java.util.Collection
18+
public method isEmpty(): boolean
19+
public bridge final method keySet(): java.util.Set
20+
public method merge(p0: java.lang.Object, p1: java.lang.Object, p2: java.util.function.BiFunction): java.lang.Object
21+
public method put(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
22+
public method putAll(p0: java.util.Map): void
23+
public method putIfAbsent(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
24+
public method remove(p0: java.lang.Object): java.lang.Object
25+
public method remove(p0: java.lang.Object, p1: java.lang.Object): boolean
26+
public method replace(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
27+
public method replace(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object): boolean
28+
public method replaceAll(p0: java.util.function.BiFunction): void
29+
public bridge final method size(): int
30+
public bridge final method values(): java.util.Collection
31+
}
32+
33+
@kotlin.Metadata
34+
public abstract class AMap2 {
35+
// source: 'specialBridgeForGet.kt'
36+
public method <init>(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
37+
public bridge final method containsKey(p0: java.lang.Object): boolean
38+
public bridge method containsKey(p0: java.lang.String): boolean
39+
public bridge method containsValue(p0: Value2): boolean
40+
public bridge final method containsValue(p0: java.lang.Object): boolean
41+
public bridge final method get(p0: java.lang.Object): Value2
42+
public synthetic bridge final method get(p0: java.lang.Object): java.lang.Object
43+
public bridge method get(p0: java.lang.String): Value2
44+
public bridge final method getOrDefault(p0: java.lang.Object, p1: Value2): Value2
45+
public synthetic bridge final method getOrDefault(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
46+
public bridge method getOrDefault(p0: java.lang.String, p1: Value2): Value2
47+
}
48+
49+
@kotlin.Metadata
50+
public final class C {
51+
// source: 'specialBridgeForGet.kt'
52+
private final @org.jetbrains.annotations.NotNull field value: java.lang.String
53+
public method <init>(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
54+
public final @org.jetbrains.annotations.NotNull method getValue(): java.lang.String
55+
}
56+
57+
@kotlin.Metadata
58+
public final class Map3 {
59+
// source: 'specialBridgeForGet.kt'
60+
public method <init>(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
61+
public bridge final method get(p0: java.lang.Object): C
62+
public bridge method get(p0: java.lang.String): C
63+
public bridge final method getOrDefault(p0: java.lang.Object, p1: C): C
64+
public bridge method getOrDefault(p0: java.lang.String, p1: C): C
65+
}
66+
67+
@kotlin.Metadata
68+
public interface Value2 {
69+
// source: 'specialBridgeForGet.kt'
70+
}

compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeListingTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeListingTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeExtBlackBoxTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)