JVM_IR prevent clash of unsubstituted special bridges

This commit is contained in:
Dmitry Petrov
2021-11-23 10:05:09 +03:00
committed by Space
parent 1e98748e0b
commit 55d1fdfbc2
12 changed files with 230 additions and 13 deletions
@@ -6676,6 +6676,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/collections/removeOverriddenInJava_Map.kt");
}
@Test
@TestMetadata("specialBridgeForGet.kt")
public void testSpecialBridgeForGet() throws Exception {
runTest("compiler/testData/codegen/box/collections/specialBridgeForGet.kt");
}
@Test
@TestMetadata("strList.kt")
public void testStrList() throws Exception {
@@ -219,7 +219,7 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
if (specialBridge != null) {
// If the current function overrides a special bridge then it's possible that we already generated a final
// bridge methods in a superclass.
blacklist += irFunction.overriddenFinalSpecialBridgeSignatures()
blacklist += irFunction.allOverridden().flatMapTo(arrayListOf()) { it.getSpecialBridgeSignatures() }
fun getSpecialBridgeTargetAddingExtraBridges(): IrSimpleFunction {
// 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
// (where a Kotlin class implements a read-only collection interface and extends a Java collection class).
val unsubstitutedSpecialBridge = specialBridge.unsubstitutedSpecialBridge
if (unsubstitutedSpecialBridge != null &&
unsubstitutedSpecialBridge.signature !in blacklist &&
irClass.functions.none { it.isClashingWithPotentialBridge(irFunction.name, unsubstitutedSpecialBridge.signature) }
) {
blacklist += unsubstitutedSpecialBridge.signature
@@ -364,19 +365,29 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
private val IrSimpleFunction.specialBridgeOrNull: SpecialBridge?
get() = context.bridgeLoweringCache.computeSpecialBridge(this)
private fun IrSimpleFunction.overriddenFinalSpecialBridgeSignatures(): List<Method> =
allOverridden().mapNotNullTo(mutableListOf()) {
// Ignore special bridges in interfaces or Java classes. While we never generate special bridges in Java
// classes, we may generate special bridges in interfaces for methods annotated with @JvmDefault.
// However, these bridges are not final and are thus safe to override.
//
// This matches the behavior of the JVM backend, but it's probably a bad idea since this is an
// opportunity for a Java and Kotlin implementation of the same interface to go out of sync.
if (it.parentAsClass.isInterface || it.isFromJava())
null
else
it.specialBridgeOrNull?.signature?.takeIf { bridgeSignature -> bridgeSignature != it.jvmMethod }
private fun IrSimpleFunction.getSpecialBridgeSignatures(): List<Method> {
// Ignore special bridges in interfaces or Java classes. While we never generate special bridges in Java
// classes, we may generate special bridges in interfaces for methods annotated with @JvmDefault.
// However, these bridges are not final and are thus safe to override.
// This matches the behavior of the JVM backend, but it's probably a bad idea since this is an
// opportunity for a Java and Kotlin implementation of the same interface to go out of sync.
if (this.parentAsClass.isInterface || this.isFromJava())
return emptyList()
val specialBridge = this.specialBridgeOrNull
?: return emptyList()
val result = SmartList<Method>()
val jvmMethod = this.jvmMethod
if (specialBridge.signature != jvmMethod) {
result.add(specialBridge.signature)
}
val unsubstitutedSpecialBridge = specialBridge.unsubstitutedSpecialBridge
if (unsubstitutedSpecialBridge != null && unsubstitutedSpecialBridge.signature != jvmMethod) {
result.add(unsubstitutedSpecialBridge.signature)
}
return result
}
// List of special bridge methods which were not implemented in Kotlin superclasses.
private fun IrSimpleFunction.overriddenSpecialBridges(): List<SpecialBridge> {
@@ -0,0 +1,18 @@
// TARGET_BACKEND: JVM
// WITH_STDLIB
// FULL_JDK
abstract class AMap1<K1, V1>(private val m: Map<K1, V1>) : Map<K1, V1> by m
interface Value2
abstract class AMap2<V2 : Value2>(m: Map<String, V2>) : AMap1<String, V2>(m)
class C(val value: String): Value2
class CMap(m: Map<String, C>) : AMap2<C>(m)
fun box(): String {
val cmap = CMap(mapOf("1" to C("OK")))
return cmap["1"]!!.value
}
@@ -0,0 +1,13 @@
// TARGET_BACKEND: JVM
// WITH_STDLIB
// FULL_JDK
abstract class AMap1<K1, V1>(private val m: Map<K1, V1>) : Map<K1, V1> by m
interface Value2
abstract class AMap2<V2 : Value2>(m: Map<String, V2>) : AMap1<String, V2>(m)
class C(val value: String): Value2
class Map3(m: Map<String, C>) : AMap2<C>(m)
@@ -0,0 +1,64 @@
@kotlin.Metadata
public abstract class AMap1 {
// source: 'specialBridgeForGet.kt'
private final field m: java.util.Map
public method <init>(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
public method clear(): void
public method compute(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object
public method computeIfAbsent(p0: java.lang.Object, p1: java.util.function.Function): java.lang.Object
public method computeIfPresent(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object
public method containsKey(p0: java.lang.Object): boolean
public method containsValue(p0: java.lang.Object): boolean
public bridge final method entrySet(): java.util.Set
public @org.jetbrains.annotations.Nullable method get(p0: java.lang.Object): java.lang.Object
public @org.jetbrains.annotations.NotNull method getEntries(): java.util.Set
public @org.jetbrains.annotations.NotNull method getKeys(): java.util.Set
public method getSize(): int
public @org.jetbrains.annotations.NotNull method getValues(): java.util.Collection
public method isEmpty(): boolean
public bridge final method keySet(): java.util.Set
public method merge(p0: java.lang.Object, p1: java.lang.Object, p2: java.util.function.BiFunction): java.lang.Object
public method put(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
public method putAll(p0: java.util.Map): void
public method putIfAbsent(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
public method remove(p0: java.lang.Object): java.lang.Object
public method remove(p0: java.lang.Object, p1: java.lang.Object): boolean
public method replace(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
public method replace(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object): boolean
public method replaceAll(p0: java.util.function.BiFunction): void
public bridge final method size(): int
public bridge final method values(): java.util.Collection
}
@kotlin.Metadata
public abstract class AMap2 {
// source: 'specialBridgeForGet.kt'
public method <init>(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
public bridge final method containsKey(p0: java.lang.Object): boolean
public bridge method containsKey(p0: java.lang.String): boolean
public bridge method containsValue(p0: Value2): boolean
public bridge final method containsValue(p0: java.lang.Object): boolean
public bridge final method get(p0: java.lang.Object): java.lang.Object
public bridge method get(p0: java.lang.String): Value2
public bridge final method getOrDefault(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
public bridge method getOrDefault(p0: java.lang.String, p1: Value2): Value2
}
@kotlin.Metadata
public final class C {
// source: 'specialBridgeForGet.kt'
private final @org.jetbrains.annotations.NotNull field value: java.lang.String
public method <init>(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
public final @org.jetbrains.annotations.NotNull method getValue(): java.lang.String
}
@kotlin.Metadata
public final class Map3 {
// source: 'specialBridgeForGet.kt'
public method <init>(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
}
@kotlin.Metadata
public interface Value2 {
// source: 'specialBridgeForGet.kt'
}
@@ -0,0 +1,70 @@
@kotlin.Metadata
public abstract class AMap1 {
// source: 'specialBridgeForGet.kt'
private final @org.jetbrains.annotations.NotNull field m: java.util.Map
public method <init>(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
public method clear(): void
public method compute(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object
public method computeIfAbsent(p0: java.lang.Object, p1: java.util.function.Function): java.lang.Object
public method computeIfPresent(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object
public method containsKey(p0: java.lang.Object): boolean
public method containsValue(p0: java.lang.Object): boolean
public bridge final method entrySet(): java.util.Set
public @org.jetbrains.annotations.Nullable method get(p0: java.lang.Object): java.lang.Object
public @org.jetbrains.annotations.NotNull method getEntries(): java.util.Set
public @org.jetbrains.annotations.NotNull method getKeys(): java.util.Set
public method getSize(): int
public @org.jetbrains.annotations.NotNull method getValues(): java.util.Collection
public method isEmpty(): boolean
public bridge final method keySet(): java.util.Set
public method merge(p0: java.lang.Object, p1: java.lang.Object, p2: java.util.function.BiFunction): java.lang.Object
public method put(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
public method putAll(p0: java.util.Map): void
public method putIfAbsent(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
public method remove(p0: java.lang.Object): java.lang.Object
public method remove(p0: java.lang.Object, p1: java.lang.Object): boolean
public method replace(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
public method replace(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object): boolean
public method replaceAll(p0: java.util.function.BiFunction): void
public bridge final method size(): int
public bridge final method values(): java.util.Collection
}
@kotlin.Metadata
public abstract class AMap2 {
// source: 'specialBridgeForGet.kt'
public method <init>(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
public bridge final method containsKey(p0: java.lang.Object): boolean
public bridge method containsKey(p0: java.lang.String): boolean
public bridge method containsValue(p0: Value2): boolean
public bridge final method containsValue(p0: java.lang.Object): boolean
public bridge final method get(p0: java.lang.Object): Value2
public synthetic bridge final method get(p0: java.lang.Object): java.lang.Object
public bridge method get(p0: java.lang.String): Value2
public bridge final method getOrDefault(p0: java.lang.Object, p1: Value2): Value2
public synthetic bridge final method getOrDefault(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
public bridge method getOrDefault(p0: java.lang.String, p1: Value2): Value2
}
@kotlin.Metadata
public final class C {
// source: 'specialBridgeForGet.kt'
private final @org.jetbrains.annotations.NotNull field value: java.lang.String
public method <init>(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
public final @org.jetbrains.annotations.NotNull method getValue(): java.lang.String
}
@kotlin.Metadata
public final class Map3 {
// source: 'specialBridgeForGet.kt'
public method <init>(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
public bridge final method get(p0: java.lang.Object): C
public bridge method get(p0: java.lang.String): C
public bridge final method getOrDefault(p0: java.lang.Object, p1: C): C
public bridge method getOrDefault(p0: java.lang.String, p1: C): C
}
@kotlin.Metadata
public interface Value2 {
// source: 'specialBridgeForGet.kt'
}
@@ -6604,6 +6604,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/collections/removeOverriddenInJava_Map.kt");
}
@Test
@TestMetadata("specialBridgeForGet.kt")
public void testSpecialBridgeForGet() throws Exception {
runTest("compiler/testData/codegen/box/collections/specialBridgeForGet.kt");
}
@Test
@TestMetadata("strList.kt")
public void testStrList() throws Exception {
@@ -2296,6 +2296,12 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/removeAtTwoSpecialBridges.kt");
}
@Test
@TestMetadata("specialBridgeForGet.kt")
public void testSpecialBridgeForGet() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/specialBridgeForGet.kt");
}
@Test
@TestMetadata("unsignedArray.kt")
public void testUnsignedArray() throws Exception {
@@ -6676,6 +6676,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/collections/removeOverriddenInJava_Map.kt");
}
@Test
@TestMetadata("specialBridgeForGet.kt")
public void testSpecialBridgeForGet() throws Exception {
runTest("compiler/testData/codegen/box/collections/specialBridgeForGet.kt");
}
@Test
@TestMetadata("strList.kt")
public void testStrList() throws Exception {
@@ -2344,6 +2344,12 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/removeAtTwoSpecialBridges.kt");
}
@Test
@TestMetadata("specialBridgeForGet.kt")
public void testSpecialBridgeForGet() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/specialBridgeForGet.kt");
}
@Test
@TestMetadata("unsignedArray.kt")
public void testUnsignedArray() throws Exception {
@@ -5755,6 +5755,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/collections/removeOverriddenInJava_Map.kt");
}
@TestMetadata("specialBridgeForGet.kt")
public void testSpecialBridgeForGet() throws Exception {
runTest("compiler/testData/codegen/box/collections/specialBridgeForGet.kt");
}
@TestMetadata("strList.kt")
public void testStrList() throws Exception {
runTest("compiler/testData/codegen/box/collections/strList.kt");
@@ -6750,6 +6750,12 @@ public class NativeExtBlackBoxTestGenerated extends AbstractNativeBlackBoxTest {
runTest("compiler/testData/codegen/box/collections/removeOverriddenInJava_Map.kt");
}
@Test
@TestMetadata("specialBridgeForGet.kt")
public void testSpecialBridgeForGet() throws Exception {
runTest("compiler/testData/codegen/box/collections/specialBridgeForGet.kt");
}
@Test
@TestMetadata("strList.kt")
public void testStrList() throws Exception {