JVM IR: move out bridge/collection lowering caches to separate files
Both BridgeLoweringCache and CollectionStubComputer are not tecnically part of the lowerings, so they shouldn't be in the "lower" package.
This commit is contained in:
@@ -11,13 +11,13 @@ import org.jetbrains.kotlin.backend.common.Mapping
|
||||
import org.jetbrains.kotlin.backend.common.ir.Ir
|
||||
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.common.psi.PsiErrorBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.caches.BridgeLoweringCache
|
||||
import org.jetbrains.kotlin.backend.jvm.caches.CollectionStubComputer
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.IrTypeMapper
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.createFakeContinuation
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.BridgeLowering
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.CollectionStubComputer
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
@@ -113,7 +113,7 @@ class JvmBackendContext(
|
||||
fun getOverridesWithoutStubs(function: IrSimpleFunction): List<IrSimpleFunctionSymbol> =
|
||||
overridesWithoutStubs.getOrElse(function) { function.overriddenSymbols }
|
||||
|
||||
internal val bridgeLoweringCache = BridgeLowering.BridgeLoweringCache(this)
|
||||
internal val bridgeLoweringCache = BridgeLoweringCache(this)
|
||||
internal val functionsWithSpecialBridges: MutableSet<IrFunction> = ConcurrentHashMap.newKeySet()
|
||||
|
||||
override var inVerbosePhase: Boolean = false // TODO: needs parallelizing
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.lower.SpecialMethodWithDefaultInfo
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
|
||||
// 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.
|
||||
data class SpecialBridge(
|
||||
val overridden: IrSimpleFunction,
|
||||
val signature: Method,
|
||||
// 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,
|
||||
val isOverriding: Boolean = true,
|
||||
)
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.caches
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.backend.common.lower.SpecialBridgeMethods
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.SpecialBridge
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
internal class BridgeLoweringCache(private val context: JvmBackendContext) {
|
||||
private val specialBridgeMethods = SpecialBridgeMethods(context)
|
||||
|
||||
// TODO: consider moving this cache out to the backend context and using it everywhere throughout the codegen.
|
||||
// It might benefit performance, but can lead to confusing behavior if some declarations are changed along the way.
|
||||
// For example, adding an override for a declaration whose signature is already cached can result in incorrect signature
|
||||
// if its return type is a primitive type, and the new override's return type is an object type.
|
||||
private val signatureCache = ConcurrentHashMap<IrFunctionSymbol, Method>()
|
||||
|
||||
fun computeJvmMethod(function: IrFunction): Method =
|
||||
signatureCache.getOrPut(function.symbol) { context.methodSignatureMapper.mapAsmMethod(function) }
|
||||
|
||||
private fun canHaveSpecialBridge(function: IrSimpleFunction): Boolean {
|
||||
if (function.name in specialBridgeMethods.specialMethodNames)
|
||||
return true
|
||||
// Function name could be mangled by inline class rules
|
||||
val functionName = function.name.asString()
|
||||
if (specialBridgeMethods.specialMethodNames.any { functionName.startsWith(it.asString() + "-") })
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
fun computeSpecialBridge(function: IrSimpleFunction): SpecialBridge? {
|
||||
// Optimization: do not try to compute special bridge for irrelevant methods.
|
||||
val correspondingProperty = function.correspondingPropertySymbol
|
||||
if (correspondingProperty != null) {
|
||||
if (correspondingProperty.owner.name !in specialBridgeMethods.specialPropertyNames) return null
|
||||
} else {
|
||||
if (!canHaveSpecialBridge(function)) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
val specialMethodInfo = specialBridgeMethods.getSpecialMethodInfo(function)
|
||||
if (specialMethodInfo != null)
|
||||
return SpecialBridge(
|
||||
function, computeJvmMethod(function), specialMethodInfo.needsGenericSignature, methodInfo = specialMethodInfo
|
||||
)
|
||||
|
||||
val specialBuiltInInfo = specialBridgeMethods.getBuiltInWithDifferentJvmName(function)
|
||||
if (specialBuiltInInfo != null)
|
||||
return SpecialBridge(
|
||||
function, computeJvmMethod(function), specialBuiltInInfo.needsGenericSignature,
|
||||
isOverriding = specialBuiltInInfo.isOverriding
|
||||
)
|
||||
|
||||
for (overridden in function.overriddenSymbols) {
|
||||
val specialBridge = computeSpecialBridge(overridden.owner) ?: continue
|
||||
if (!specialBridge.needsGenericSignature) return specialBridge
|
||||
|
||||
// Compute the substituted signature.
|
||||
val erasedParameterCount = specialBridge.methodInfo?.argumentsToCheck ?: 0
|
||||
val substitutedParameterTypes = function.valueParameters.mapIndexed { index, param ->
|
||||
if (index < erasedParameterCount) context.irBuiltIns.anyNType else param.type
|
||||
}
|
||||
|
||||
val substitutedOverride = context.irFactory.buildFun {
|
||||
updateFrom(specialBridge.overridden)
|
||||
name = Name.identifier(specialBridge.signature.name)
|
||||
returnType = function.returnType
|
||||
}.apply {
|
||||
// All existing special bridges only have value parameter types.
|
||||
valueParameters = function.valueParameters.zip(substitutedParameterTypes).map { (param, type) ->
|
||||
param.copyTo(this, IrDeclarationOrigin.BRIDGE, type = type)
|
||||
}
|
||||
overriddenSymbols = listOf(specialBridge.overridden.symbol)
|
||||
parent = function.parent
|
||||
}
|
||||
|
||||
return specialBridge.copy(
|
||||
signature = computeJvmMethod(substitutedOverride),
|
||||
substitutedParameterTypes = substitutedParameterTypes,
|
||||
substitutedReturnType = function.returnType
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.caches
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.types.isStrictSubtypeOfClass
|
||||
import org.jetbrains.kotlin.ir.types.isSubtypeOfClass
|
||||
import org.jetbrains.kotlin.ir.util.functions
|
||||
import org.jetbrains.kotlin.ir.util.isFromJava
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
internal class CollectionStubComputer(val context: JvmBackendContext) {
|
||||
private class LazyStubsForCollectionClass(
|
||||
override val readOnlyClass: IrClassSymbol,
|
||||
override val mutableClass: IrClassSymbol
|
||||
) : StubsForCollectionClass {
|
||||
override val candidatesForStubs: Collection<IrSimpleFunction> by lazy {
|
||||
// Old back-end generates stubs for 'class A : C', where
|
||||
// 'C' is some "read-only collection" interface from kotlin.collections,
|
||||
// 'MC' is a corresponding "mutable collection" interface from kotlin.collections,
|
||||
// by building fake overrides for a special 'class X : A(), MC'
|
||||
// and taking fake overrides that override members from 'MC'.
|
||||
//
|
||||
// Here we are looking at this problem from a slightly different angle:
|
||||
// we select suitable member functions 'f' in 'MC' that might potentially require stubs (we are here!),
|
||||
// and then we generate stubs for functions 'f' that are not effectively overridden by members of 'A'
|
||||
// (this happens in the lowering itself).
|
||||
//
|
||||
// In order for this to be equivalent to the old back-end approach,
|
||||
// we should take 'f' in 'MC' such that any of the following conditions is true:
|
||||
// - 'f' is declared in 'MC' - that is, 'f' itself is not a fake override;
|
||||
// - 'f' is abstract and doesn't override anything from 'C'.
|
||||
//
|
||||
// NB1 it also covers default methods from JDK collection classes case
|
||||
// (since that's the only way a member function in 'MC' might be non-abstract).
|
||||
//
|
||||
// NB2 the scheme of stub method generation in the old back-end depends too much on
|
||||
// which particular declarations are present in 'MC'.
|
||||
// Some of these declarations are redundant from the stub generation point of view -
|
||||
// for example, 'kotlin.collections.MutableListIterator' contains the following (redundant) declarations:
|
||||
// override fun next(): T
|
||||
// override fun hasNext(): Boolean
|
||||
// which cause stubs for 'next' and 'hasNext' to be generated in a 'abstract class A<T> : ListIterator<T>'.
|
||||
// See https://youtrack.jetbrains.com/issue/KT-36724.
|
||||
// In the ideal world, it should be enough to check that
|
||||
// the given member function 'f' from 'MC' doesn't override anything from 'C'.
|
||||
|
||||
mutableClass.owner.functions
|
||||
.filter { memberFun ->
|
||||
!memberFun.isFakeOverride ||
|
||||
(memberFun.modality == Modality.ABSTRACT && memberFun.overriddenSymbols.none { overriddenFun ->
|
||||
overriddenFun.owner.parentAsClass.symbol == readOnlyClass
|
||||
})
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
}
|
||||
|
||||
private val preComputedStubs: Collection<StubsForCollectionClass> by lazy {
|
||||
with(context.ir.symbols) {
|
||||
listOf(
|
||||
LazyStubsForCollectionClass(collection, mutableCollection),
|
||||
LazyStubsForCollectionClass(set, mutableSet),
|
||||
LazyStubsForCollectionClass(list, mutableList),
|
||||
LazyStubsForCollectionClass(map, mutableMap),
|
||||
LazyStubsForCollectionClass(mapEntry, mutableMapEntry),
|
||||
LazyStubsForCollectionClass(iterable, mutableIterable),
|
||||
LazyStubsForCollectionClass(iterator, mutableIterator),
|
||||
LazyStubsForCollectionClass(listIterator, mutableListIterator)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val stubsCache = ConcurrentHashMap<IrClass, List<StubsForCollectionClass>>()
|
||||
|
||||
fun stubsForCollectionClasses(irClass: IrClass): List<StubsForCollectionClass> =
|
||||
stubsCache.getOrPut(irClass) {
|
||||
computeStubsForCollectionClasses(irClass)
|
||||
}
|
||||
|
||||
private fun computeStubsForCollectionClasses(irClass: IrClass): List<StubsForCollectionClass> {
|
||||
if (irClass.isFromJava()) return emptyList()
|
||||
val stubs = preComputedStubs.filter {
|
||||
irClass.symbol.isStrictSubtypeOfClass(it.readOnlyClass) && !irClass.symbol.isSubtypeOfClass(it.mutableClass)
|
||||
}
|
||||
return stubs.filter {
|
||||
stubs.none { other -> it.readOnlyClass != other.readOnlyClass && other.readOnlyClass.isSubtypeOfClass(it.readOnlyClass) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface StubsForCollectionClass {
|
||||
val readOnlyClass: IrClassSymbol
|
||||
val mutableClass: IrClassSymbol
|
||||
val candidatesForStubs: Collection<IrSimpleFunction>
|
||||
}
|
||||
+5
-118
@@ -10,10 +10,14 @@ import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny
|
||||
import org.jetbrains.kotlin.backend.common.ir.isStatic
|
||||
import org.jetbrains.kotlin.backend.common.lower.*
|
||||
import org.jetbrains.kotlin.backend.common.lower.SpecialMethodWithDefaultInfo
|
||||
import org.jetbrains.kotlin.backend.common.lower.VariableRemapper
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irNot
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.SpecialBridge
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
@@ -22,11 +26,9 @@ import org.jetbrains.kotlin.descriptors.Modality
|
||||
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.*
|
||||
@@ -35,7 +37,6 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/*
|
||||
* Generate bridge methods to fix virtual dispatch after type erasure and to adapt Kotlin collections to
|
||||
@@ -127,39 +128,6 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
|
||||
val overriddenSymbols: MutableList<IrSimpleFunctionSymbol> = mutableListOf()
|
||||
)
|
||||
|
||||
// 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.
|
||||
data class SpecialBridge(
|
||||
val overridden: IrSimpleFunction,
|
||||
val signature: Method,
|
||||
// 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,
|
||||
val isOverriding: Boolean = true,
|
||||
)
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid()
|
||||
}
|
||||
@@ -601,86 +569,6 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
|
||||
|
||||
private val IrFunction.jvmMethod: Method
|
||||
get() = context.bridgeLoweringCache.computeJvmMethod(this)
|
||||
|
||||
internal class BridgeLoweringCache(private val context: JvmBackendContext) {
|
||||
private val specialBridgeMethods = SpecialBridgeMethods(context)
|
||||
|
||||
// TODO: consider moving this cache out to the backend context and using it everywhere throughout the codegen.
|
||||
// It might benefit performance, but can lead to confusing behavior if some declarations are changed along the way.
|
||||
// For example, adding an override for a declaration whose signature is already cached can result in incorrect signature
|
||||
// if its return type is a primitive type, and the new override's return type is an object type.
|
||||
private val signatureCache = ConcurrentHashMap<IrFunctionSymbol, Method>()
|
||||
|
||||
fun computeJvmMethod(function: IrFunction): Method =
|
||||
signatureCache.getOrPut(function.symbol) { context.methodSignatureMapper.mapAsmMethod(function) }
|
||||
|
||||
private fun canHaveSpecialBridge(function: IrSimpleFunction): Boolean {
|
||||
if (function.name in specialBridgeMethods.specialMethodNames)
|
||||
return true
|
||||
// Function name could be mangled by inline class rules
|
||||
val functionName = function.name.asString()
|
||||
if (specialBridgeMethods.specialMethodNames.any { functionName.startsWith(it.asString() + "-") })
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
fun computeSpecialBridge(function: IrSimpleFunction): SpecialBridge? {
|
||||
// Optimization: do not try to compute special bridge for irrelevant methods.
|
||||
val correspondingProperty = function.correspondingPropertySymbol
|
||||
if (correspondingProperty != null) {
|
||||
if (correspondingProperty.owner.name !in specialBridgeMethods.specialPropertyNames) return null
|
||||
} else {
|
||||
if (!canHaveSpecialBridge(function)) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
val specialMethodInfo = specialBridgeMethods.getSpecialMethodInfo(function)
|
||||
if (specialMethodInfo != null)
|
||||
return SpecialBridge(
|
||||
function, computeJvmMethod(function), specialMethodInfo.needsGenericSignature, methodInfo = specialMethodInfo
|
||||
)
|
||||
|
||||
val specialBuiltInInfo = specialBridgeMethods.getBuiltInWithDifferentJvmName(function)
|
||||
if (specialBuiltInInfo != null)
|
||||
return SpecialBridge(
|
||||
function, computeJvmMethod(function), specialBuiltInInfo.needsGenericSignature,
|
||||
isOverriding = specialBuiltInInfo.isOverriding
|
||||
)
|
||||
|
||||
for (overridden in function.overriddenSymbols) {
|
||||
val specialBridge = computeSpecialBridge(overridden.owner) ?: continue
|
||||
if (!specialBridge.needsGenericSignature) return specialBridge
|
||||
|
||||
// Compute the substituted signature.
|
||||
val erasedParameterCount = specialBridge.methodInfo?.argumentsToCheck ?: 0
|
||||
val substitutedParameterTypes = function.valueParameters.mapIndexed { index, param ->
|
||||
if (index < erasedParameterCount) context.irBuiltIns.anyNType else param.type
|
||||
}
|
||||
|
||||
val substitutedOverride = context.irFactory.buildFun {
|
||||
updateFrom(specialBridge.overridden)
|
||||
name = Name.identifier(specialBridge.signature.name)
|
||||
returnType = function.returnType
|
||||
}.apply {
|
||||
// All existing special bridges only have value parameter types.
|
||||
valueParameters = function.valueParameters.zip(substitutedParameterTypes).map { (param, type) ->
|
||||
param.copyTo(this, IrDeclarationOrigin.BRIDGE, type = type)
|
||||
}
|
||||
overriddenSymbols = listOf(specialBridge.overridden.symbol)
|
||||
parent = function.parent
|
||||
}
|
||||
|
||||
return specialBridge.copy(
|
||||
signature = computeJvmMethod(substitutedOverride),
|
||||
substitutedParameterTypes = substitutedParameterTypes,
|
||||
substitutedReturnType = function.returnType
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether a fake override will resolve to an implementation in class, not an interface.
|
||||
@@ -691,4 +579,3 @@ private fun IrSimpleFunction.resolvesToClass(): Boolean {
|
||||
|
||||
private fun IrSimpleFunction.overriddenFromClass(): IrSimpleFunction? =
|
||||
overriddenSymbols.singleOrNull { !it.owner.parentAsClass.isJvmInterface }?.owner
|
||||
|
||||
|
||||
+1
-88
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.caches.StubsForCollectionClass
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
@@ -28,7 +29,6 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.TypeCheckerState
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
internal val collectionStubMethodLowering = makeIrFilePhase(
|
||||
::CollectionStubMethodLowering,
|
||||
@@ -390,90 +390,3 @@ internal class CollectionStubMethodLowering(val context: JvmBackendContext) : Cl
|
||||
private val IrClass.superClassChain: Sequence<IrClass>
|
||||
get() = generateSequence(this) { it.superClass }
|
||||
}
|
||||
|
||||
internal interface StubsForCollectionClass {
|
||||
val readOnlyClass: IrClassSymbol
|
||||
val mutableClass: IrClassSymbol
|
||||
val candidatesForStubs: Collection<IrSimpleFunction>
|
||||
}
|
||||
|
||||
internal class CollectionStubComputer(val context: JvmBackendContext) {
|
||||
|
||||
private class LazyStubsForCollectionClass(
|
||||
override val readOnlyClass: IrClassSymbol,
|
||||
override val mutableClass: IrClassSymbol
|
||||
) : StubsForCollectionClass {
|
||||
override val candidatesForStubs: Collection<IrSimpleFunction> by lazy {
|
||||
// Old back-end generates stubs for 'class A : C', where
|
||||
// 'C' is some "read-only collection" interface from kotlin.collections,
|
||||
// 'MC' is a corresponding "mutable collection" interface from kotlin.collections,
|
||||
// by building fake overrides for a special 'class X : A(), MC'
|
||||
// and taking fake overrides that override members from 'MC'.
|
||||
//
|
||||
// Here we are looking at this problem from a slightly different angle:
|
||||
// we select suitable member functions 'f' in 'MC' that might potentially require stubs (we are here!),
|
||||
// and then we generate stubs for functions 'f' that are not effectively overridden by members of 'A'
|
||||
// (this happens in the lowering itself).
|
||||
//
|
||||
// In order for this to be equivalent to the old back-end approach,
|
||||
// we should take 'f' in 'MC' such that any of the following conditions is true:
|
||||
// - 'f' is declared in 'MC' - that is, 'f' itself is not a fake override;
|
||||
// - 'f' is abstract and doesn't override anything from 'C'.
|
||||
//
|
||||
// NB1 it also covers default methods from JDK collection classes case
|
||||
// (since that's the only way a member function in 'MC' might be non-abstract).
|
||||
//
|
||||
// NB2 the scheme of stub method generation in the old back-end depends too much on
|
||||
// which particular declarations are present in 'MC'.
|
||||
// Some of these declarations are redundant from the stub generation point of view -
|
||||
// for example, 'kotlin.collections.MutableListIterator' contains the following (redundant) declarations:
|
||||
// override fun next(): T
|
||||
// override fun hasNext(): Boolean
|
||||
// which cause stubs for 'next' and 'hasNext' to be generated in a 'abstract class A<T> : ListIterator<T>'.
|
||||
// See https://youtrack.jetbrains.com/issue/KT-36724.
|
||||
// In the ideal world, it should be enough to check that
|
||||
// the given member function 'f' from 'MC' doesn't override anything from 'C'.
|
||||
|
||||
mutableClass.owner.functions
|
||||
.filter { memberFun ->
|
||||
!memberFun.isFakeOverride ||
|
||||
(memberFun.modality == Modality.ABSTRACT && memberFun.overriddenSymbols.none { overriddenFun ->
|
||||
overriddenFun.owner.parentAsClass.symbol == readOnlyClass
|
||||
})
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
}
|
||||
|
||||
private val preComputedStubs: Collection<StubsForCollectionClass> by lazy {
|
||||
with(context.ir.symbols) {
|
||||
listOf(
|
||||
LazyStubsForCollectionClass(collection, mutableCollection),
|
||||
LazyStubsForCollectionClass(set, mutableSet),
|
||||
LazyStubsForCollectionClass(list, mutableList),
|
||||
LazyStubsForCollectionClass(map, mutableMap),
|
||||
LazyStubsForCollectionClass(mapEntry, mutableMapEntry),
|
||||
LazyStubsForCollectionClass(iterable, mutableIterable),
|
||||
LazyStubsForCollectionClass(iterator, mutableIterator),
|
||||
LazyStubsForCollectionClass(listIterator, mutableListIterator)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val stubsCache = ConcurrentHashMap<IrClass, List<StubsForCollectionClass>>()
|
||||
|
||||
fun stubsForCollectionClasses(irClass: IrClass): List<StubsForCollectionClass> =
|
||||
stubsCache.getOrPut(irClass) {
|
||||
computeStubsForCollectionClasses(irClass)
|
||||
}
|
||||
|
||||
private fun computeStubsForCollectionClasses(irClass: IrClass): List<StubsForCollectionClass> {
|
||||
if (irClass.isFromJava()) return emptyList()
|
||||
val stubs = preComputedStubs.filter {
|
||||
irClass.symbol.isStrictSubtypeOfClass(it.readOnlyClass) && !irClass.symbol.isSubtypeOfClass(it.mutableClass)
|
||||
}
|
||||
return stubs.filter {
|
||||
stubs.none { other -> it.readOnlyClass != other.readOnlyClass && other.readOnlyClass.isSubtypeOfClass(it.readOnlyClass) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user