Replace transitiveClosure (commonizer-api) with closure (tooling-core)
This commit is contained in:
committed by
Space
parent
530a3bb6ea
commit
14046b81ff
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* 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.util
|
||||
|
||||
|
||||
public inline fun <reified T> transitiveClosure(seed: T, edges: T.() -> Iterable<T>): Set<T> {
|
||||
// Fast path when initial edges are empty
|
||||
val initialEdges = seed.edges()
|
||||
if (initialEdges is Collection && initialEdges.isEmpty()) return emptySet()
|
||||
|
||||
val queue = deque<T>(initialEdges.count() * 2)
|
||||
val results = mutableSetOf<T>()
|
||||
queue.addAll(initialEdges)
|
||||
while (queue.isNotEmpty()) {
|
||||
// ArrayDeque implementation will optimize this call to 'removeFirst'
|
||||
val resolved = queue.removeAt(0)
|
||||
if (resolved != seed && results.add(resolved)) {
|
||||
queue.addAll(resolved.edges())
|
||||
}
|
||||
}
|
||||
|
||||
return results.toSet()
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal inline fun <reified T> deque(initialSize: Int): MutableList<T> {
|
||||
return if (KotlinVersion.CURRENT.isAtLeast(1, 4)) ArrayDeque(initialSize)
|
||||
else ArrayList(initialSize)
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* 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 kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class TransitiveClosureTest {
|
||||
private class Node(val value: String, val children: MutableList<Node> = mutableListOf()) {
|
||||
|
||||
override fun toString(): String {
|
||||
return value
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Node) return false
|
||||
return other.value == value
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return value.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Node.transitiveClosure() = org.jetbrains.kotlin.commonizer.util.transitiveClosure(this) { children }
|
||||
|
||||
@Test
|
||||
fun `transitiveClosure does not include root node`() {
|
||||
val closure = Node("a", mutableListOf(Node("b"), Node("c"))).transitiveClosure()
|
||||
assertEquals(setOf(Node("b"), Node("c")), closure, "Expected transitiveClosure to not include root node")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `transitiveClosure handles loop and self references`() {
|
||||
val nodeA = Node("a")
|
||||
val nodeB = Node("b")
|
||||
val nodeC = Node("c")
|
||||
val nodeD = Node("d")
|
||||
|
||||
// a -> b -> c -> d
|
||||
nodeA.children.add(nodeB)
|
||||
nodeB.children.add(nodeC)
|
||||
nodeC.children.add(nodeD)
|
||||
|
||||
// add self reference to b
|
||||
nodeB.children.add(nodeB)
|
||||
|
||||
// add loop from c -> a
|
||||
nodeC.children.add(nodeA)
|
||||
|
||||
val closure = nodeA.transitiveClosure()
|
||||
assertEquals(
|
||||
setOf(nodeB, nodeC, nodeD), closure,
|
||||
"Expected transitiveClosure to be robust against loops and self references"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,14 @@ dependencies {
|
||||
embedded(project(":kotlinx-metadata-klib")) { isTransitive = false }
|
||||
embedded(project(":kotlinx-metadata")) { isTransitive = false }
|
||||
embedded(project(":native:kotlin-klib-commonizer-api")) { isTransitive = false }
|
||||
embedded(project(":kotlin-tooling-core")) { isTransitive = false }
|
||||
|
||||
// N.B. The order of "kotlinx-metadata*" dependencies makes sense for runtime classpath
|
||||
// of the "runCommonizer" task. Please, don't mix them up.
|
||||
compileOnly(project(":kotlinx-metadata-klib")) { isTransitive = false }
|
||||
compileOnly(project(":kotlinx-metadata")) { isTransitive = false }
|
||||
compileOnly(project(":native:kotlin-klib-commonizer-api")) { isTransitive = false }
|
||||
compileOnly(project(":kotlin-tooling-core")) { isTransitive = false }
|
||||
compileOnly(project(":compiler:cli-common"))
|
||||
compileOnly(project(":compiler:ir.serialization.common"))
|
||||
compileOnly(project(":compiler:frontend"))
|
||||
@@ -40,6 +42,7 @@ dependencies {
|
||||
testImplementation(project(":kotlinx-metadata-klib")) { isTransitive = false }
|
||||
testImplementation(project(":kotlinx-metadata")) { isTransitive = false }
|
||||
testImplementation(project(":native:kotlin-klib-commonizer-api"))
|
||||
testImplementation(project(":kotlin-tooling-core"))
|
||||
testApi(intellijCore())
|
||||
}
|
||||
|
||||
|
||||
+4
-12
@@ -5,16 +5,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.core
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirClassType
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirType
|
||||
import org.jetbrains.kotlin.commonizer.cir.SimpleCirSupertypesResolver
|
||||
import org.jetbrains.kotlin.commonizer.cir.*
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirClassifierIndex
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirProvided
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.findClass
|
||||
import org.jetbrains.kotlin.commonizer.util.transitiveClosure
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.tooling.core.withClosure
|
||||
|
||||
private typealias Supertypes = List<CirType>
|
||||
|
||||
@@ -188,12 +184,8 @@ private class TypeNode(
|
||||
val supertypes: List<TypeNode>,
|
||||
var isConsumed: Boolean = false
|
||||
) {
|
||||
val allNodes: List<TypeNode> by lazy {
|
||||
val allSupertypes = transitiveClosure(this, TypeNode::supertypes)
|
||||
ArrayList<TypeNode>(allSupertypes.size + 1).also { list ->
|
||||
list.add(this)
|
||||
list.addAll(allSupertypes)
|
||||
}
|
||||
val allNodes: Set<TypeNode> by lazy {
|
||||
this.withClosure(TypeNode::supertypes)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
|
||||
+2
-2
@@ -7,10 +7,10 @@ package org.jetbrains.kotlin.commonizer.tree.deserializer
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirClassType
|
||||
import org.jetbrains.kotlin.commonizer.tree.CirTreeClass
|
||||
import org.jetbrains.kotlin.commonizer.util.transitiveClosure
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.tooling.core.withClosure
|
||||
import kotlin.test.*
|
||||
|
||||
class CirTreeClassDeserializerTest : AbstractCirTreeDeserializerTest() {
|
||||
@@ -130,7 +130,7 @@ class CirTreeClassDeserializerTest : AbstractCirTreeDeserializerTest() {
|
||||
)
|
||||
|
||||
val pkg = module.assertSinglePackage()
|
||||
val xClass = pkg.classes.flatMap { transitiveClosure(it, CirTreeClass::classes) + it }
|
||||
val xClass = pkg.classes.withClosure<CirTreeClass> { it.classes }
|
||||
.singleOrNull { it.clazz.name.toStrippedString() == "X" } ?: kotlin.test.fail("Missing class 'X'")
|
||||
val xSuperType = xClass.clazz.supertypes.singleOrNull()
|
||||
?: kotlin.test.fail("Expected single supertype for 'X'. Found ${xClass.clazz.supertypes}")
|
||||
|
||||
Reference in New Issue
Block a user