JVM_IR emulate JVM stub generation scheme
KT-42114 KT-42115
This commit is contained in:
+55
-51
@@ -148,7 +148,8 @@ internal class CollectionStubMethodLowering(val context: JvmBackendContext) : Cl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrSimpleFunction.toJvmSignature(): String = collectionStubComputer.getJvmSignature(this)
|
private fun IrSimpleFunction.toJvmSignature(): String =
|
||||||
|
context.methodSignatureMapper.mapAsmMethod(this).toString()
|
||||||
|
|
||||||
private fun createStubMethod(
|
private fun createStubMethod(
|
||||||
function: IrSimpleFunction,
|
function: IrSimpleFunction,
|
||||||
@@ -299,10 +300,10 @@ internal class CollectionStubMethodLowering(val context: JvmBackendContext) : Cl
|
|||||||
|
|
||||||
// Compute stubs that should be generated, compare based on signature
|
// Compute stubs that should be generated, compare based on signature
|
||||||
private fun generateRelevantStubMethods(irClass: IrClass): List<IrSimpleFunction> {
|
private fun generateRelevantStubMethods(irClass: IrClass): List<IrSimpleFunction> {
|
||||||
fun createStubFuns(stubs: CollectionStubComputer.StubsForCollectionClass): List<IrSimpleFunction> {
|
fun createStubFuns(stubs: StubsForCollectionClass): List<IrSimpleFunction> {
|
||||||
val (readOnlyClass, mutableClass, mutableOnlyMethods) = stubs
|
val (readOnlyClass, mutableClass, candidatesForStubs) = stubs
|
||||||
val substitutionMap = computeSubstitutionMap(readOnlyClass.owner, mutableClass.owner, irClass)
|
val substitutionMap = computeSubstitutionMap(readOnlyClass.owner, mutableClass.owner, irClass)
|
||||||
return mutableOnlyMethods.map { function ->
|
return candidatesForStubs.map { function ->
|
||||||
createStubMethod(function, irClass, substitutionMap)
|
createStubMethod(function, irClass, substitutionMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,57 +345,60 @@ internal class CollectionStubMethodLowering(val context: JvmBackendContext) : Cl
|
|||||||
get() = generateSequence(this) { it.superClass }
|
get() = generateSequence(this) { it.superClass }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class CollectionStubComputer(val context: JvmBackendContext) {
|
|
||||||
fun getJvmSignature(irFunction: IrSimpleFunction): String = context.methodSignatureMapper.mapAsmMethod(irFunction).toString()
|
|
||||||
|
|
||||||
inner class StubsForCollectionClass(
|
internal class StubsForCollectionClass(
|
||||||
val readOnlyClass: IrClassSymbol,
|
val readOnlyClass: IrClassSymbol,
|
||||||
val mutableClass: IrClassSymbol
|
val mutableClass: IrClassSymbol
|
||||||
) {
|
) {
|
||||||
|
// Old back-end generates stubs for 'class A : C', where
|
||||||
val mutableOnlyMethods: Collection<IrSimpleFunction> by lazy {
|
// 'C' is some "read-only collection" interface from kotlin.collections,
|
||||||
val readOnlyMethodSignatures =
|
// 'MC' is a corresponding "mutable collection" interface from kotlin.collections,
|
||||||
readOnlyClass.functions
|
// by building fake overrides for a special 'class X : A(), MC'
|
||||||
.filter { !it.owner.isSpecialCaseStubForOldBackend() }
|
// and taking fake overrides that override members from 'MC'.
|
||||||
.map { getJvmSignature(it.owner) }
|
//
|
||||||
.toHashSet()
|
// Here we are looking at this problem from a slightly different angle:
|
||||||
mutableClass.functions
|
// we select suitable member functions 'f' in 'MC' that might potentially require stubs (we are here!),
|
||||||
.map { it.owner }
|
// and then we generate stubs for functions 'f' that are not effectively overridden by members of 'A'
|
||||||
.filter { getJvmSignature(it) !in readOnlyMethodSignatures }
|
// (this happens in the lowering itself).
|
||||||
.toHashSet()
|
//
|
||||||
}
|
// 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:
|
||||||
operator fun component1() = readOnlyClass
|
// - 'f' is declared in 'MC' - that is, 'f' itself is not a fake override;
|
||||||
operator fun component2() = mutableClass
|
// - 'f' is abstract and doesn't override anything from 'C'.
|
||||||
operator fun component3() = mutableOnlyMethods
|
//
|
||||||
}
|
// 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).
|
||||||
// Preserve old backend's logic to generate stubs for special cases where a mutable method
|
//
|
||||||
// has the same JVM signature as the immutable method. See KT-36724 for more details.
|
// NB2 the scheme of stub method generation in the old back-end depends too much on
|
||||||
private fun IrSimpleFunction.isSpecialCaseStubForOldBackend(): Boolean {
|
// which particular declarations are present in 'MC'.
|
||||||
return when (name.asString()) {
|
// Some of these declarations are redundant from the stub generation point of view -
|
||||||
"iterator" -> {
|
// for example, 'kotlin.collections.MutableListIterator' contains the following (redundant) declarations:
|
||||||
val parentClassSymbol = parentAsClass.symbol
|
// override fun next(): T
|
||||||
// Due to the specific way Kotlin built-in collection classes are written,
|
// override fun hasNext(): Boolean
|
||||||
// old JVM back-end generates throwing method stubs for the following abstract member functions:
|
// which cause stubs for 'next' and 'hasNext' to be generated in a 'abstract class A<T> : ListIterator<T>'.
|
||||||
// Iterable<T>#iterator(): Iterator<T>
|
// See https://youtrack.jetbrains.com/issue/KT-36724.
|
||||||
// Collection<E>#iterator(): Iterator<E>
|
// In the ideal world, it should be enough to check that
|
||||||
// Set<E>#iterator(): Iterator<E>
|
// the given member function 'f' from 'MC' doesn't override anything from 'C'.
|
||||||
// This happens because MutableIterable, MutableCollection, and MutableSet contain explicit override
|
val candidatesForStubs: Collection<IrSimpleFunction> by lazy {
|
||||||
// override fun iterator(): MutableIterator<E>
|
mutableClass.functions
|
||||||
// and MutableList doesn't, which makes corresponding member of MutableList a FAKE_OVERRIDE.
|
.map { it.owner }
|
||||||
with(context.ir.symbols) {
|
.filter { memberFun ->
|
||||||
// Note that here we are looking at a read-only collection member.
|
!memberFun.isFakeOverride ||
|
||||||
parentClassSymbol == iterable || parentClassSymbol == collection || parentClassSymbol == set
|
(memberFun.modality == Modality.ABSTRACT && memberFun.overriddenSymbols.none { overriddenFun ->
|
||||||
}
|
overriddenFun.owner.parentAsClass.symbol == readOnlyClass
|
||||||
|
})
|
||||||
}
|
}
|
||||||
"listIterator", "subList" ->
|
.toHashSet()
|
||||||
true
|
|
||||||
else ->
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator fun component1() = readOnlyClass
|
||||||
|
operator fun component2() = mutableClass
|
||||||
|
operator fun component3() = candidatesForStubs
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class CollectionStubComputer(val context: JvmBackendContext) {
|
||||||
|
|
||||||
private val preComputedStubs: Collection<StubsForCollectionClass> by lazy {
|
private val preComputedStubs: Collection<StubsForCollectionClass> by lazy {
|
||||||
with(context.ir.symbols) {
|
with(context.ir.symbols) {
|
||||||
listOf(
|
listOf(
|
||||||
|
|||||||
+3
-2
@@ -57,7 +57,6 @@ public abstract class AbstractList {
|
|||||||
public method remove(p0: int): java.lang.String
|
public method remove(p0: int): java.lang.String
|
||||||
public method remove(p0: java.lang.Object): boolean
|
public method remove(p0: java.lang.Object): boolean
|
||||||
public method removeAll(p0: java.util.Collection): boolean
|
public method removeAll(p0: java.util.Collection): boolean
|
||||||
public method removeIf(p0: java.util.function.Predicate): boolean
|
|
||||||
public method replaceAll(p0: java.util.function.UnaryOperator): void
|
public method replaceAll(p0: java.util.function.UnaryOperator): void
|
||||||
public method retainAll(p0: java.util.Collection): boolean
|
public method retainAll(p0: java.util.Collection): boolean
|
||||||
public synthetic bridge method set(p0: int, p1: java.lang.Object): java.lang.Object
|
public synthetic bridge method set(p0: int, p1: java.lang.Object): java.lang.Object
|
||||||
@@ -75,6 +74,9 @@ public abstract class AbstractListIterator {
|
|||||||
public method <init>(): void
|
public method <init>(): void
|
||||||
public synthetic bridge method add(p0: java.lang.Object): void
|
public synthetic bridge method add(p0: java.lang.Object): void
|
||||||
public method add(p0: java.lang.String): void
|
public method add(p0: java.lang.String): void
|
||||||
|
public method hasNext(): boolean
|
||||||
|
public synthetic bridge method next(): java.lang.Object
|
||||||
|
public method next(): java.lang.String
|
||||||
public method remove(): void
|
public method remove(): void
|
||||||
public synthetic bridge method set(p0: java.lang.Object): void
|
public synthetic bridge method set(p0: java.lang.Object): void
|
||||||
public method set(p0: java.lang.String): void
|
public method set(p0: java.lang.String): void
|
||||||
@@ -145,7 +147,6 @@ public abstract class AbstractSet {
|
|||||||
public method iterator(): java.util.Iterator
|
public method iterator(): java.util.Iterator
|
||||||
public method remove(p0: java.lang.Object): boolean
|
public method remove(p0: java.lang.Object): boolean
|
||||||
public method removeAll(p0: java.util.Collection): boolean
|
public method removeAll(p0: java.util.Collection): boolean
|
||||||
public method removeIf(p0: java.util.function.Predicate): boolean
|
|
||||||
public method retainAll(p0: java.util.Collection): boolean
|
public method retainAll(p0: java.util.Collection): boolean
|
||||||
public bridge final method size(): int
|
public bridge final method size(): int
|
||||||
public method toArray(): java.lang.Object[]
|
public method toArray(): java.lang.Object[]
|
||||||
|
|||||||
Reference in New Issue
Block a user