JVM_IR KT-45920 don't add special bridge if it clashes with known method

This commit is contained in:
Dmitry Petrov
2021-04-08 16:28:29 +03:00
committed by TeamCityServer
parent 92d200e093
commit c2a5b0b6e2
6 changed files with 78 additions and 11 deletions
@@ -34,7 +34,6 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method
import java.util.concurrent.ConcurrentHashMap
@@ -246,6 +245,9 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
private fun createBridges(irClass: IrClass, irFunction: IrSimpleFunction) {
// Track final overrides and bridges to avoid clashes
val blacklist = mutableSetOf<Method>()
val existingMethodSignatures = irClass.functions
.filter { !it.isFakeOverride && it.name == irFunction.name }
.mapTo(HashSet()) { it.jvmMethod }
// Add the current method to the blacklist if it is concrete or final.
val targetMethod = (irFunction.resolveFakeOverride() ?: irFunction).jvmMethod
@@ -260,10 +262,14 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
// bridge methods in a superclass.
blacklist += irFunction.overriddenFinalSpecialBridges()
// We only generate a special bridge method if it does not clash with a final method in a superclass or the current method
val specialBridgeTarget = if (
specialBridge.signature !in blacklist && (!irFunction.isFakeOverride || irFunction.jvmMethod != specialBridge.signature)
) {
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
if (specialBridge.signature in blacklist ||
irFunction.isFakeOverride && irFunction.jvmMethod == specialBridge.signature
) {
return irFunction
}
// If irFunction is a fake override, we replace it with a stub and redirect all calls to irFunction with
// calls to the stub instead. Otherwise we'll end up calling the special method itself and get into an
// infinite loop.
@@ -306,12 +312,16 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
blacklist += bridgeTarget.jvmMethod
}
if (specialBridge.signature in existingMethodSignatures) {
return irFunction
}
blacklist += specialBridge.signature
irClass.addSpecialBridge(specialBridge, bridgeTarget)
} else {
irFunction
return irClass.addSpecialBridge(specialBridge, bridgeTarget)
}
val specialBridgeTarget = getSpecialBridgeTargetAddingExtraBridges()
// Deal with existing function that override special bridge methods.
if (!irFunction.isFakeOverride && specialBridge.methodInfo != null) {
irFunction.rewriteSpecialMethodBody(targetMethod, specialBridge.signature, specialBridge.methodInfo)
@@ -434,9 +444,8 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
// This is technically wrong, but it's necessary to generate a method which maps to the same signature.
// In case of 'fun foo(): T', where 'T' is a type parameter with primitive upper bound (e.g., 'T : Char'),
// 'foo' is mapped to 'foo()C', regardless of its overrides.
val inheritedOverrides = bridge.overriddenSymbols.flatMapTo(mutableSetOf()) { function ->
function.owner.safeAs<IrSimpleFunction>()?.overriddenSymbols ?: emptyList()
}
val inheritedOverrides = bridge.overriddenSymbols
.flatMapTo(mutableSetOf()) { it.owner.overriddenSymbols }
val redundantOverrides = inheritedOverrides.flatMapTo(mutableSetOf()) {
it.owner.allOverridden().map { override -> override.symbol }
}
@@ -0,0 +1,3 @@
abstract class MindMap : MutableMap<Int, String> {
operator fun get(key: Any?): String? = null
}
@@ -0,0 +1,22 @@
@kotlin.Metadata
public abstract class MindMap {
// source: 'abstractMapRedefiningGetAny.kt'
public method <init>(): void
public abstract method containsKey(p0: int): boolean
public bridge final method containsKey(p0: java.lang.Object): boolean
public bridge final method containsValue(p0: java.lang.Object): boolean
public abstract method containsValue(p0: java.lang.String): boolean
public bridge final method entrySet(): java.util.Set
public final @org.jetbrains.annotations.Nullable method get(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.String
public abstract method get(p0: int): java.lang.String
public bridge final method get(p0: java.lang.Object): java.lang.Object
public abstract method getEntries(): java.util.Set
public abstract method getKeys(): java.util.Set
public abstract method getSize(): int
public abstract method getValues(): java.util.Collection
public bridge final method keySet(): java.util.Set
public abstract method remove(p0: int): java.lang.String
public bridge final method remove(p0: java.lang.Object): java.lang.Object
public bridge final method size(): int
public bridge final method values(): java.util.Collection
}
@@ -0,0 +1,23 @@
@kotlin.Metadata
public abstract class MindMap {
// source: 'abstractMapRedefiningGetAny.kt'
public method <init>(): void
public abstract method containsKey(p0: int): boolean
public bridge final method containsKey(p0: java.lang.Object): boolean
public bridge final method containsValue(p0: java.lang.Object): boolean
public abstract method containsValue(p0: java.lang.String): boolean
public bridge final method entrySet(): java.util.Set
public final @org.jetbrains.annotations.Nullable method get(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.String
public abstract method get(p0: int): java.lang.String
public synthetic bridge method get(p0: java.lang.Object): java.lang.Object
public abstract method getEntries(): java.util.Set
public abstract method getKeys(): java.util.Set
public abstract method getSize(): int
public abstract method getValues(): java.util.Collection
public bridge final method keySet(): java.util.Set
public abstract method remove(p0: int): java.lang.String
public synthetic bridge method remove(p0: java.lang.Object): java.lang.Object
public bridge final method remove(p0: java.lang.Object): java.lang.String
public bridge final method size(): int
public bridge final method values(): java.util.Collection
}
@@ -351,6 +351,11 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
@TestMetadata("abstractMapRedefiningGetAny.kt")
public void testAbstractMapRedefiningGetAny() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/abstractMapRedefiningGetAny.kt");
}
public void testAllFilesPresentInCollectionStubs() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/collectionStubs"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@@ -351,6 +351,11 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
}
@TestMetadata("abstractMapRedefiningGetAny.kt")
public void testAbstractMapRedefiningGetAny() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/abstractMapRedefiningGetAny.kt");
}
public void testAllFilesPresentInCollectionStubs() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/collectionStubs"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}