[Commonizer] Implement CirCommonClassifierId & CirCommonClassifierIdResolver
This common representation of a classifier id can later be used to render typealias invariant approximation keys. ^KT-48288
This commit is contained in:
committed by
Space
parent
a0d36d7e29
commit
763ddcddf4
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.commonizer.cir
|
package org.jetbrains.kotlin.commonizer.cir
|
||||||
|
|
||||||
interface CirClassifier :
|
sealed interface CirClassifier :
|
||||||
CirDeclaration,
|
CirDeclaration,
|
||||||
CirHasAnnotations,
|
CirHasAnnotations,
|
||||||
CirHasName,
|
CirHasName,
|
||||||
|
|||||||
+24
@@ -28,6 +28,30 @@ interface CirClassifierIndex {
|
|||||||
fun findTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId): List<CirTreeTypeAlias>
|
fun findTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId): List<CirTreeTypeAlias>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves all typealias that will either declare the given [underlyingClassifier] as their underlying type directly, *or transitively*.
|
||||||
|
* The returned List will be ordered BFS:
|
||||||
|
* Elements at the beginning of the returned List will have a 'shorter path' to the underlying classifier
|
||||||
|
*/
|
||||||
|
internal fun CirClassifierIndex.findAllTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId): List<CirTreeTypeAlias> {
|
||||||
|
/* Fast Path (no aliases) */
|
||||||
|
val firstAliases = findTypeAliasesWithUnderlyingType(underlyingClassifier)
|
||||||
|
if (firstAliases.isEmpty()) return emptyList()
|
||||||
|
|
||||||
|
val resolved = ArrayList<CirTreeTypeAlias>(firstAliases)
|
||||||
|
val enqueued = ArrayDeque<CirEntityId>(firstAliases.size * 2)
|
||||||
|
firstAliases.forEach { alias -> enqueued.add(alias.id) }
|
||||||
|
|
||||||
|
while (enqueued.isNotEmpty()) {
|
||||||
|
val next = enqueued.removeFirst()
|
||||||
|
val aliases = findTypeAliasesWithUnderlyingType(next)
|
||||||
|
resolved.addAll(aliases)
|
||||||
|
aliases.forEach { alias -> enqueued.add(alias.id) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolved
|
||||||
|
}
|
||||||
|
|
||||||
internal fun CirClassifierIndex.findClass(id: CirEntityId): CirClass? = findClassifier(id) as? CirClass
|
internal fun CirClassifierIndex.findClass(id: CirEntityId): CirClass? = findClassifier(id) as? CirClass
|
||||||
|
|
||||||
internal fun CirClassifierIndex.findTypeAlias(id: CirEntityId): CirTypeAlias? = findClassifier(id) as? CirTypeAlias
|
internal fun CirClassifierIndex.findTypeAlias(id: CirEntityId): CirTypeAlias? = findClassifier(id) as? CirTypeAlias
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* 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.commonizer.mergedtree
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
internal value class CirCommonClassifierId(val aliases: Set<CirEntityId>) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return aliases.joinToString(prefix = "(", postfix = ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(aliases.isNotEmpty())
|
||||||
|
}
|
||||||
|
}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("MoveVariableDeclarationIntoWhen")
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.commonizer.mergedtree
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.commonizer.TargetDependent
|
||||||
|
import org.jetbrains.kotlin.commonizer.cir.CirClass
|
||||||
|
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
|
||||||
|
import org.jetbrains.kotlin.commonizer.cir.CirTypeAlias
|
||||||
|
import org.jetbrains.kotlin.commonizer.forEachWithTarget
|
||||||
|
import org.jetbrains.kotlin.commonizer.mapValue
|
||||||
|
|
||||||
|
internal class CirCommonClassifierIdResolver(private val classifierIndices: TargetDependent<CirClassifierIndex>) {
|
||||||
|
fun findCommonId(id: CirEntityId): CirCommonClassifierId? {
|
||||||
|
val results = mutableSetOf<CirEntityId>()
|
||||||
|
|
||||||
|
/* Set of every classifier id that once was enqueued already */
|
||||||
|
val enqueued = mutableSetOf<CirEntityId>()
|
||||||
|
|
||||||
|
/* Actual, current queue of classifiers to resolve */
|
||||||
|
val queue = ArrayDeque<CirEntityId>()
|
||||||
|
|
||||||
|
enqueued.add(id)
|
||||||
|
queue.add(id)
|
||||||
|
|
||||||
|
while (queue.isNotEmpty()) {
|
||||||
|
val nextClassifierId = queue.removeFirst()
|
||||||
|
val foundClassifiers = classifierIndices.mapValue { index -> index.findClassifier(nextClassifierId) }
|
||||||
|
|
||||||
|
/* Classifier is available for all targets */
|
||||||
|
if (foundClassifiers.all { it != null }) {
|
||||||
|
results.add(nextClassifierId)
|
||||||
|
}
|
||||||
|
|
||||||
|
foundClassifiers.forEachWithTarget forEach@{ target, classifier ->
|
||||||
|
if (classifier == null) return@forEach
|
||||||
|
|
||||||
|
val classifierExpansionId = when (classifier) {
|
||||||
|
is CirClass -> nextClassifierId
|
||||||
|
is CirTypeAlias -> classifier.expandedType.classifierId
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enqueued.add(classifierExpansionId)) {
|
||||||
|
queue.add(classifierExpansionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
val aliases = classifierIndices[target].findAllTypeAliasesWithUnderlyingType(classifierExpansionId)
|
||||||
|
aliases.forEach { alias ->
|
||||||
|
if (enqueued.add(alias.id)) {
|
||||||
|
queue.add(alias.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return if(results.isNotEmpty()) CirCommonClassifierId(results) else null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -205,7 +205,6 @@ private class CirTreeSerializationVisitor(
|
|||||||
val fullClassName = typeAliasContext.currentPath.toString()
|
val fullClassName = typeAliasContext.currentPath.toString()
|
||||||
cirClassifier.serializeClass(typeAliasContext, fullClassName, emptyList(), emptyList(), emptyList(), emptyList())
|
cirClassifier.serializeClass(typeAliasContext, fullClassName, emptyList(), emptyList(), emptyList(), emptyList())
|
||||||
}
|
}
|
||||||
else -> error("Unexpected CIR classifier: ${cirClassifier::class.java}, $cirClassifier")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+197
@@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
* 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.commonizer
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
|
||||||
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirClassifierIndex
|
||||||
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirCommonClassifierIdResolver
|
||||||
|
import org.jetbrains.kotlin.commonizer.tree.CirTreeRoot
|
||||||
|
import org.jetbrains.kotlin.commonizer.utils.createCirTreeRootFromSourceCode
|
||||||
|
|
||||||
|
class CirCommonClassifierIdResolverTest : AbstractInlineSourcesCommonizationTest() {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Platform A: X -> A
|
||||||
|
Platform B: X -> B
|
||||||
|
|
||||||
|
Expected: X
|
||||||
|
*/
|
||||||
|
fun `test sample 0`() {
|
||||||
|
val resolver = createCommonClassifierIdResolver(
|
||||||
|
createCirTreeRootFromSourceCode(
|
||||||
|
"""
|
||||||
|
class A
|
||||||
|
typealias X = A
|
||||||
|
""".trimIndent()
|
||||||
|
),
|
||||||
|
createCirTreeRootFromSourceCode(
|
||||||
|
"""
|
||||||
|
class B
|
||||||
|
typealias X = B
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(setOf("X"), resolver.findCommonId("A"))
|
||||||
|
assertEquals(setOf("X"), resolver.findCommonId("B"))
|
||||||
|
assertEquals(setOf("X"), resolver.findCommonId("X"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Platform A: A -> B -> C
|
||||||
|
Platform B: C -> D -> E
|
||||||
|
|
||||||
|
Expected: C
|
||||||
|
*/
|
||||||
|
fun `test sample 1`() {
|
||||||
|
val resolver = createCommonClassifierIdResolver(
|
||||||
|
createCirTreeRootFromSourceCode(
|
||||||
|
"""
|
||||||
|
class C
|
||||||
|
typealias B = C
|
||||||
|
typealias A = B
|
||||||
|
""".trimIndent()
|
||||||
|
),
|
||||||
|
createCirTreeRootFromSourceCode(
|
||||||
|
"""
|
||||||
|
class E
|
||||||
|
typealias D = E
|
||||||
|
typealias C = D
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(setOf("C"), resolver.findCommonId("A"))
|
||||||
|
assertEquals(setOf("C"), resolver.findCommonId("B"))
|
||||||
|
assertEquals(setOf("C"), resolver.findCommonId("C"))
|
||||||
|
assertEquals(setOf("C"), resolver.findCommonId("D"))
|
||||||
|
assertEquals(setOf("C"), resolver.findCommonId("E"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Platform A: A -> B -> C
|
||||||
|
Platform B: A -> X -> B -> C
|
||||||
|
|
||||||
|
Expected: A, B, C
|
||||||
|
*/
|
||||||
|
fun `test sample 2`() {
|
||||||
|
val resolver = createCommonClassifierIdResolver(
|
||||||
|
createCirTreeRootFromSourceCode(
|
||||||
|
"""
|
||||||
|
class C
|
||||||
|
typealias B = C
|
||||||
|
typealias A = B
|
||||||
|
""".trimIndent()
|
||||||
|
),
|
||||||
|
createCirTreeRootFromSourceCode(
|
||||||
|
"""
|
||||||
|
class C
|
||||||
|
typealias B = C
|
||||||
|
typealias X = B
|
||||||
|
typealias A = X
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(setOf("A", "B", "C"), resolver.findCommonId("A"))
|
||||||
|
assertEquals(setOf("A", "B", "C"), resolver.findCommonId("B"))
|
||||||
|
assertEquals(setOf("A", "B", "C"), resolver.findCommonId("C"))
|
||||||
|
assertEquals(setOf("A", "B", "C"), resolver.findCommonId("X"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `test sample 3`() {
|
||||||
|
val resolver = createCommonClassifierIdResolver(
|
||||||
|
createCirTreeRootFromSourceCode("class A"),
|
||||||
|
createCirTreeRootFromSourceCode("class B")
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(emptySet<String>(), resolver.findCommonId("A"))
|
||||||
|
assertEquals(emptySet<String>(), resolver.findCommonId("B"))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Platform A: A -> B -> C
|
||||||
|
Platform B: A -> B -> C
|
||||||
|
Platform C: A -> B
|
||||||
|
|
||||||
|
Expected: A, B
|
||||||
|
*/
|
||||||
|
fun `test sample 4`() {
|
||||||
|
val resolver = createCommonClassifierIdResolver(
|
||||||
|
createCirTreeRootFromSourceCode(
|
||||||
|
"""
|
||||||
|
class C
|
||||||
|
typealias B = C
|
||||||
|
typealias A = B
|
||||||
|
""".trimIndent()
|
||||||
|
),
|
||||||
|
createCirTreeRootFromSourceCode(
|
||||||
|
"""
|
||||||
|
class C
|
||||||
|
typealias B = C
|
||||||
|
typealias A = B
|
||||||
|
""".trimIndent()
|
||||||
|
),
|
||||||
|
createCirTreeRootFromSourceCode(
|
||||||
|
"""
|
||||||
|
class B
|
||||||
|
typealias A = B
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(setOf("A", "B"), resolver.findCommonId("A"))
|
||||||
|
assertEquals(setOf("A", "B"), resolver.findCommonId("B"))
|
||||||
|
assertEquals(setOf("A", "B"), resolver.findCommonId("C"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Rather esoteric case!
|
||||||
|
Platform A: A -> B -> C D -> E
|
||||||
|
Platform B: B -> C - D
|
||||||
|
|
||||||
|
Expected: B, C, D
|
||||||
|
*/
|
||||||
|
fun `test sample 5`() {
|
||||||
|
val rootA = createCirTreeRootFromSourceCode(
|
||||||
|
"""
|
||||||
|
class C
|
||||||
|
typealias B = C
|
||||||
|
typealias A = B
|
||||||
|
|
||||||
|
class E
|
||||||
|
typealias D = E
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
val rootB = createCirTreeRootFromSourceCode(
|
||||||
|
"""
|
||||||
|
class D
|
||||||
|
typealias C = D
|
||||||
|
typealias B = C
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
val resolver = createCommonClassifierIdResolver(rootB, rootA)
|
||||||
|
assertEquals(setOf("B", "C", "D"), resolver.findCommonId("B"))
|
||||||
|
assertEquals(setOf("B", "C", "D"), resolver.findCommonId("C"))
|
||||||
|
assertEquals(setOf("B", "C", "D"), resolver.findCommonId("D"))
|
||||||
|
assertEquals(setOf("B", "C", "D"), resolver.findCommonId("A"))
|
||||||
|
assertEquals(setOf("B", "C", "D"), resolver.findCommonId("E"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createCommonClassifierIdResolver(vararg root: CirTreeRoot): CirCommonClassifierIdResolver {
|
||||||
|
return CirCommonClassifierIdResolver(
|
||||||
|
TargetDependent(root.withIndex().associate { (index, root) -> LeafCommonizerTarget(index.toString()) to root })
|
||||||
|
.mapValue(::CirClassifierIndex)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CirCommonClassifierIdResolver.findCommonId(id: String): Set<String> =
|
||||||
|
findCommonId(CirEntityId.create(id))?.aliases.orEmpty().map { it.toQualifiedNameString() }.toSet()
|
||||||
|
|
||||||
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.commonizer.utils
|
|||||||
import org.intellij.lang.annotations.Language
|
import org.intellij.lang.annotations.Language
|
||||||
import org.jetbrains.kotlin.commonizer.AbstractInlineSourcesCommonizationTest.InlineSourcesCommonizationTestDsl
|
import org.jetbrains.kotlin.commonizer.AbstractInlineSourcesCommonizationTest.InlineSourcesCommonizationTestDsl
|
||||||
import org.jetbrains.kotlin.commonizer.tree.CirTreeModule
|
import org.jetbrains.kotlin.commonizer.tree.CirTreeModule
|
||||||
|
import org.jetbrains.kotlin.commonizer.tree.CirTreeRoot
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.library.SerializedMetadata
|
import org.jetbrains.kotlin.library.SerializedMetadata
|
||||||
|
|
||||||
@@ -83,3 +84,13 @@ fun InlineSourceBuilder.createMetadata(builder: InlineSourceBuilder.ModuleBuilde
|
|||||||
return createMetadata(createModule(builder))
|
return createMetadata(createModule(builder))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InlineSourceBuilder.ModuleBuilderDsl
|
||||||
|
fun InlineSourceBuilder.createCirTreeRoot(builder: InlineSourceBuilder.ModuleBuilder.() -> Unit): CirTreeRoot {
|
||||||
|
return CirTreeRoot(listOf(createCirTree(builder)))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@InlineSourceBuilder.ModuleBuilderDsl
|
||||||
|
fun InlineSourceBuilder.createCirTreeRootFromSourceCode(@Language("kotlin") sourceCode: String): CirTreeRoot {
|
||||||
|
return CirTreeRoot(listOf(createCirTreeFromSourceCode(sourceCode)))
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user