mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[Kotlin] Don't check manifest properties in Kotlin JS stdlib detector
Use name matching as it's done for JVM stdlib. Allows removing org.jetbrains.kotlin.utils.LibraryUtils in kotlin.git. Changes: — Remove LibraryUtils usage and supporting code — Add tests for watershed versions — Allow requesting KLIBs from MavenDependencyUtil for the new tests — Workaround: extract VFGist used for KotlinLibraryKind from the service A comment about the last workaround. StdlibJsDetectorFacilityTest revealed a Project leakage. The project is captured by a coroutine context and is hard referenced through ComponentManager by an application-level virtual file gist cleanup task. The exact reference chain is not clear, but removing the gist construction from the service prevents the leakage. KTIJ-27969 GitOrigin-RevId: 7d6dbe93f4aa3125f5d204a81babcf8483874cb4
This commit is contained in:
committed by
intellij-monorepo-bot
parent
934691271b
commit
aae5c6cd9f
+18
-2
@@ -14,6 +14,7 @@ import com.intellij.openapi.roots.libraries.ui.OrderRoot;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.idea.maven.utils.library.RepositoryLibraryProperties;
|
||||
import org.jetbrains.jps.model.library.JpsMavenRepositoryLibraryDescriptor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -57,7 +58,7 @@ public final class MavenDependencyUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Maven library to {@code model}
|
||||
* Adds a Maven library with the default packaging (JAR) to {@code model}
|
||||
* @param additionalRepositories additional Maven repositories where the artifacts should be searched (in addition to repositories
|
||||
* configured in intellij project)
|
||||
*/
|
||||
@@ -66,9 +67,24 @@ public final class MavenDependencyUtil {
|
||||
boolean includeTransitiveDependencies,
|
||||
DependencyScope dependencyScope,
|
||||
List<RemoteRepositoryDescription> additionalRepositories) {
|
||||
addFromMaven(model, mavenCoordinates, includeTransitiveDependencies, dependencyScope, additionalRepositories,
|
||||
JpsMavenRepositoryLibraryDescriptor.DEFAULT_PACKAGING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Maven library to {@code model}
|
||||
* @param packaging artifact packaging matching {@link org.jetbrains.idea.maven.aether.ArtifactKind} of the requested library
|
||||
*/
|
||||
public static void addFromMaven(@NotNull ModifiableRootModel model,
|
||||
String mavenCoordinates,
|
||||
boolean includeTransitiveDependencies,
|
||||
DependencyScope dependencyScope,
|
||||
List<RemoteRepositoryDescription> additionalRepositories,
|
||||
@NotNull String packaging) {
|
||||
List<RemoteRepositoryDescription> remoteRepositoryDescriptions = ContainerUtil.concat(getRemoteRepositoryDescriptions(),
|
||||
additionalRepositories);
|
||||
RepositoryLibraryProperties libraryProperties = new RepositoryLibraryProperties(mavenCoordinates, includeTransitiveDependencies);
|
||||
RepositoryLibraryProperties libraryProperties =
|
||||
new RepositoryLibraryProperties(mavenCoordinates, packaging, includeTransitiveDependencies);
|
||||
Collection<OrderRoot> roots =
|
||||
JarRepositoryManager.loadDependenciesModal(model.getProject(), libraryProperties, false, false, null, remoteRepositoryDescriptions);
|
||||
LibraryTable.ModifiableModel tableModel = model.getModuleLibraryTable().getModifiableModel();
|
||||
|
||||
+2
-14
@@ -2,20 +2,16 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.base.platforms
|
||||
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.kotlin.idea.base.plugin.artifacts.KotlinArtifactNames
|
||||
import org.jetbrains.kotlin.utils.LibraryUtils
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import java.io.File
|
||||
import java.util.regex.Pattern
|
||||
|
||||
@ApiStatus.Internal
|
||||
object KotlinJavaScriptStdlibDetectorFacility : StdlibDetectorFacility() {
|
||||
private val IS_JS_LIBRARY_STD_LIB = Key.create<Boolean>("IS_JS_LIBRARY_STD_LIB")
|
||||
private val KOTLIN_JS_LIBRARY_KLIB_PATTERN = Pattern.compile("kotlin-stdlib-js.*\\.klib")
|
||||
|
||||
override val supportedLibraryKind: KotlinLibraryKind
|
||||
@@ -23,6 +19,7 @@ object KotlinJavaScriptStdlibDetectorFacility : StdlibDetectorFacility() {
|
||||
|
||||
override fun getStdlibJar(roots: List<VirtualFile>): VirtualFile? {
|
||||
for (root in roots) {
|
||||
// KLIBs fall under the JAR file system as well
|
||||
if (root.fileSystem.protocol !== StandardFileSystems.JAR_PROTOCOL) continue
|
||||
|
||||
val name = root.url.substringBefore("!/").substringAfterLast('/')
|
||||
@@ -33,16 +30,7 @@ object KotlinJavaScriptStdlibDetectorFacility : StdlibDetectorFacility() {
|
||||
|| KOTLIN_JS_LIBRARY_KLIB_PATTERN.matcher(name).matches()
|
||||
) {
|
||||
val jar = VfsUtilCore.getVirtualFileForJar(root) ?: continue
|
||||
var isJSStdLib = jar.getUserData(IS_JS_LIBRARY_STD_LIB)
|
||||
if (isJSStdLib == null) {
|
||||
isJSStdLib = KOTLIN_JS_LIBRARY_KLIB_PATTERN.matcher(name).matches()
|
||||
|| LibraryUtils.isKotlinJavascriptStdLibrary(File(jar.path))
|
||||
jar.putUserData(IS_JS_LIBRARY_STD_LIB, isJSStdLib)
|
||||
}
|
||||
|
||||
if (isJSStdLib) {
|
||||
return jar
|
||||
}
|
||||
return jar
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+37
-39
@@ -25,6 +25,7 @@ import com.intellij.util.indexing.roots.kind.LibraryOrigin
|
||||
import com.intellij.util.io.*
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinBuiltInFileType
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.base.platforms.LibraryEffectiveKindProvider.LibraryKindScanner
|
||||
import org.jetbrains.kotlin.platform.idePlatformKind
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DOT_METADATA_FILE_EXTENSION
|
||||
@@ -33,42 +34,6 @@ import java.io.DataOutput
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
class LibraryEffectiveKindProvider(private val project: Project) {
|
||||
private companion object {
|
||||
val LIBRARY_KIND_KEY: Key<PersistentLibraryKind<*>> = Key.create("LibraryEffectiveKind")
|
||||
val CLASS_ROOTS_KEY: Key<Array<VirtualFile>> = Key.create("LibraryClassRoots")
|
||||
val NEEDS_TO_BE_CLARIFIED_KIND: UnknownLibraryKind = UnknownLibraryKind.getOrCreate("Needs to be clarified")
|
||||
|
||||
@JvmStatic
|
||||
private val KOTLIN_LIBRARY_KIND_GIST: VirtualFileGist<PersistentLibraryKind<*>> = GistManager.getInstance().newVirtualFileGist(
|
||||
"kotlin-library-kind",
|
||||
1,
|
||||
object : DataExternalizer<PersistentLibraryKind<*>> {
|
||||
override fun save(out: DataOutput, value: PersistentLibraryKind<*>) {
|
||||
val kindId = value.kindId
|
||||
IOUtil.writeString(kindId, out)
|
||||
}
|
||||
|
||||
override fun read(`in`: DataInput): PersistentLibraryKind<*>? =
|
||||
when (val kindId = IOUtil.readString(`in`)) {
|
||||
// as KotlinJvmEffectiveLibraryKind is a fake library kind
|
||||
KotlinJvmEffectiveLibraryKind.kindId -> KotlinJvmEffectiveLibraryKind
|
||||
else -> LibraryKindRegistry.getInstance().findKindById(kindId) as? PersistentLibraryKind<*>
|
||||
}
|
||||
}
|
||||
) { _, file ->
|
||||
val classRoots = file.getUserData(CLASS_ROOTS_KEY) ?: arrayOf(file)
|
||||
LibraryKindScanner.runScannerOutsideScanningSession(classRoots)
|
||||
var platformKind: PersistentLibraryKind<*>? = file.getUserData(LIBRARY_KIND_KEY)
|
||||
if (platformKind == NEEDS_TO_BE_CLARIFIED_KIND) {
|
||||
val matchingPlatformKind = IdePlatformKindProjectStructure.getLibraryPlatformKind(file)
|
||||
?: JvmPlatforms.defaultJvmPlatform.idePlatformKind
|
||||
platformKind = IdePlatformKindProjectStructure.getLibraryKind(matchingPlatformKind)
|
||||
}
|
||||
platformKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private enum class KnownLibraryKindForIndex {
|
||||
COMMON, JS, UNKNOWN
|
||||
}
|
||||
@@ -97,8 +62,7 @@ class LibraryEffectiveKindProvider(private val project: Project) {
|
||||
val classRoot = classRoots.firstOrNull() ?: return null
|
||||
|
||||
val platformKind: PersistentLibraryKind<*>? =
|
||||
classRoot.getUserData(LIBRARY_KIND_KEY)?.takeIf { it != NEEDS_TO_BE_CLARIFIED_KIND } ?:
|
||||
findKind(classRoots)?.let {
|
||||
classRoot.getUserData(LIBRARY_KIND_KEY)?.takeIf { it != NEEDS_TO_BE_CLARIFIED_KIND } ?: findKind(classRoots)?.let {
|
||||
classRoot.putUserData(LIBRARY_KIND_KEY, it)
|
||||
it
|
||||
}
|
||||
@@ -122,7 +86,7 @@ class LibraryEffectiveKindProvider(private val project: Project) {
|
||||
scannerVisitor.result = null
|
||||
VfsUtil.visitChildrenRecursively(classRoot, object : VirtualFileVisitor<Any?>() {
|
||||
override fun visitFileEx(file: VirtualFile): Result =
|
||||
if (visitFile(file)) CONTINUE else skipTo(classRoot)
|
||||
if (visitFile(file)) CONTINUE else skipTo(classRoot)
|
||||
|
||||
override fun visitFile(file: VirtualFile): Boolean {
|
||||
ProgressManager.checkCanceled()
|
||||
@@ -210,3 +174,37 @@ class LibraryEffectiveKindProvider(private val project: Project) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private val LIBRARY_KIND_KEY: Key<PersistentLibraryKind<*>> = Key.create("LibraryEffectiveKind")
|
||||
private val CLASS_ROOTS_KEY: Key<Array<VirtualFile>> = Key.create("LibraryClassRoots")
|
||||
private val NEEDS_TO_BE_CLARIFIED_KIND: UnknownLibraryKind = UnknownLibraryKind.getOrCreate("Needs to be clarified")
|
||||
|
||||
private val KOTLIN_LIBRARY_KIND_GIST: VirtualFileGist<PersistentLibraryKind<*>> by lazy {
|
||||
GistManager.getInstance().newVirtualFileGist(
|
||||
"kotlin-library-kind",
|
||||
1,
|
||||
object : DataExternalizer<PersistentLibraryKind<*>> {
|
||||
override fun save(out: DataOutput, value: PersistentLibraryKind<*>) {
|
||||
val kindId = value.kindId
|
||||
IOUtil.writeString(kindId, out)
|
||||
}
|
||||
|
||||
override fun read(`in`: DataInput): PersistentLibraryKind<*>? =
|
||||
when (val kindId = IOUtil.readString(`in`)) {
|
||||
// as KotlinJvmEffectiveLibraryKind is a fake library kind
|
||||
KotlinJvmEffectiveLibraryKind.kindId -> KotlinJvmEffectiveLibraryKind
|
||||
else -> LibraryKindRegistry.getInstance().findKindById(kindId) as? PersistentLibraryKind<*>
|
||||
}
|
||||
}
|
||||
) { _, file ->
|
||||
val classRoots = file.getUserData(CLASS_ROOTS_KEY) ?: arrayOf(file)
|
||||
LibraryKindScanner.runScannerOutsideScanningSession(classRoots)
|
||||
var platformKind: PersistentLibraryKind<*>? = file.getUserData(LIBRARY_KIND_KEY)
|
||||
if (platformKind == NEEDS_TO_BE_CLARIFIED_KIND) {
|
||||
val matchingPlatformKind = IdePlatformKindProjectStructure.getLibraryPlatformKind(file)
|
||||
?: JvmPlatforms.defaultJvmPlatform.idePlatformKind
|
||||
platformKind = IdePlatformKindProjectStructure.getLibraryKind(matchingPlatformKind)
|
||||
}
|
||||
platformKind
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,5 +140,10 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.navbar.testFramework" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.execution.dashboard" scope="TEST" />
|
||||
<orderEntry type="module" module-name="kotlin.code-insight.intentions.shared" scope="TEST" />
|
||||
<orderEntry type="library" scope="TEST" name="JUnit5" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="JUnit5Jupiter" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="JUnit5Launcher" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="JUnit5Params" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.testFramework.junit5" />
|
||||
</component>
|
||||
</module>
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
package org.jetbrains.kotlin.idea.configuration
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.openapi.roots.OrderEnumerator
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.testFramework.fixtures.MavenDependencyUtil
|
||||
import com.intellij.testFramework.junit5.TestApplication
|
||||
import com.intellij.testFramework.junit5.fixture.TestFixture
|
||||
import com.intellij.testFramework.junit5.fixture.moduleFixture
|
||||
import com.intellij.testFramework.junit5.fixture.projectFixture
|
||||
import org.jetbrains.kotlin.idea.base.platforms.KotlinJavaScriptStdlibDetectorFacility
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@TestApplication
|
||||
class StdlibJsDetectorFacilityTest {
|
||||
companion object {
|
||||
// the project is reused
|
||||
val project: TestFixture<Project> = projectFixture()
|
||||
}
|
||||
|
||||
// the module is recreated after each test to clean up module libraries
|
||||
val module: TestFixture<Module> = project.moduleFixture()
|
||||
|
||||
@Test
|
||||
fun testJsStdlib_1_0_7() {
|
||||
doTest("org.jetbrains.kotlin:kotlin-js-library:1.0.7", isJsStdlib = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testJsStdlib_1_1_0() {
|
||||
doTest("org.jetbrains.kotlin:kotlin-stdlib-js:1.1.0", isJsStdlib = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testJsStdlib_1_3_0() {
|
||||
doTest("org.jetbrains.kotlin:kotlin-stdlib-js:1.3.0", isJsStdlib = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testJsStdlib_1_6_20() {
|
||||
doTest("org.jetbrains.kotlin:kotlin-stdlib-js:1.6.20", isJsStdlib = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testJsStdlib_1_9_24() {
|
||||
doTest("org.jetbrains.kotlin:kotlin-stdlib-js:1.9.24", isJsStdlib = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testJsStdlib_2_0_0() {
|
||||
doTest("org.jetbrains.kotlin:kotlin-stdlib-js:2.0.0", isJsStdlib = true, packaging = "klib")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNonStdlib() {
|
||||
doTest("org.jetbrains:annotations:24.0.1", isJsStdlib = false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNonJsStdlib() {
|
||||
doTest("org.jetbrains.kotlin:kotlin-stdlib:1.9.24", isJsStdlib = false)
|
||||
}
|
||||
|
||||
private fun doTest(coordinates: String, isJsStdlib: Boolean, packaging: String = "jar") {
|
||||
ModuleRootModificationUtil.updateModel(module.get()) { modifiableModel ->
|
||||
MavenDependencyUtil.addFromMaven(
|
||||
modifiableModel, coordinates, /* includeTransitiveDependencies = */ false,
|
||||
DependencyScope.COMPILE, /* additionalRepositories = */ emptyList(), packaging,
|
||||
)
|
||||
}
|
||||
val libraries = mutableListOf<Library>()
|
||||
OrderEnumerator.orderEntries(module.get()).forEachLibrary { library ->
|
||||
libraries.add(library)
|
||||
true
|
||||
}
|
||||
|
||||
val theLibrary = libraries.singleOrNull()
|
||||
Assertions.assertNotNull(theLibrary, "Expected a single library, got: $libraries")
|
||||
KotlinJavaScriptStdlibDetectorFacility.isStdlib(module.get().project, library = theLibrary!!)
|
||||
Assertions.assertEquals(
|
||||
isJsStdlib, KotlinJavaScriptStdlibDetectorFacility.isStdlib(module.get().project, library = theLibrary),
|
||||
"Expected $theLibrary to be${" not".takeIf { !isJsStdlib }.orEmpty()} detected as stdlib",
|
||||
)
|
||||
|
||||
val stdlibJar = KotlinJavaScriptStdlibDetectorFacility.getStdlibJar(theLibrary.getFiles(OrderRootType.CLASSES).toList())
|
||||
if (isJsStdlib) Assertions.assertNotNull(stdlibJar) else Assertions.assertNull(stdlibJar)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user