[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
|
||||
|
||||
interface CirClassifier :
|
||||
sealed interface CirClassifier :
|
||||
CirDeclaration,
|
||||
CirHasAnnotations,
|
||||
CirHasName,
|
||||
|
||||
+24
@@ -28,6 +28,30 @@ interface CirClassifierIndex {
|
||||
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.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()
|
||||
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.jetbrains.kotlin.commonizer.AbstractInlineSourcesCommonizationTest.InlineSourcesCommonizationTestDsl
|
||||
import org.jetbrains.kotlin.commonizer.tree.CirTreeModule
|
||||
import org.jetbrains.kotlin.commonizer.tree.CirTreeRoot
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.library.SerializedMetadata
|
||||
|
||||
@@ -83,3 +84,13 @@ fun InlineSourceBuilder.createMetadata(builder: InlineSourceBuilder.ModuleBuilde
|
||||
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