JVM_IR KT-48945 generate special bridge with unsubstituted signature

This commit is contained in:
Dmitry Petrov
2021-11-15 15:51:28 +03:00
committed by TeamCityServer
parent 4c3404888a
commit 30ceb49442
36 changed files with 809 additions and 42 deletions
@@ -24,6 +24,7 @@ data class SpecialMethodWithDefaultInfo(
val defaultValueGenerator: (IrSimpleFunction) -> IrExpression,
val argumentsToCheck: Int,
val needsGenericSignature: Boolean = false,
val needsUnsubstitutedBridge: Boolean = false
)
class BuiltInWithDifferentJvmName(
@@ -78,11 +79,11 @@ class SpecialBridgeMethods(val context: CommonBackendContext) {
makeDescription(StandardNames.FqNames.list, "lastIndexOf", 1) to
SpecialMethodWithDefaultInfo(::constMinusOne, 1),
makeDescription(StandardNames.FqNames.map, "getOrDefault", 2) to
SpecialMethodWithDefaultInfo(::getSecondArg, 1, needsGenericSignature = true),
SpecialMethodWithDefaultInfo(::getSecondArg, 1, needsGenericSignature = true, needsUnsubstitutedBridge = true),
makeDescription(StandardNames.FqNames.map, "get", 1) to
SpecialMethodWithDefaultInfo(::constNull, 1, needsGenericSignature = true),
SpecialMethodWithDefaultInfo(::constNull, 1, needsGenericSignature = true, needsUnsubstitutedBridge = true),
makeDescription(StandardNames.FqNames.mutableMap, "remove", 1) to
SpecialMethodWithDefaultInfo(::constNull, 1, needsGenericSignature = true)
SpecialMethodWithDefaultInfo(::constNull, 1, needsGenericSignature = true, needsUnsubstitutedBridge = true)
)
private val specialProperties = mapOf(
@@ -265,18 +265,35 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
irClass.addSpecialBridge(superBridge, superTarget)
}
}
else -> irFunction
else -> {
irFunction
}
}
blacklist += bridgeTarget.jvmMethod
}
if (irClass.functions.any { !it.isFakeOverride && it.name == irFunction.name && specialBridge.signature == it.jvmMethod }) {
return irFunction
// Add special bridge with unsubstituted signature (as generated by old JVM back-end) if required.
// According to KT-40277 we generate special bridges for 'get', 'getOrDefault', and 'remove' with substituted return type.
// However, this introduces performance regressions and changes in behavior if cases like KT-48945
// (where a Kotlin class implements a read-only collection interface and extends a Java collection class).
val unsubstitutedSpecialBridge = specialBridge.unsubstitutedSpecialBridge
if (unsubstitutedSpecialBridge != null &&
irClass.functions.none { it.isClashingWithPotentialBridge(irFunction.name, unsubstitutedSpecialBridge.signature) }
) {
blacklist += unsubstitutedSpecialBridge.signature
// TODO what should be the special bridge target if we CAN NOT add a substituted special bridge
// (below, clashes with existing method),
// but CAN add an unsubstituted special bridge (here)?
irClass.addSpecialBridge(unsubstitutedSpecialBridge, bridgeTarget)
}
blacklist += specialBridge.signature
return irClass.addSpecialBridge(specialBridge, bridgeTarget)
if (irClass.functions.none { it.isClashingWithPotentialBridge(irFunction.name, specialBridge.signature) }) {
blacklist += specialBridge.signature
return irClass.addSpecialBridge(specialBridge, bridgeTarget)
}
return irFunction
}
val specialBridgeTarget = getSpecialBridgeTargetAddingExtraBridges()
@@ -339,6 +356,10 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
.forEach { irClass.addBridge(it, bridgeTarget) }
}
private fun IrSimpleFunction.isClashingWithPotentialBridge(name: Name, signature: Method): Boolean =
(!this.isFakeOverride || this.modality == Modality.FINAL) && this.name == name && this.jvmMethod == signature
// Returns the special bridge overridden by the current methods if it exists.
private val IrSimpleFunction.specialBridgeOrNull: SpecialBridge?
get() = context.bridgeLoweringCache.computeSpecialBridge(this)
@@ -42,4 +42,9 @@ data class SpecialBridge(
val isFinal: Boolean = true,
val isSynthetic: Boolean = false,
val isOverriding: Boolean = true,
// 'true' if we also should produce a synthetic bridge with unsubstituted signature.
// NB this is passed down the hierarchy to the point where 'unsubstitutedSpecialBridge' is created,
// see BridgeLoweringCache::computeSpecialBridge
val needsUnsubstitutedBridge: Boolean = false,
val unsubstitutedSpecialBridge: SpecialBridge? = null
)
@@ -54,7 +54,10 @@ class BridgeLoweringCache(private val context: JvmBackendContext) {
val specialMethodInfo = specialBridgeMethods.getSpecialMethodInfo(function)
if (specialMethodInfo != null)
return SpecialBridge(
function, computeJvmMethod(function), specialMethodInfo.needsGenericSignature, methodInfo = specialMethodInfo
overridden = function, signature = computeJvmMethod(function),
needsGenericSignature = specialMethodInfo.needsGenericSignature,
methodInfo = specialMethodInfo,
needsUnsubstitutedBridge = specialMethodInfo.needsUnsubstitutedBridge
)
val specialBuiltInInfo = specialBridgeMethods.getBuiltInWithDifferentJvmName(function)
@@ -87,10 +90,22 @@ class BridgeLoweringCache(private val context: JvmBackendContext) {
parent = function.parent
}
val substitutedOverrideSignature = computeJvmMethod(substitutedOverride)
val unsubstitutedSpecialBridge =
when {
specialBridge.unsubstitutedSpecialBridge != null ->
specialBridge.unsubstitutedSpecialBridge
specialBridge.needsUnsubstitutedBridge && specialBridge.signature != substitutedOverrideSignature ->
specialBridge.copy(isSynthetic = true)
else ->
null
}
return specialBridge.copy(
signature = computeJvmMethod(substitutedOverride),
signature = substitutedOverrideSignature,
substitutedParameterTypes = substitutedParameterTypes,
substitutedReturnType = function.returnType
substitutedReturnType = function.returnType,
unsubstitutedSpecialBridge = unsubstitutedSpecialBridge
)
}