JVM IR: Produce correct generic signatures for special bridge methods

This commit is contained in:
Steven Schäfer
2020-07-02 10:58:03 +02:00
committed by Alexander Udalov
parent c16b548dff
commit 9d63412b3e
12 changed files with 308 additions and 89 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -19,7 +19,14 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
data class SpecialMethodWithDefaultInfo(
val defaultValueGenerator: (IrSimpleFunction) -> IrExpression, val argumentsToCheck: Int, val needsArgumentBoxing: Boolean = false
val defaultValueGenerator: (IrSimpleFunction) -> IrExpression,
val argumentsToCheck: Int,
val needsArgumentBoxing: Boolean = false,
val needsGenericSignature: Boolean = false,
)
class BuiltInWithDifferentJvmName(
val needsGenericSignature: Boolean = false,
)
class SpecialBridgeMethods(val context: CommonBackendContext) {
@@ -54,36 +61,46 @@ class SpecialBridgeMethods(val context: CommonBackendContext) {
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, bridge.valueParameters[1].symbol)
private val SPECIAL_METHODS_WITH_DEFAULTS_MAP = mapOf(
makeDescription(KotlinBuiltIns.FQ_NAMES.collection, "contains", 1) to SpecialMethodWithDefaultInfo(::constFalse, 1),
makeDescription(KotlinBuiltIns.FQ_NAMES.mutableCollection, "remove", 1) to SpecialMethodWithDefaultInfo(::constFalse, 1, true),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "containsKey", 1) to SpecialMethodWithDefaultInfo(::constFalse, 1),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "containsValue", 1) to SpecialMethodWithDefaultInfo(::constFalse, 1),
makeDescription(KotlinBuiltIns.FQ_NAMES.mutableMap, "remove", 2) to SpecialMethodWithDefaultInfo(::constFalse, 2),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "getOrDefault", 2) to SpecialMethodWithDefaultInfo(::getSecondArg, 1),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "get", 1) to SpecialMethodWithDefaultInfo(::constNull, 1),
makeDescription(KotlinBuiltIns.FQ_NAMES.mutableMap, "remove", 1) to SpecialMethodWithDefaultInfo(::constNull, 1),
makeDescription(KotlinBuiltIns.FQ_NAMES.list, "indexOf", 1) to SpecialMethodWithDefaultInfo(::constMinusOne, 1),
makeDescription(KotlinBuiltIns.FQ_NAMES.list, "lastIndexOf", 1) to SpecialMethodWithDefaultInfo(::constMinusOne, 1)
makeDescription(KotlinBuiltIns.FQ_NAMES.collection, "contains", 1) to
SpecialMethodWithDefaultInfo(::constFalse, 1),
makeDescription(KotlinBuiltIns.FQ_NAMES.mutableCollection, "remove", 1) to
SpecialMethodWithDefaultInfo(::constFalse, 1, needsArgumentBoxing = true),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "containsKey", 1) to
SpecialMethodWithDefaultInfo(::constFalse, 1),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "containsValue", 1) to
SpecialMethodWithDefaultInfo(::constFalse, 1),
makeDescription(KotlinBuiltIns.FQ_NAMES.mutableMap, "remove", 2) to
SpecialMethodWithDefaultInfo(::constFalse, 2),
makeDescription(KotlinBuiltIns.FQ_NAMES.list, "indexOf", 1) to
SpecialMethodWithDefaultInfo(::constMinusOne, 1),
makeDescription(KotlinBuiltIns.FQ_NAMES.list, "lastIndexOf", 1) to
SpecialMethodWithDefaultInfo(::constMinusOne, 1),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "getOrDefault", 2) to
SpecialMethodWithDefaultInfo(::getSecondArg, 1, needsGenericSignature = true),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "get", 1) to
SpecialMethodWithDefaultInfo(::constNull, 1, needsGenericSignature = true),
makeDescription(KotlinBuiltIns.FQ_NAMES.mutableMap, "remove", 1) to
SpecialMethodWithDefaultInfo(::constNull, 1, needsGenericSignature = true)
)
private val SPECIAL_PROPERTIES_SET = setOf(
makeDescription(KotlinBuiltIns.FQ_NAMES.collection, "size"),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "size"),
makeDescription(KotlinBuiltIns.FQ_NAMES.charSequence.toSafe(), "length"),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "keys"),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "values"),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "entries")
private val SPECIAL_PROPERTIES_SET = mapOf(
makeDescription(KotlinBuiltIns.FQ_NAMES.collection, "size") to BuiltInWithDifferentJvmName(),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "size") to BuiltInWithDifferentJvmName(),
makeDescription(KotlinBuiltIns.FQ_NAMES.charSequence.toSafe(), "length") to BuiltInWithDifferentJvmName(),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "keys") to BuiltInWithDifferentJvmName(needsGenericSignature = true),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "values") to BuiltInWithDifferentJvmName(needsGenericSignature = true),
makeDescription(KotlinBuiltIns.FQ_NAMES.map, "entries") to BuiltInWithDifferentJvmName(needsGenericSignature = true)
)
private val SPECIAL_METHODS_SETS = setOf(
makeDescription(KotlinBuiltIns.FQ_NAMES.number.toSafe(), "toByte"),
makeDescription(KotlinBuiltIns.FQ_NAMES.number.toSafe(), "toShort"),
makeDescription(KotlinBuiltIns.FQ_NAMES.number.toSafe(), "toInt"),
makeDescription(KotlinBuiltIns.FQ_NAMES.number.toSafe(), "toLong"),
makeDescription(KotlinBuiltIns.FQ_NAMES.number.toSafe(), "toFloat"),
makeDescription(KotlinBuiltIns.FQ_NAMES.number.toSafe(), "toDouble"),
makeDescription(KotlinBuiltIns.FQ_NAMES.mutableList, "removeAt", 1),
makeDescription(KotlinBuiltIns.FQ_NAMES.charSequence.toSafe(), "get", 1)
private val SPECIAL_METHODS_SETS = mapOf(
makeDescription(KotlinBuiltIns.FQ_NAMES.number.toSafe(), "toByte") to BuiltInWithDifferentJvmName(),
makeDescription(KotlinBuiltIns.FQ_NAMES.number.toSafe(), "toShort") to BuiltInWithDifferentJvmName(),
makeDescription(KotlinBuiltIns.FQ_NAMES.number.toSafe(), "toInt") to BuiltInWithDifferentJvmName(),
makeDescription(KotlinBuiltIns.FQ_NAMES.number.toSafe(), "toLong") to BuiltInWithDifferentJvmName(),
makeDescription(KotlinBuiltIns.FQ_NAMES.number.toSafe(), "toFloat") to BuiltInWithDifferentJvmName(),
makeDescription(KotlinBuiltIns.FQ_NAMES.number.toSafe(), "toDouble") to BuiltInWithDifferentJvmName(),
makeDescription(KotlinBuiltIns.FQ_NAMES.charSequence.toSafe(), "get", 1) to BuiltInWithDifferentJvmName(),
makeDescription(KotlinBuiltIns.FQ_NAMES.mutableList, "removeAt", 1) to BuiltInWithDifferentJvmName(needsGenericSignature = true)
)
fun findSpecialWithOverride(irFunction: IrSimpleFunction): Pair<IrSimpleFunction, SpecialMethodWithDefaultInfo>? {
@@ -101,15 +118,15 @@ class SpecialBridgeMethods(val context: CommonBackendContext) {
return SPECIAL_METHODS_WITH_DEFAULTS_MAP[description]
}
fun isBuiltInWithDifferentJvmName(irFunction: IrSimpleFunction): Boolean {
fun getBuiltInWithDifferentJvmName(irFunction: IrSimpleFunction): BuiltInWithDifferentJvmName? {
irFunction.correspondingPropertySymbol?.let {
val classFqName = irFunction.parentAsClass.fqNameWhenAvailable
?: return false
?: return null
return makeDescription(classFqName, it.owner.name.asString()) in SPECIAL_PROPERTIES_SET
return SPECIAL_PROPERTIES_SET[makeDescription(classFqName, it.owner.name.asString())]
}
return irFunction.toDescription() in SPECIAL_METHODS_SETS
return SPECIAL_METHODS_SETS[irFunction.toDescription()]
}
}
@@ -24,9 +24,11 @@ import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
@@ -127,19 +129,36 @@ private class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass,
val overriddenSymbols: MutableList<IrSimpleFunctionSymbol> = mutableListOf()
)
// Represents a special bridge to `overridden`. Special bridges are always public and non-synthetic.
// There are ten type-safe wrappers for different collection methods, which have an additional `methodInfo`
// field. There is only one special bridge method which uses generic types in Java (MutableList.removeAt)
// and needs a `specializedReturnType` for its overrides. Finally, we sometimes need to use INVOKESPECIAL
// to invoke an existing special bridge implementation in a superclass, which is what `superQualifierSymbol`
// is for.
// Represents a special bridge to `overridden`. Special bridges are overrides for Java methods which are
// exposed to Kotlin with a different name or different types. Typically, the Java version of a method is
// non-generic, while Kotlin exposes a generic method. In this case, the bridge method needs to perform
// additional type checking at runtime. The behavior in case of type errors is configured in `methodInfo`.
//
// Since special bridges may be exposed to Java as non-synthetic methods, we need correct generic signatures.
// There are a total of seven generic special bridge methods (Map.getOrDefault, Map.get, MutableMap.remove with
// only one argument, Map.keys, Map.values, Map.entries, and MutableList.removeAt). Of these seven there is only
// one which the JVM backend currently handles correctly (MutableList.removeAt). For the rest, it's impossible
// to reproduce the behavior of the JVM backend in this lowering.
//
// Finally, we sometimes need to use INVOKESPECIAL to invoke an existing special bridge implementation in a
// superclass, which is what `superQualifierSymbol` is for.
private data class SpecialBridge(
val overridden: IrSimpleFunction,
val signature: Method,
val specializedReturnType: IrType? = null,
// We need to produce a generic signature if the underlying Java method contains type parameters.
// E.g., the `java.util.Map<K, V>.keySet` method has a return type of `Set<K>`, and hence overrides
// need to generate a generic signature.
val needsGenericSignature: Boolean = false,
// The result of substituting type parameters in the overridden Java method. This is different from
// substituting into the overridden Kotlin method. For example, Map.getOrDefault has two arguments
// with generic types in Kotlin, but only the second parameter is generic in Java.
// May be null if the underlying Java method does not contain generic types.
val substitutedParameterTypes: List<IrType>? = null,
val substitutedReturnType: IrType? = null,
val methodInfo: SpecialMethodWithDefaultInfo? = null,
val superQualifierSymbol: IrClassSymbol? = null,
val isFinal: Boolean = true,
val isSynthetic: Boolean = false,
)
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid()
@@ -206,7 +225,9 @@ private class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass,
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
if (specialBridge.signature !in blacklist && (!irFunction.isFakeOverride || irFunction.jvmMethod != specialBridge.signature)) {
val specialBridgeTarget = if (
specialBridge.signature !in blacklist && (!irFunction.isFakeOverride || irFunction.jvmMethod != specialBridge.signature)
) {
// 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.
@@ -243,14 +264,26 @@ private class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass,
blacklist += bridgeTarget.jvmMethod
}
irClass.addSpecialBridge(specialBridge, bridgeTarget)
blacklist += specialBridge.signature
irClass.addSpecialBridge(specialBridge, bridgeTarget)
} else {
irFunction
}
// Deal with existing function that override special bridge methods.
if (!irFunction.isFakeOverride && specialBridge.methodInfo != null) {
irFunction.rewriteSpecialMethodBody(targetMethod, specialBridge.signature, specialBridge.methodInfo)
}
// For generic special bridge methods we need to generate bridges for generic overrides coming from Java or Kotlin interfaces.
if (specialBridge.substitutedReturnType != null) {
for (overriddenSpecialBridge in irFunction.overriddenSpecialBridges()) {
if (overriddenSpecialBridge.signature !in blacklist) {
irClass.addSpecialBridge(overriddenSpecialBridge, specialBridgeTarget)
blacklist += overriddenSpecialBridge.signature
}
}
}
} else if (irFunction.isJvmAbstract(context.state.jvmDefaultMode)) {
// Do not generate bridge methods for abstract methods which do not override a special bridge method.
// This matches the behavior of the JVM backend, but it does mean that we generate superfluous bridges
@@ -271,6 +304,9 @@ private class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass,
}
}
if (generated.isEmpty())
return
// For concrete fake overrides, some of the bridges may be inherited from the super-classes. Specifically, bridges for all
// declarations that are reachable from all concrete immediate super-functions of the given function. Note that all such bridges are
// guaranteed to delegate to the same implementation as bridges for the given function, that's why it's safe to inherit them.
@@ -289,32 +325,38 @@ private class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass,
// Returns the special bridge overridden by the current methods if it exists.
private val IrSimpleFunction.specialBridgeOrNull: SpecialBridge?
get() = specialBridgeCache.getOrPutNullable(symbol) {
val specialMethodInfo = specialBridgeMethods.getSpecialMethodInfo(this)
return when {
specialMethodInfo != null ->
// Note that there are type-safe special bridges with generic return types in Java (namely Map.getOrDefault,
// Map.get, and MutableMap.remove), but the JVM backend does not produce overrides with specialized return
// types for them. So for compatibility, neither do we.
SpecialBridge(this, jvmMethod, methodInfo = specialMethodInfo)
specialBridgeMethods.isBuiltInWithDifferentJvmName(this) ->
if (returnType.isTypeParameter())
SpecialBridge(this, jvmMethod, specializedReturnType = returnType)
else
SpecialBridge(this, jvmMethod)
else -> {
val overriddenSpecialBridge = overriddenSymbols.asSequence().mapNotNull { it.owner.specialBridgeOrNull }.firstOrNull()
if (overriddenSpecialBridge?.specializedReturnType != null) {
val specializedSignature = Method(
overriddenSpecialBridge.signature.name,
context.methodSignatureMapper.mapReturnType(this),
overriddenSpecialBridge.signature.argumentTypes
)
overriddenSpecialBridge.copy(signature = specializedSignature, specializedReturnType = returnType)
} else {
overriddenSpecialBridge
specialBridgeMethods.getSpecialMethodInfo(this)?.let { specialMethodInfo ->
SpecialBridge(this, jvmMethod, methodInfo = specialMethodInfo, needsGenericSignature = specialMethodInfo.needsGenericSignature)
} ?: specialBridgeMethods.getBuiltInWithDifferentJvmName(this)?.let { specialBuiltInInfo ->
SpecialBridge(this, jvmMethod, needsGenericSignature = specialBuiltInInfo.needsGenericSignature)
} ?: overriddenSymbols.asSequence().mapNotNull { it.owner.specialBridgeOrNull }.firstOrNull()?.let { specialBridge ->
if (specialBridge.needsGenericSignature) {
// Compute the substituted signature.
val erasedParameterCount = specialBridge.methodInfo?.argumentsToCheck ?: 0
val substitutedParameterTypes = valueParameters.mapIndexed { index, param ->
if (index < erasedParameterCount) context.irBuiltIns.anyNType else param.type
}
val substitutedOverride = buildFun {
updateFrom(specialBridge.overridden)
name = Name.identifier(specialBridge.signature.name)
returnType = this@specialBridgeOrNull.returnType
}.apply {
// All existing special bridges only have value parameter types.
valueParameters = this@specialBridgeOrNull.valueParameters.zip(substitutedParameterTypes).map { (param, type) ->
param.copyTo(this, IrDeclarationOrigin.BRIDGE, type = type)
}
overriddenSymbols = listOf(specialBridge.overridden.symbol)
parent = this@specialBridgeOrNull.parent
}
specialBridge.copy(
signature = substitutedOverride.jvmMethod,
substitutedParameterTypes = substitutedParameterTypes,
substitutedReturnType = returnType
)
} else {
specialBridge
}
}
}
@@ -334,6 +376,14 @@ private class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass,
it.specialBridgeOrNull?.signature?.takeIf { bridgeSignature -> bridgeSignature != it.jvmMethod }
}
// Sequence of special bridge methods which were not implemented in Kotlin superclasses.
private fun IrSimpleFunction.overriddenSpecialBridges(): Sequence<SpecialBridge> = allOverridden().mapNotNull {
if (it.parentAsClass.isInterface || it.comesFromJava())
it.specialBridgeOrNull?.takeIf { bridge -> bridge.signature != jvmMethod }
?.copy(isFinal = false, isSynthetic = true, methodInfo = null)
else null
}
private fun IrClass.addAbstractMethodStub(irFunction: IrSimpleFunction, needsArgumentBoxing: Boolean) =
addFunction {
updateFrom(irFunction)
@@ -347,7 +397,10 @@ private class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass,
// However, we cannot link in the new function as the new accessor for the property, since there might still
// be references to the original fake override stub.
copyCorrespondingPropertyFrom(irFunction)
copyParametersWithErasure(this@addAbstractMethodStub, irFunction, needsArgumentBoxing)
dispatchReceiverParameter = thisReceiver?.copyTo(this, type = defaultType)
valueParameters = irFunction.valueParameters.map { param ->
param.copyTo(this, type = if (needsArgumentBoxing) param.type.makeNullable() else param.type)
}
}
private fun IrClass.addBridge(bridge: Bridge, target: IrSimpleFunction): IrSimpleFunction =
@@ -379,15 +432,17 @@ private class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass,
private fun IrClass.addSpecialBridge(specialBridge: SpecialBridge, target: IrSimpleFunction): IrSimpleFunction =
addFunction {
modality = if (specialBridge.isFinal) Modality.FINAL else Modality.OPEN
origin = IrDeclarationOrigin.BRIDGE_SPECIAL
origin = if (specialBridge.isSynthetic) IrDeclarationOrigin.BRIDGE else IrDeclarationOrigin.BRIDGE_SPECIAL
name = Name.identifier(specialBridge.signature.name)
returnType = specialBridge.specializedReturnType ?: specialBridge.overridden.returnType.eraseTypeParameters()
returnType = specialBridge.substitutedReturnType ?: specialBridge.overridden.returnType.eraseTypeParameters()
}.apply {
copyParametersWithErasure(
this@addSpecialBridge,
specialBridge.overridden,
specialBridge.methodInfo?.needsArgumentBoxing == true
specialBridge.methodInfo?.needsArgumentBoxing == true,
specialBridge.substitutedParameterTypes
)
body = context.createIrBuilder(symbol).irBlockBody {
specialBridge.methodInfo?.let { info ->
valueParameters.take(info.argumentsToCheck).forEach {
@@ -396,7 +451,7 @@ private class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass,
}
+irReturn(delegatingCall(this@apply, target, specialBridge.superQualifierSymbol))
}
overriddenSymbols += specialBridge.overridden.symbol
overriddenSymbols = listOf(specialBridge.overridden.symbol)
}
private fun IrSimpleFunction.rewriteSpecialMethodBody(
@@ -421,8 +476,8 @@ private class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass,
argumentsToCheck.forEach {
val parameterType = it.type
if (!parameterType.isNullable()) {
val newParameter = it.copyTo(this@rewriteSpecialMethodBody, type = context.irBuiltIns.anyNType)
variableMap.put(valueParameters[it.index], newParameter)
val newParameter = it.copyTo(this@rewriteSpecialMethodBody, type = parameterType.makeNullable())
variableMap[valueParameters[it.index]] = newParameter
newValueParameters[it.index] = newParameter
+parameterTypeCheck(
newParameter,
@@ -463,17 +518,33 @@ private class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass,
private fun IrBuilderWithScope.parameterTypeCheck(parameter: IrValueParameter, type: IrType, defaultValue: IrExpression) =
irIfThen(context.irBuiltIns.unitType, irNot(irIs(irGet(parameter), type)), irReturn(defaultValue))
private fun IrSimpleFunction.copyParametersWithErasure(irClass: IrClass, from: IrSimpleFunction, forceArgumentBoxing: Boolean = false) {
private fun IrSimpleFunction.copyParametersWithErasure(
irClass: IrClass,
from: IrSimpleFunction,
forceArgumentBoxing: Boolean = false,
substitutedParameterTypes: List<IrType>? = null
) {
// This is a workaround for a bug affecting fake overrides. Sometimes we encounter fake overrides
// with dispatch receivers pointing at a superclass instead of the current class.
dispatchReceiverParameter = irClass.thisReceiver?.copyTo(this, type = irClass.defaultType)
extensionReceiverParameter = from.extensionReceiverParameter?.copyWithTypeErasure(this, forceArgumentBoxing)
valueParameters = from.valueParameters.map { it.copyWithTypeErasure(this, forceArgumentBoxing) }
valueParameters = if (substitutedParameterTypes != null) {
from.valueParameters.zip(substitutedParameterTypes).map { (param, type) ->
param.copyWithTypeErasure(this, forceArgumentBoxing, type)
}
} else {
from.valueParameters.map { it.copyWithTypeErasure(this, forceArgumentBoxing) }
}
}
private fun IrValueParameter.copyWithTypeErasure(target: IrSimpleFunction, forceBoxing: Boolean = false): IrValueParameter = copyTo(
private fun IrValueParameter.copyWithTypeErasure(
target: IrSimpleFunction,
forceArgumentBoxing: Boolean = false,
substitutedType: IrType? = null
): IrValueParameter = copyTo(
target, IrDeclarationOrigin.BRIDGE,
type = type.eraseTypeParameters().let { if (forceBoxing) it.makeNullable() else it },
type = (substitutedType ?: type.eraseTypeParameters()).let { if (forceArgumentBoxing) it.makeNullable() else it },
// Currently there are no special bridge methods with vararg parameters, so we don't track substituted vararg element types.
varargElementType = varargElementType?.eraseTypeParameters()
)
@@ -492,10 +563,10 @@ private class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass,
private fun IrBuilderWithScope.irCastIfNeeded(expression: IrExpression, to: IrType): IrExpression =
if (expression.type == to || to.isAny() || to.isNullableAny()) expression else irImplicitCast(expression, to)
private val signatureCache = mutableMapOf<IrFunction, Method>()
private val signatureCache = mutableMapOf<IrFunctionSymbol, Method>()
private val IrFunction.jvmMethod: Method
get() = signatureCache.getOrPut(this) { context.methodSignatureMapper.mapAsmMethod(this) }
get() = signatureCache.getOrPut(symbol) { context.methodSignatureMapper.mapAsmMethod(this) }
}
private fun IrDeclaration.comesFromJava() = parentAsClass.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
+1
View File
@@ -1,4 +1,5 @@
// TARGET_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// java.lang.NoSuchMethodError: java.util.TreeMap.remove
// IGNORE_BACKEND: ANDROID
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
interface ImmutableCollection<out E> : Collection<E> {
fun add(element: @UnsafeVariance E): ImmutableCollection<E>
fun addAll(elements: Collection<@UnsafeVariance E>): ImmutableCollection<E>
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class A0<E> : MutableList<E> {
override fun add(element: E): Boolean {
throw UnsupportedOperationException()
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_SIGNATURES
class GenericMap<K, V> : MutableMap<K, V> by HashMap<K, V>()
@@ -1,5 +1,5 @@
// IGNORE_BACKEND: JVM_IR
// WITH_SIGNATURES
// Test expectations differ between JVM and JVM IR backends, because of KT-40277. This should be revisited once KT-40277 is resolved.
class StringStringMap : MutableMap<String, String> by HashMap<String, String>()
@@ -0,0 +1,57 @@
@kotlin.Metadata
public abstract class<Ljava/lang/Object;Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;Lkotlin/jvm/internal/markers/KMutableMap;> AbstractStringStringMap {
public @org.jetbrains.annotations.NotNull <()Ljava/util/Collection<Ljava/lang/String;>;> method getValues(): java.util.Collection
public bridge final <()Ljava/util/Collection<Ljava/lang/String;>;> method values(): java.util.Collection
public @org.jetbrains.annotations.NotNull <()Ljava/util/Set<Ljava/lang/String;>;> method getKeys(): java.util.Set
public bridge final <()Ljava/util/Set<Ljava/lang/String;>;> method keySet(): java.util.Set
public bridge final <()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;>;> method entrySet(): java.util.Set
public @org.jetbrains.annotations.NotNull <()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;>;> method getEntries(): java.util.Set
public <(Ljava/util/Map<+Ljava/lang/String;+Ljava/lang/String;>;)V> method putAll(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
public <null> method <init>(): void
public <null> method clear(): void
public <null> method containsKey(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean
public bridge final <null> method containsKey(p0: java.lang.Object): boolean
public <null> method containsValue(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean
public bridge final <null> method containsValue(p0: java.lang.Object): boolean
public @org.jetbrains.annotations.Nullable <null> method get(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String
public synthetic bridge <null> method get(p0: java.lang.Object): java.lang.Object
public bridge final <null> method get(p0: java.lang.Object): java.lang.String
public <null> method getSize(): int
public <null> method isEmpty(): boolean
public @org.jetbrains.annotations.Nullable <null> method put(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: java.lang.String): java.lang.String
public synthetic bridge <null> method put(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
public @org.jetbrains.annotations.Nullable <null> method remove(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String
public synthetic bridge <null> method remove(p0: java.lang.Object): java.lang.Object
public bridge final <null> method remove(p0: java.lang.Object): java.lang.String
public bridge final <null> method size(): int
private synthetic final field <Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/String;>;> $$delegate_0: java.util.HashMap
}
@kotlin.Metadata
public final class<Ljava/lang/Object;Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;Lkotlin/jvm/internal/markers/KMutableMap;> StringStringMap {
public @org.jetbrains.annotations.NotNull <()Ljava/util/Collection<Ljava/lang/String;>;> method getValues(): java.util.Collection
public bridge final <()Ljava/util/Collection<Ljava/lang/String;>;> method values(): java.util.Collection
public @org.jetbrains.annotations.NotNull <()Ljava/util/Set<Ljava/lang/String;>;> method getKeys(): java.util.Set
public bridge final <()Ljava/util/Set<Ljava/lang/String;>;> method keySet(): java.util.Set
public bridge final <()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;>;> method entrySet(): java.util.Set
public @org.jetbrains.annotations.NotNull <()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;>;> method getEntries(): java.util.Set
public <(Ljava/util/Map<+Ljava/lang/String;+Ljava/lang/String;>;)V> method putAll(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
public <null> method <init>(): void
public <null> method clear(): void
public <null> method containsKey(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean
public bridge final <null> method containsKey(p0: java.lang.Object): boolean
public <null> method containsValue(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean
public bridge final <null> method containsValue(p0: java.lang.Object): boolean
public @org.jetbrains.annotations.Nullable <null> method get(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String
public synthetic bridge <null> method get(p0: java.lang.Object): java.lang.Object
public bridge final <null> method get(p0: java.lang.Object): java.lang.String
public <null> method getSize(): int
public <null> method isEmpty(): boolean
public @org.jetbrains.annotations.Nullable <null> method put(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: java.lang.String): java.lang.String
public synthetic bridge <null> method put(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
public @org.jetbrains.annotations.Nullable <null> method remove(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String
public synthetic bridge <null> method remove(p0: java.lang.Object): java.lang.Object
public bridge final <null> method remove(p0: java.lang.Object): java.lang.String
public bridge final <null> method size(): int
private synthetic final field <Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/String;>;> $$delegate_0: java.util.HashMap
}
@@ -1,5 +1,5 @@
// IGNORE_BACKEND: JVM_IR
// WITH_SIGNATURES
// Test expectations differ between JVM and JVM IR backends, because of KT-40277. This should be revisited once KT-40277 is resolved.
class StringMap<V> : MutableMap<String, V> by HashMap<String, V>()
@@ -0,0 +1,51 @@
@kotlin.Metadata
public abstract class<<V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Map<Ljava/lang/String;TV;>;Lkotlin/jvm/internal/markers/KMutableMap;> AbstractStringMap {
public @org.jetbrains.annotations.NotNull <()Ljava/util/Collection<TV;>;> method getValues(): java.util.Collection
public bridge final <()Ljava/util/Collection<TV;>;> method values(): java.util.Collection
public @org.jetbrains.annotations.NotNull <()Ljava/util/Set<Ljava/lang/String;>;> method getKeys(): java.util.Set
public bridge final <()Ljava/util/Set<Ljava/lang/String;>;> method keySet(): java.util.Set
public bridge final <()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;TV;>;>;> method entrySet(): java.util.Set
public @org.jetbrains.annotations.NotNull <()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;TV;>;>;> method getEntries(): java.util.Set
public @org.jetbrains.annotations.Nullable <(Ljava/lang/Object;)TV;> method get(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.Object
public bridge final <(Ljava/lang/Object;)TV;> method get(p0: java.lang.Object): java.lang.Object
public @org.jetbrains.annotations.Nullable <(Ljava/lang/Object;)TV;> method remove(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.Object
public bridge final <(Ljava/lang/Object;)TV;> method remove(p0: java.lang.Object): java.lang.Object
public @org.jetbrains.annotations.Nullable <(Ljava/lang/String;TV;)TV;> method put(@org.jetbrains.annotations.NotNull p0: java.lang.String, p1: java.lang.Object): java.lang.Object
public <(Ljava/util/Map<+Ljava/lang/String;+TV;>;)V> method putAll(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
public <null> method <init>(): void
public <null> method clear(): void
public <null> method containsKey(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean
public bridge final <null> method containsKey(p0: java.lang.Object): boolean
public <null> method containsValue(p0: java.lang.Object): boolean
public <null> method getSize(): int
public <null> method isEmpty(): boolean
public synthetic bridge <null> method put(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
public bridge final <null> method size(): int
private synthetic final field <Ljava/util/HashMap<Ljava/lang/String;TV;>;> $$delegate_0: java.util.HashMap
}
@kotlin.Metadata
public final class<<V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Map<Ljava/lang/String;TV;>;Lkotlin/jvm/internal/markers/KMutableMap;> StringMap {
public @org.jetbrains.annotations.NotNull <()Ljava/util/Collection<TV;>;> method getValues(): java.util.Collection
public bridge final <()Ljava/util/Collection<TV;>;> method values(): java.util.Collection
public @org.jetbrains.annotations.NotNull <()Ljava/util/Set<Ljava/lang/String;>;> method getKeys(): java.util.Set
public bridge final <()Ljava/util/Set<Ljava/lang/String;>;> method keySet(): java.util.Set
public bridge final <()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;TV;>;>;> method entrySet(): java.util.Set
public @org.jetbrains.annotations.NotNull <()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;TV;>;>;> method getEntries(): java.util.Set
public @org.jetbrains.annotations.Nullable <(Ljava/lang/Object;)TV;> method get(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.Object
public bridge final <(Ljava/lang/Object;)TV;> method get(p0: java.lang.Object): java.lang.Object
public @org.jetbrains.annotations.Nullable <(Ljava/lang/Object;)TV;> method remove(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.Object
public bridge final <(Ljava/lang/Object;)TV;> method remove(p0: java.lang.Object): java.lang.Object
public @org.jetbrains.annotations.Nullable <(Ljava/lang/String;TV;)TV;> method put(@org.jetbrains.annotations.NotNull p0: java.lang.String, p1: java.lang.Object): java.lang.Object
public <(Ljava/util/Map<+Ljava/lang/String;+TV;>;)V> method putAll(@org.jetbrains.annotations.NotNull p0: java.util.Map): void
public <null> method <init>(): void
public <null> method clear(): void
public <null> method containsKey(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean
public bridge final <null> method containsKey(p0: java.lang.Object): boolean
public <null> method containsValue(p0: java.lang.Object): boolean
public <null> method getSize(): int
public <null> method isEmpty(): boolean
public synthetic bridge <null> method put(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
public bridge final <null> method size(): int
private synthetic final field <Ljava/util/HashMap<Ljava/lang/String;TV;>;> $$delegate_0: java.util.HashMap
}
@@ -90,18 +90,33 @@ fun box(
}
/*
16 INVOKEVIRTUAL A[0-9]+.removeAt \(I\) -> calls in bridges with signature `public final bridge remove\(I\)` + 7 calls from `public synthetic bridge remove\(I\)Ljava/lang/Object;`
9 INVOKEVIRTUAL A[0-9]+\.remove \(I\) -> calls to A1-A9.removeAt
1 INVOKEINTERFACE A9\.remove \(I\) -> call A9.removeAt
1 INVOKEINTERFACE A9\.remove \(Ljava/lang/Object;\) -> call A9.remove
On the JVM backend we have:
16 INVOKEVIRTUAL A[0-9]+.removeAt \(I\) -> calls in bridges with signature `public final bridge remove\(I\)` + 7 calls from `public synthetic bridge remove\(I\)Ljava/lang/Object;`
9 INVOKEVIRTUAL A[0-9]+\.remove \(I\) -> calls to A1-A9.removeAt
1 INVOKEVIRTUAL A10\.remove \(I\) -> one call in 'box' function
On the JVM IR backend we have:
9 INVOKEVIRTUAL A[0-9]+.removeAt \(I\) -> calls in non-synthetic bridges with signature `public final bridge remove(I)`
17 INVOKEVIRTUAL A[0-9]+\.remove \(I\) -> calls to A1-A9.removeAt + calls in synthetic bridges with signature `public synthetic bridge remove(I)Ljava/lang/Object;`
2 INVOKEVIRTUAL A10\.remove \(I\) -> one call in 'box' function + call from synthetic `remove(I)` bridge
This currently differs because of KT-40277, and the test expectations should be revised once KT-40277 is resolved.
*/
// 16 INVOKEVIRTUAL A[0-9]+.removeAt \(I\)
// 9 INVOKEVIRTUAL A[0-9]+\.remove \(I\)
// 1 INVOKEINTERFACE A9\.remove \(I\)
// 1 INVOKEINTERFACE A9\.remove \(Ljava/lang/Object;\)
// 1 INVOKEVIRTUAL A10\.remove \(I\)
// 2 INVOKEINTERFACE java\/util\/List.remove \(I\)
// 2 INVOKEINTERFACE java\/util\/List.remove \(Ljava/lang/Object;\)
// JVM_TEMPLATES:
// 16 INVOKEVIRTUAL A[0-9]+.removeAt \(I\)
// 9 INVOKEVIRTUAL A[0-9]+\.remove \(I\)
// 1 INVOKEVIRTUAL A10\.remove \(I\)
// JVM_IR_TEMPLATES:
// 9 INVOKEVIRTUAL A[0-9]+.removeAt \(I\)
// 17 INVOKEVIRTUAL A[0-9]+\.remove \(I\)
// 2 INVOKEVIRTUAL A10\.remove \(I\)
@@ -27,4 +27,14 @@ public class TestMap<K, V> implements Map<K, V> {
class MyMap: TestMap<String, String>()
// The Kotlin version of getOrDefault, which redirects to the default implementation in java.util.Map
// 1 public bridge getOrDefault\(Ljava/lang/String;Ljava/lang/String;\)Ljava/lang/String;
// Test expectations differ between JVM and JVM IR backends, because of KT-40277. This should be revisited once KT-40277 is resolved.
// JVM_TEMPLATES:
// 1 public final bridge getOrDefault\(Ljava/lang/Object;Ljava/lang/Object;\)Ljava/lang/Object;
// JVM_IR_TEMPLATES:
// 1 public final bridge getOrDefault\(Ljava/lang/Object;Ljava/lang/String;\)Ljava/lang/String;
// 1 public synthetic bridge getOrDefault\(Ljava/lang/Object;Ljava/lang/Object;\)Ljava/lang/Object;