diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/LegacyKlibDependencyUtils.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/LegacyKlibDependencyUtils.kt index 3c65a184a64be..85025e1658e77 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/LegacyKlibDependencyUtils.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/LegacyKlibDependencyUtils.kt @@ -12,25 +12,6 @@ import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.uniqueName import org.jetbrains.kotlin.utils.topologicalSort -/** - * This functions allows sorting the libraries in the reverse topological order using the legacy manifest-based mechanics. - * I.e. [KLIB_PROPERTY_UNIQUE_NAME] and [KLIB_PROPERTY_DEPENDS] properties. - * - * TODO(KT-70118): This is a temporary solution for the Kotlin/Native compiler's second stage. It is going to be - * replaced by the IR linker-based dependency computation or another sustainable solution. - */ -fun Collection.legacyKlibReverseTopoSort(): List { - if (size <= 1) return toList() - - val dependencies = LegacyKlibDependencies(this) - - return topologicalSort( - nodes = this, - reportCycle = { library -> error("Cyclic dependency in library graph for: ${library.location}") }, - dependencies = { dependencies.getDependenciesFor(this@topologicalSort) } - ).reversed() -} - /** * This class allows computing the dependencies of a library using the legacy manifest-based mechanics. * I.e. [KLIB_PROPERTY_UNIQUE_NAME] and [KLIB_PROPERTY_DEPENDS] properties. diff --git a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/resolver/KotlinLibraryResolver.kt b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/resolver/KotlinLibraryResolver.kt index b40db82ae8221..1f313d96f8f2b 100644 --- a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/resolver/KotlinLibraryResolver.kt +++ b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/resolver/KotlinLibraryResolver.kt @@ -58,6 +58,9 @@ interface KotlinLibraryResolveResult { fun filterRoots(predicate: (KotlinResolvedLibrary) -> Boolean): KotlinLibraryResolveResult + /** + * Returns the list of libraries in reverse topological order. + */ fun getFullList(): List fun forEach(action: (KotlinLibrary) -> Unit) diff --git a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/resolver/impl/KotlinLibraryResolverImpl.kt b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/resolver/impl/KotlinLibraryResolverImpl.kt index 7c79955c7bc2a..6cacd6d07f1a0 100644 --- a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/resolver/impl/KotlinLibraryResolverImpl.kt +++ b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/resolver/impl/KotlinLibraryResolverImpl.kt @@ -191,24 +191,27 @@ class KotlinLibraryResolverResultImpl( private val all: List by lazy { - val result = mutableSetOf() - val queue = ArrayDeque(roots) // Use a queue for traversal - result.addAll(roots) - - while (queue.isNotEmpty()) { - val current = queue.removeFirst() - for (dependency in current.resolvedDependencies) { - if (result.add(dependency)) { - queue.add(dependency) - } + buildList { + val visiting = mutableSetOf() + val visited = mutableSetOf() + fun dfs(current: KotlinResolvedLibrary) { + if (current in visited) return + if (current in visiting) error("Cyclic dependency in library graph for: ${current.library.location}") + visiting.add(current) + current.resolvedDependencies.forEach(::dfs) + add(current) + visited.add(current) } + roots.forEach(::dfs) } - result.toList() } override fun filterRoots(predicate: (KotlinResolvedLibrary) -> Boolean) = KotlinLibraryResolverResultImpl(roots.filter(predicate)) + /** + * Returns the list of libraries in reverse topological order. + */ override fun getFullList(): List = all.map { it.library } override fun forEach(action: (KotlinLibrary) -> Unit) { diff --git a/kotlin-native/Interop/StubGenerator/src/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt b/kotlin-native/Interop/StubGenerator/src/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt index 036b3a4c75ce4..4ba05b4a9c77e 100644 --- a/kotlin-native/Interop/StubGenerator/src/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt +++ b/kotlin-native/Interop/StubGenerator/src/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt @@ -23,7 +23,6 @@ import kotlinx.cli.default import kotlinx.cli.required import kotlinx.metadata.klib.ChunkedKlibModuleFragmentWriteStrategy import kotlinx.metadata.klib.KlibMetadataVersion -import org.jetbrains.kotlin.backend.common.legacyKlibReverseTopoSort import org.jetbrains.kotlin.config.KlibAbiCompatibilityLevel import org.jetbrains.kotlin.konan.ForeignExceptionMode import org.jetbrains.kotlin.konan.TempFiles @@ -582,7 +581,7 @@ private fun resolveDependencies( noStdLib = false, noDefaultLibs = noDefaultLibs, noEndorsedLibs = noEndorsedLibs - ).getFullList().legacyKlibReverseTopoSort() + ).getFullList() validateNoLibrariesWerePassedViaCliByUniqueName(cinteropArguments.library, resolvedLibraries, resolver.logger) return resolvedLibraries } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheBuilder.kt index e8512de18d5d4..15d16c339281d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheBuilder.kt @@ -40,7 +40,6 @@ import java.nio.channels.FileLock import java.nio.channels.OverlappingFileLockException import java.nio.file.Paths import java.nio.file.StandardOpenOption -import org.jetbrains.kotlin.backend.common.legacyKlibReverseTopoSort import org.jetbrains.kotlin.konan.config.konanHome internal fun KotlinLibrary.getAllTransitiveDependencies(allLibraries: Map): List { @@ -75,7 +74,7 @@ class CacheBuilder( && (config.isFinalBinary || config.produce.isFullCache) && (autoCacheableFrom.isNotEmpty() || icEnabled) - private val allLibraries by lazy { config.resolvedLibraries.getFullList().legacyKlibReverseTopoSort() } + private val allLibraries by lazy { config.resolvedLibraries.getFullList() } private val uniqueNameToLibrary by lazy { allLibraries.associateBy { it.uniqueName } } private val uniqueNameToHash = mutableMapOf() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DependenciesTracker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DependenciesTracker.kt index 93c8d94a545a1..ce2a9460a71d5 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DependenciesTracker.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DependenciesTracker.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.backend.konan -import org.jetbrains.kotlin.backend.common.legacyKlibReverseTopoSort import org.jetbrains.kotlin.utils.atMostOne import org.jetbrains.kotlin.backend.konan.llvm.FunctionOrigin import org.jetbrains.kotlin.backend.konan.llvm.llvmSymbolOrigin @@ -172,7 +171,7 @@ internal class DependenciesTrackerImpl( usedWeakBitcodeOfFile.map { UsedLibraryFile(it, weak = true) } private val topSortedLibraries by lazy { - context.config.resolvedLibraries.getFullList().legacyKlibReverseTopoSort() + context.config.resolvedLibraries.getFullList() } private inner class CachedBitcodeDependenciesComputer { @@ -447,7 +446,7 @@ data class DependenciesTrackingResult( val allNativeLibs = DependenciesSerializer.deserialize(path, dependencies.subList(allNativeDepsIndex + 1, allCachedBitcodeDepsIndex)).map { it.libName } val allCachedBitcodeDeps = DependenciesSerializer.deserialize(path, dependencies.subList(allCachedBitcodeDepsIndex + 1, dependencies.size)) - val topSortedLibraries = config.resolvedLibraries.getFullList().legacyKlibReverseTopoSort() + val topSortedLibraries = config.resolvedLibraries.getFullList() val nativeDependenciesToLink = topSortedLibraries.mapNotNull { if (it.uniqueName in nativeLibsToLink) it else null } val allNativeDependencies = topSortedLibraries.mapNotNull { if (it.uniqueName in allNativeLibs) it else null } val allCachedBitcodeDependencies = allCachedBitcodeDeps.map { unresolvedDep -> diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/NativeSecondStageCompilationConfig.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/NativeSecondStageCompilationConfig.kt index 1f49b1062885c..07ec143fd9af0 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/NativeSecondStageCompilationConfig.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/NativeSecondStageCompilationConfig.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.backend.konan import com.google.common.base.StandardSystemProperty import com.intellij.openapi.project.Project -import org.jetbrains.kotlin.backend.common.legacyKlibReverseTopoSort import org.jetbrains.kotlin.backend.common.linkage.partial.partialLinkageConfig import org.jetbrains.kotlin.backend.konan.ir.BridgesPolicy import org.jetbrains.kotlin.backend.konan.objcexport.ObjCEntryPoints @@ -423,8 +422,11 @@ class NativeSecondStageCompilationConfig( val exportedLibraries: List get() = getExportedLibraries(configuration, resolve.resolvedLibraries, resolve.resolver.searchPathResolver, report = true) + /** + * Returns the list of libraries in reverse topological order. + */ fun librariesWithDependencies(): List { - return resolvedLibraries.filterRoots { (!it.library.isFromKotlinNativeDistribution && !purgeUserLibs) || it.library.hasDeclarationsAccessedDuringFrontendResolve }.getFullList().legacyKlibReverseTopoSort() + return resolvedLibraries.filterRoots { (!it.library.isFromKotlinNativeDistribution && !purgeUserLibs) || it.library.hasDeclarationsAccessedDuringFrontendResolve }.getFullList() } internal val externalDependenciesFile = configuration.externalDependencies?.let(::File) diff --git a/native/native.tests/klib-ir-inliner/tests/org/jetbrains/kotlin/konan/test/klib/LegacyKlibTopoSortSanityTest.kt b/native/native.tests/klib-ir-inliner/tests/org/jetbrains/kotlin/konan/test/klib/LegacyKlibTopoSortSanityTest.kt deleted file mode 100644 index 6d92c8fcc0da6..0000000000000 --- a/native/native.tests/klib-ir-inliner/tests/org/jetbrains/kotlin/konan/test/klib/LegacyKlibTopoSortSanityTest.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2010-2026 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.konan.test.klib - -import org.jetbrains.kotlin.backend.common.legacyKlibReverseTopoSort -import org.jetbrains.kotlin.konan.properties.Properties -import org.jetbrains.kotlin.konan.test.blackbox.AbstractNativeSimpleTest -import org.jetbrains.kotlin.library.KLIB_PROPERTY_UNIQUE_NAME -import org.jetbrains.kotlin.library.KlibComponent -import org.jetbrains.kotlin.library.KotlinLibrary -import org.jetbrains.kotlin.library.loader.KlibLoader -import org.jetbrains.kotlin.library.uniqueName -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Test -import java.util.* - -class LegacyKlibTopoSortSanityTest : AbstractNativeSimpleTest() { - @Test - fun test() { - val klibs = ArrayList() - - val modules = newSourceModules { - addRegularModule("a") - addRegularModule("b") { dependsOn("a") } - addRegularModule("c") { dependsOn("a") } - addRegularModule("d") { dependsOn("b", "c") } - } - - modules.compileToKlibsViaCli { _, successKlib -> - val loadingResult = KlibLoader { libraryPaths(successKlib.resultingArtifact.klibFile) }.load() - assertFalse(loadingResult.hasProblems) - assertEquals(1, loadingResult.librariesStdlibFirst.size) - klibs += loadingResult.librariesStdlibFirst[0] - } - - assertEquals(4, klibs.size) - - // Imitate stdlib. - klibs += object : KotlinLibrary { - override val location get() = error("Unsupported") - override val attributes get() = error("Unsupported") - override fun getComponent(kind: KlibComponent.Kind) = error("Unsupported") - override val libraryFile get() = error("Unsupported") - override val versions get() = error("Unsupported") - - override val manifestProperties = Properties().apply { this[KLIB_PROPERTY_UNIQUE_NAME] = "stdlib" } - } - - for (permutation in klibs.permutations()) { - val sortedKlibUniqueNames = permutation.legacyKlibReverseTopoSort().map { it.uniqueName } - assertEquals(5, sortedKlibUniqueNames.size) - assertEquals("stdlib", sortedKlibUniqueNames[0]) - assertEquals("a", sortedKlibUniqueNames[1]) - if (sortedKlibUniqueNames[2] == "b") { - assertEquals("c", sortedKlibUniqueNames[3]) - } else { - assertEquals("c", sortedKlibUniqueNames[2]) - assertEquals("b", sortedKlibUniqueNames[3]) - } - assertEquals("d", sortedKlibUniqueNames[4]) - } - } - - companion object { - private fun List.permutations(): List> { - val solutions = mutableListOf>() - permutationsRecursive(toMutableList(), 0, solutions) - return solutions - } - - private fun permutationsRecursive(input: MutableList, index: Int, answers: MutableList>) { - if (index == input.lastIndex) answers.add(input.toList()) - for (i in index..input.lastIndex) { - Collections.swap(input, index, i) - permutationsRecursive(input, index + 1, answers) - Collections.swap(input, i, index) - } - } - } -} \ No newline at end of file