mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-06 03:21:12 +07:00
IDEA-273306 building enabled plugin and module maps — unify implementation
GitOrigin-RevId: fd68078ffa6f48c859c5f4787cbf496012a59d29
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1e6d1496f7
commit
4d37e309bf
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.framework.detection;
|
||||
|
||||
import com.intellij.facet.Facet;
|
||||
@@ -54,7 +54,7 @@ public class FrameworkDetectionTest extends FrameworkDetectionTestCase {
|
||||
|
||||
Disposer.register(getTestRootDisposable(), DynamicPluginsTestUtil.loadExtensionWithText(
|
||||
"<framework.detector implementation=\"" + MockFacetDetector.class.getName() + "\"/>",
|
||||
MockFacetDetector.class.getClassLoader()));
|
||||
"com.intellij"));
|
||||
|
||||
assertFrameworkDetectedIn(file);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.framework.detection;
|
||||
|
||||
import com.intellij.facet.FacetTestCase;
|
||||
@@ -21,11 +21,11 @@ public abstract class FrameworkDetectionTestCase extends FacetTestCase {
|
||||
//todo we can get rid of this ugly check by converting facet tests to JUnit4 and using test rules to enable facet detection
|
||||
Disposer.register(getTestRootDisposable(), DynamicPluginsTestUtil.loadExtensionWithText(
|
||||
"<framework.detector implementation=\"" + MockFacetDetector.class.getName() + "\"/>",
|
||||
MockFacetDetector.class.getClassLoader()));
|
||||
"com.intellij"));
|
||||
|
||||
Disposer.register(getTestRootDisposable(), DynamicPluginsTestUtil.loadExtensionWithText(
|
||||
"<framework.detector implementation=\"" + MockSubFacetDetector.class.getName() + "\"/>",
|
||||
MockSubFacetDetector.class.getClassLoader()));
|
||||
"com.intellij"));
|
||||
}
|
||||
FrameworkDetectionManager.getInstance(myProject).doInitialize();
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ private const val FILE_NAME = "FileIdentifiableByText"
|
||||
@SkipSlowTestLocally
|
||||
class FileTypeIndexConsistencyTest : LightJavaCodeInsightFixtureTestCase() {
|
||||
fun testFuzzActions() {
|
||||
Disposer.register(testRootDisposable, loadExtensionWithText("<fileTypeDetector implementation=\"${MyFileTypeDetector::class.java.name}\"/>",
|
||||
MyFileTypeDetector::class.java.classLoader))
|
||||
Disposer.register(testRootDisposable, loadExtensionWithText(
|
||||
"<fileTypeDetector implementation=\"${MyFileTypeDetector::class.java.name}\"/>"))
|
||||
|
||||
val genAction: Generator<PsiIndexConsistencyTester.Action> = Generator.from { data ->
|
||||
MyTextChange(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.lang
|
||||
|
||||
import com.intellij.ide.plugins.loadExtensionWithText
|
||||
@@ -10,7 +10,6 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.LanguageSubstitutor
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
|
||||
import com.intellij.util.SystemProperties
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
|
||||
class LanguageSubstitutorLoadUnloadTest : LightJavaCodeInsightFixtureTestCase() {
|
||||
@@ -24,7 +23,7 @@ class LanguageSubstitutorLoadUnloadTest : LightJavaCodeInsightFixtureTestCase()
|
||||
|
||||
val virtualFile = beforeLoading.virtualFile
|
||||
val text = "<lang.substitutor language=\"TEXT\" implementationClass=\"${TextToJavaSubstitutor::class.java.name}\"/>"
|
||||
loadExtensionWithText(text, javaClass.classLoader).use {
|
||||
loadExtensionWithText(text).use {
|
||||
val afterLoading = PsiManager.getInstance(myFixture.project).findFile(virtualFile)
|
||||
assertThat(afterLoading!!.language).isInstanceOf(JavaLanguage::class.java)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.scopes;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
@@ -45,7 +45,7 @@ public class LibraryUseSearchUsingScopeEnlargerTest extends JavaCodeInsightFixtu
|
||||
super.setUp();
|
||||
Disposer.register(getTestRootDisposable(), DynamicPluginsTestUtil.loadExtensionWithText(
|
||||
"<useScopeEnlarger implementation=\"com.intellij.scopes.LibraryUseSearchUsingScopeEnlargerTest$LibraryUseScopeEnlarger\"/>",
|
||||
getClass().getClassLoader()));
|
||||
"com.intellij"));
|
||||
//bug? Test seems to use stale data.
|
||||
LocalFileSystem.getInstance().refreshIoFiles(Collections.singleton(new File(getTestDataPath())), false, true, null);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.indexing
|
||||
|
||||
import com.intellij.ide.plugins.loadExtensionWithText
|
||||
@@ -38,7 +38,7 @@ class BrokenPluginIndexingTest : JavaCodeInsightFixtureTestCase() {
|
||||
}
|
||||
|
||||
val text = "<fileBasedIndex implementation=\"" + BrokenFileBasedIndexExtension::class.qualifiedName + "\"/>"
|
||||
Disposer.register(testRootDisposable, loadExtensionWithText(text, BrokenFileBasedIndexExtension::class.java.classLoader))
|
||||
Disposer.register(testRootDisposable, loadExtensionWithText(text))
|
||||
val file = myFixture.addClass("class Some {}").containingFile.virtualFile
|
||||
FileBasedIndex.getInstance().getFileData(BrokenFileBasedIndexExtension.INDEX_ID, file, project)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.indexing
|
||||
|
||||
import com.intellij.ide.plugins.loadExtensionWithText
|
||||
@@ -16,7 +16,7 @@ import kotlin.streams.toList
|
||||
class IndexInfrastructureExtensionTest : LightJavaCodeInsightFixtureTestCase() {
|
||||
fun `test infrastructure extension drops all indexes when it requires invalidation`() {
|
||||
val text = "<fileBasedIndexInfrastructureExtension implementation=\"" + TestIndexInfrastructureExtension::class.java.name + "\"/>"
|
||||
Disposer.register(testRootDisposable, loadExtensionWithText(text, TestIndexInfrastructureExtension::class.java.classLoader))
|
||||
Disposer.register(testRootDisposable, loadExtensionWithText(text))
|
||||
|
||||
val before = Files.list(PathManager.getIndexRoot()).use {
|
||||
it.toList().associate { p -> p.fileName.toString() to p.lastModified().toMillis() }.toSortedMap()
|
||||
|
||||
@@ -47,7 +47,7 @@ class MultiProjectIndexTest {
|
||||
@Test
|
||||
fun `test index extension process files intersection`() {
|
||||
val text = "<fileBasedIndexInfrastructureExtension implementation=\"" + CountingTestExtension::class.java.name + "\"/>"
|
||||
Disposer.register(disposable.disposable, loadExtensionWithText(text, CountingTestExtension::class.java.classLoader))
|
||||
Disposer.register(disposable.disposable, loadExtensionWithText(text))
|
||||
val ext = FileBasedIndexInfrastructureExtension.EP_NAME.findExtension(CountingTestExtension::class.java)!!
|
||||
|
||||
val projectPath1 = tempDir.newDirectory("project1").toPath()
|
||||
|
||||
@@ -1,70 +1,93 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ide.plugins
|
||||
|
||||
import com.intellij.openapi.extensions.PluginId
|
||||
import com.intellij.util.graph.DFSTBuilder
|
||||
import com.intellij.util.graph.GraphGenerator
|
||||
import com.intellij.util.graph.InboundSemiGraph
|
||||
import com.intellij.util.graph.Graph
|
||||
import com.intellij.util.lang.Java11Shim
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.util.*
|
||||
import java.util.function.Supplier
|
||||
import java.util.stream.Stream
|
||||
|
||||
internal class CachingSemiGraph<Node>(private val nodes: Collection<Node>,
|
||||
private val pluginToDirectDependencies: Map<Node, List<Node>>) : InboundSemiGraph<Node> {
|
||||
@ApiStatus.Internal
|
||||
class CachingSemiGraph<Node>(private val nodes: Collection<Node>,
|
||||
private val pluginToDirectDependencies: Map<Node, List<Node>>) : Graph<Node> {
|
||||
private val outs = IdentityHashMap<Node, MutableList<Node>>()
|
||||
|
||||
init {
|
||||
buildOuts()
|
||||
}
|
||||
|
||||
private fun buildOuts() {
|
||||
val edges = Collections.newSetFromMap<Map.Entry<Node, Node>>(HashMap())
|
||||
for (node in nodes) {
|
||||
for (inNode in (pluginToDirectDependencies.get(node) ?: continue)) {
|
||||
if (edges.add(AbstractMap.SimpleImmutableEntry(inNode, node))) {
|
||||
// not a duplicate edge
|
||||
outs.computeIfAbsent(inNode) { ArrayList() }.add(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getOut(n: Node): Iterator<Node> = outs.get(n)?.iterator() ?: Collections.emptyIterator()
|
||||
|
||||
override fun getNodes() = nodes
|
||||
|
||||
override fun getIn(node: Node): Iterator<Node> {
|
||||
return pluginToDirectDependencies[node]?.iterator()
|
||||
?: Collections.emptyIterator()
|
||||
return pluginToDirectDependencies.get(node)?.iterator() ?: Collections.emptyIterator()
|
||||
}
|
||||
|
||||
fun getInStream(node: Node): Stream<Node> {
|
||||
return pluginToDirectDependencies[node]?.stream()
|
||||
?: Stream.empty()
|
||||
return pluginToDirectDependencies.get(node)?.stream() ?: Stream.empty()
|
||||
}
|
||||
}
|
||||
|
||||
fun getTopologicallySorted(descriptors: Collection<IdeaPluginDescriptorImpl>,
|
||||
pluginSet: PluginSet,
|
||||
withOptional: Boolean): List<IdeaPluginDescriptorImpl> {
|
||||
val graph = createPluginIdGraph(descriptors = descriptors,
|
||||
pluginSet = pluginSet,
|
||||
withOptional = withOptional)
|
||||
val requiredOnlyGraph = DFSTBuilder(GraphGenerator.generate(graph))
|
||||
val sortedRequired = ArrayList(graph.nodes)
|
||||
val graph = createPluginIdGraph(descriptors = descriptors, pluginSet = pluginSet, withOptional = withOptional)
|
||||
val requiredOnlyGraph = DFSTBuilder(graph)
|
||||
val comparator = requiredOnlyGraph.comparator()
|
||||
// there is circular reference between core and implementation-detail plugin, as not all such plugins extracted from core,
|
||||
// so, ensure that core plugin is always first (otherwise not possible to register actions - parent group not defined)
|
||||
// don't use sortWith here - avoid loading kotlin stdlib
|
||||
Collections.sort(sortedRequired, Comparator { o1, o2 ->
|
||||
val sortedRequired = descriptors.toTypedArray()
|
||||
Arrays.sort(sortedRequired, Comparator { o1, o2 ->
|
||||
when (PluginManagerCore.CORE_ID) {
|
||||
o1.pluginId -> -1
|
||||
o2.pluginId -> 1
|
||||
else -> comparator.compare(o1, o2)
|
||||
o1.id -> -1
|
||||
o2.id -> 1
|
||||
else -> comparator.compare(o1.id, o2.id)
|
||||
}
|
||||
})
|
||||
return sortedRequired
|
||||
@Suppress("ReplaceJavaStaticMethodWithKotlinAnalog", "UNCHECKED_CAST")
|
||||
return Java11Shim.INSTANCE.listOf(sortedRequired)
|
||||
}
|
||||
|
||||
internal fun createPluginIdGraph(descriptors: Collection<IdeaPluginDescriptorImpl>,
|
||||
pluginSet: PluginSet,
|
||||
withOptional: Boolean): CachingSemiGraph<IdeaPluginDescriptorImpl> {
|
||||
@ApiStatus.Internal
|
||||
fun createPluginIdGraph(descriptors: Collection<IdeaPluginDescriptorImpl>,
|
||||
pluginSet: PluginSet,
|
||||
withOptional: Boolean): CachingSemiGraph<PluginId> {
|
||||
val hasAllModules = pluginSet.isPluginEnabled(PluginManagerCore.ALL_MODULES_MARKER)
|
||||
val javaDep = Supplier {
|
||||
pluginSet.findEnabledPlugin(PluginManagerCore.JAVA_MODULE_ID)
|
||||
}
|
||||
|
||||
val uniqueCheck = HashSet<IdeaPluginDescriptorImpl>()
|
||||
val pluginToDirectDependencies = HashMap<IdeaPluginDescriptorImpl, List<IdeaPluginDescriptorImpl>>(descriptors.size)
|
||||
val list = ArrayList<IdeaPluginDescriptorImpl>(32)
|
||||
val uniqueCheck = Collections.newSetFromMap<PluginId>(IdentityHashMap())
|
||||
val pluginToDirectDependencies = IdentityHashMap<PluginId, List<PluginId>>(descriptors.size)
|
||||
val list = ArrayList<PluginId>(32)
|
||||
val ids = arrayOfNulls<PluginId>(descriptors.size)
|
||||
var index = 0
|
||||
for (descriptor in descriptors) {
|
||||
ids[index++] = descriptor.id
|
||||
collectDirectDependencies(descriptor, pluginSet, withOptional, hasAllModules, javaDep, uniqueCheck, list)
|
||||
if (!list.isEmpty()) {
|
||||
pluginToDirectDependencies.put(descriptor, Java11Shim.INSTANCE.copyOf(list))
|
||||
pluginToDirectDependencies.put(descriptor.id, Java11Shim.INSTANCE.copyOf(list))
|
||||
list.clear()
|
||||
}
|
||||
}
|
||||
return CachingSemiGraph(descriptors, pluginToDirectDependencies)
|
||||
return CachingSemiGraph(Java11Shim.INSTANCE.listOf<PluginId>(ids), pluginToDirectDependencies)
|
||||
}
|
||||
|
||||
private fun collectDirectDependencies(rootDescriptor: IdeaPluginDescriptorImpl,
|
||||
@@ -72,8 +95,8 @@ private fun collectDirectDependencies(rootDescriptor: IdeaPluginDescriptorImpl,
|
||||
withOptional: Boolean,
|
||||
hasAllModules: Boolean,
|
||||
javaDep: Supplier<IdeaPluginDescriptorImpl?>,
|
||||
uniqueCheck: MutableSet<IdeaPluginDescriptorImpl>,
|
||||
result: MutableList<IdeaPluginDescriptorImpl>) {
|
||||
uniqueCheck: MutableSet<PluginId>,
|
||||
result: MutableList<PluginId>) {
|
||||
val implicitDep = if (hasAllModules) PluginManagerCore.getImplicitDependency(rootDescriptor, javaDep) else null
|
||||
uniqueCheck.clear()
|
||||
if (implicitDep != null) {
|
||||
@@ -81,10 +104,11 @@ private fun collectDirectDependencies(rootDescriptor: IdeaPluginDescriptorImpl,
|
||||
PluginManagerCore.getLogger().error("Plugin $rootDescriptor depends on self")
|
||||
}
|
||||
else {
|
||||
uniqueCheck.add(implicitDep)
|
||||
result.add(implicitDep)
|
||||
uniqueCheck.add(implicitDep.id)
|
||||
result.add(implicitDep.id)
|
||||
}
|
||||
}
|
||||
|
||||
for (dependency in rootDescriptor.pluginDependencies) {
|
||||
if (!withOptional && dependency.isOptional) {
|
||||
continue
|
||||
@@ -103,8 +127,8 @@ private fun collectDirectDependencies(rootDescriptor: IdeaPluginDescriptorImpl,
|
||||
PluginManagerCore.getLogger().error("Plugin $rootDescriptor depends on self")
|
||||
}
|
||||
}
|
||||
else if (uniqueCheck.add(dep)) {
|
||||
result.add(dep)
|
||||
else if (uniqueCheck.add(dep.id)) {
|
||||
result.add(dep.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,19 +142,19 @@ private fun collectDirectDependencies(rootDescriptor: IdeaPluginDescriptorImpl,
|
||||
}
|
||||
for (moduleId in rootDescriptor.incompatibilities) {
|
||||
val dep = pluginSet.findEnabledPlugin(moduleId)
|
||||
if (dep != null && uniqueCheck.add(dep)) {
|
||||
result.add(dep)
|
||||
if (dep != null && uniqueCheck.add(dep.id)) {
|
||||
result.add(dep.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun directDependenciesOfModule(module: IdeaPluginDescriptorImpl,
|
||||
pluginSet: PluginSet,
|
||||
uniqueCheck: MutableSet<IdeaPluginDescriptorImpl>,
|
||||
result: MutableList<IdeaPluginDescriptorImpl>) {
|
||||
uniqueCheck: MutableSet<PluginId>,
|
||||
result: MutableList<PluginId>) {
|
||||
processDirectDependencies(module, pluginSet) {
|
||||
if (uniqueCheck.add(it)) {
|
||||
result.add(it)
|
||||
if (uniqueCheck.add(it.id)) {
|
||||
result.add(it.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.util.function.BiPredicate
|
||||
import java.util.function.Function
|
||||
|
||||
private val DEFAULT_CLASSLOADER_CONFIGURATION = UrlClassLoader.build().useCache()
|
||||
private val EMPTY_DESCRIPTOR_ARRAY = arrayOfNulls<IdeaPluginDescriptorImpl>(0)
|
||||
private val EMPTY_DESCRIPTOR_ARRAY = emptyArray<IdeaPluginDescriptorImpl>()
|
||||
|
||||
@ApiStatus.Internal
|
||||
class ClassLoaderConfigurator(
|
||||
@@ -31,8 +31,7 @@ class ClassLoaderConfigurator(
|
||||
|
||||
// temporary set to produce arrays (avoid allocation for each plugin)
|
||||
// set to remove duplicated classloaders
|
||||
private val loaders = LinkedHashSet<ClassLoader>()
|
||||
private val dependencies = ArrayList<IdeaPluginDescriptorImpl>()
|
||||
private val dependencies = LinkedHashSet<IdeaPluginDescriptorImpl>()
|
||||
|
||||
private val hasAllModules = pluginSet.isPluginEnabled(PluginManagerCore.ALL_MODULES_MARKER)
|
||||
|
||||
@@ -71,17 +70,17 @@ class ClassLoaderConfigurator(
|
||||
}
|
||||
}
|
||||
else {
|
||||
mainDependentClassLoader.attachParent(dependencyPlugin.classLoader!!)
|
||||
mainDependentClassLoader.attachParent(dependencyPlugin)
|
||||
for (module in modules) {
|
||||
module.classLoader = mainDependentClassLoader
|
||||
}
|
||||
}
|
||||
}
|
||||
loaders.clear()
|
||||
dependencies.clear()
|
||||
}
|
||||
|
||||
fun configureAll() {
|
||||
val postTasks = mutableListOf<() -> Unit>()
|
||||
val postTasks = ArrayList<() -> Unit>()
|
||||
for (plugin in pluginSet.enabledPlugins) {
|
||||
// not only for core plugin, but also for a plugin from classpath (run TraverseUi)
|
||||
if (plugin.pluginId == PluginManagerCore.CORE_ID || (plugin.isUseCoreClassLoader && !plugin.content.modules.isEmpty())) {
|
||||
@@ -109,7 +108,7 @@ class ClassLoaderConfigurator(
|
||||
return
|
||||
}
|
||||
|
||||
loaders.clear()
|
||||
dependencies.clear()
|
||||
|
||||
// first, set class loader for main descriptor
|
||||
if (hasAllModules) {
|
||||
@@ -120,7 +119,12 @@ class ClassLoaderConfigurator(
|
||||
}
|
||||
javaDep!!.orElse(null)
|
||||
}
|
||||
implicitDependency?.let { addLoaderOrLogError(plugin, it, loaders) }
|
||||
implicitDependency?.let {
|
||||
if (it.classLoader !== coreLoader) {
|
||||
dependencies.add(it)
|
||||
}
|
||||
Unit
|
||||
}
|
||||
}
|
||||
|
||||
var files = plugin.jarFiles
|
||||
@@ -142,7 +146,7 @@ class ClassLoaderConfigurator(
|
||||
addContentModulesIfNeeded(dependency)
|
||||
}
|
||||
// must be after adding implicit module class loaders
|
||||
loaders.add(loader)
|
||||
dependencies.add(p)
|
||||
}
|
||||
|
||||
dependency.subDescriptor?.let {
|
||||
@@ -155,9 +159,8 @@ class ClassLoaderConfigurator(
|
||||
|
||||
// new format
|
||||
processDirectDependencies(plugin, pluginSet) {
|
||||
val classLoader = it.classLoader!!
|
||||
if (classLoader !== coreLoader) {
|
||||
loaders.add(classLoader)
|
||||
if (it.classLoader !== coreLoader) {
|
||||
dependencies.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,28 +196,28 @@ class ClassLoaderConfigurator(
|
||||
}
|
||||
|
||||
// reset to ensure that stalled data will be not reused somehow later
|
||||
loaders.clear()
|
||||
dependencies.clear()
|
||||
}
|
||||
|
||||
private fun addContentModulesIfNeeded(dependency: PluginDependency) {
|
||||
when (dependency.pluginId.idString) {
|
||||
"Docker" -> {
|
||||
pluginSet.findEnabledModule("intellij.clouds.docker.file")?.classLoader?.let {
|
||||
loaders.add(it)
|
||||
pluginSet.findEnabledModule("intellij.clouds.docker.file")?.let {
|
||||
dependencies.add(it)
|
||||
}
|
||||
pluginSet.findEnabledModule("intellij.clouds.docker.remoteRun")?.classLoader?.let {
|
||||
loaders.add(it)
|
||||
pluginSet.findEnabledModule("intellij.clouds.docker.remoteRun")?.let {
|
||||
dependencies.add(it)
|
||||
}
|
||||
}
|
||||
"com.intellij.diagram" -> {
|
||||
// https://youtrack.jetbrains.com/issue/IDEA-266323
|
||||
pluginSet.findEnabledModule("intellij.diagram.java")?.classLoader?.let {
|
||||
loaders.add(it)
|
||||
pluginSet.findEnabledModule("intellij.diagram.java")?.let {
|
||||
dependencies.add(it)
|
||||
}
|
||||
}
|
||||
"com.intellij.modules.clion" -> {
|
||||
pluginSet.findEnabledModule("intellij.profiler.clion")?.classLoader?.let {
|
||||
loaders.add(it)
|
||||
pluginSet.findEnabledModule("intellij.profiler.clion")?.let {
|
||||
dependencies.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -290,14 +293,14 @@ class ClassLoaderConfigurator(
|
||||
files: List<Path>,
|
||||
libDirectories: MutableList<String>,
|
||||
classPath: ClassPath): PluginClassLoader {
|
||||
val parentLoaders = if (loaders.isEmpty()) {
|
||||
PluginClassLoader.EMPTY_CLASS_LOADER_ARRAY
|
||||
val parents: Array<IdeaPluginDescriptorImpl> = if (dependencies.isEmpty()) {
|
||||
EMPTY_DESCRIPTOR_ARRAY
|
||||
}
|
||||
else {
|
||||
loaders.toArray(arrayOfNulls(loaders.size))
|
||||
dependencies.toArray(arrayOfNulls(dependencies.size))
|
||||
}
|
||||
|
||||
return createPluginClassLoader(parentLoaders = parentLoaders,
|
||||
return createPluginClassLoader(parents = parents,
|
||||
descriptor = descriptor,
|
||||
files = files,
|
||||
coreLoader = coreLoader,
|
||||
@@ -322,11 +325,7 @@ class ClassLoaderConfigurator(
|
||||
|
||||
for (item in module.dependencies.modules) {
|
||||
// Module dependency is always optional. If the module depends on an unavailable plugin, it will not be loaded.
|
||||
val descriptor = (pluginSet.findEnabledModule(item.name) ?: return)
|
||||
val classLoader = descriptor.classLoader
|
||||
if (classLoader !== coreLoader) {
|
||||
dependencies.add(descriptor)
|
||||
}
|
||||
dependencies.add(pluginSet.findEnabledModule(item.name) ?: return)
|
||||
}
|
||||
for (item in module.dependencies.plugins) {
|
||||
val descriptor = pluginSet.findEnabledPlugin(item.id) ?: return
|
||||
@@ -352,7 +351,6 @@ class ClassLoaderConfigurator(
|
||||
module.classLoader = PluginClassLoader(
|
||||
files,
|
||||
classPath,
|
||||
null,
|
||||
array,
|
||||
module,
|
||||
coreLoader,
|
||||
@@ -361,18 +359,6 @@ class ClassLoaderConfigurator(
|
||||
)
|
||||
}
|
||||
|
||||
private fun addLoaderOrLogError(dependent: IdeaPluginDescriptorImpl,
|
||||
dependency: IdeaPluginDescriptorImpl,
|
||||
loaders: MutableCollection<ClassLoader>) {
|
||||
val loader = dependency.classLoader
|
||||
if (loader == null) {
|
||||
log.error(PluginLoadingError.formatErrorMessage(dependent, "requires missing class loader for '${dependency.name}'"))
|
||||
}
|
||||
else if (loader !== coreLoader) {
|
||||
loaders.add(loader)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setPluginClassLoaderForMainAndSubPlugins(rootDescriptor: IdeaPluginDescriptorImpl, classLoader: ClassLoader?) {
|
||||
rootDescriptor.classLoader = classLoader
|
||||
for (dependency in rootDescriptor.pluginDependencies) {
|
||||
@@ -400,7 +386,7 @@ private val log: Logger
|
||||
get() = Logger.getInstance("#com.intellij.ide.plugins.PluginManager")
|
||||
|
||||
// static to ensure that anonymous classes will not hold ClassLoaderConfigurator
|
||||
private fun createPluginClassLoader(parentLoaders: Array<ClassLoader>,
|
||||
private fun createPluginClassLoader(parents: Array<IdeaPluginDescriptorImpl>,
|
||||
descriptor: IdeaPluginDescriptorImpl,
|
||||
files: List<Path>,
|
||||
libDirectories: MutableList<String>,
|
||||
@@ -412,7 +398,7 @@ private fun createPluginClassLoader(parentLoaders: Array<ClassLoader>,
|
||||
when (descriptor.id.idString) {
|
||||
"com.intellij.diagram" -> {
|
||||
// multiple packages - intellij.diagram and intellij.diagram.impl modules
|
||||
return createPluginClassLoaderWithExtraPackage(parentLoaders = parentLoaders,
|
||||
return createPluginClassLoaderWithExtraPackage(parents = parents,
|
||||
descriptor = descriptor,
|
||||
files = files,
|
||||
coreLoader = coreLoader,
|
||||
@@ -421,7 +407,7 @@ private fun createPluginClassLoader(parentLoaders: Array<ClassLoader>,
|
||||
customPackage = "com.intellij.diagram.")
|
||||
}
|
||||
"com.intellij.struts2" -> {
|
||||
return createPluginClassLoaderWithExtraPackage(parentLoaders = parentLoaders,
|
||||
return createPluginClassLoaderWithExtraPackage(parents = parents,
|
||||
descriptor = descriptor,
|
||||
files = files,
|
||||
coreLoader = coreLoader,
|
||||
@@ -431,7 +417,7 @@ private fun createPluginClassLoader(parentLoaders: Array<ClassLoader>,
|
||||
}
|
||||
"com.intellij.properties" -> {
|
||||
// todo ability to customize (cannot move due to backward compatibility)
|
||||
return createPluginClassloader(parentLoaders = parentLoaders,
|
||||
return createPluginClassloader(parents = parents,
|
||||
descriptor = descriptor,
|
||||
files = files,
|
||||
coreLoader = coreLoader,
|
||||
@@ -455,7 +441,7 @@ private fun createPluginClassLoader(parentLoaders: Array<ClassLoader>,
|
||||
else {
|
||||
if (!descriptor.content.modules.isEmpty()) {
|
||||
// see "The `content.module` element" section about content handling for a module
|
||||
return createPluginClassloader(parentLoaders = parentLoaders,
|
||||
return createPluginClassloader(parents = parents,
|
||||
descriptor = descriptor,
|
||||
files = files,
|
||||
coreLoader = coreLoader,
|
||||
@@ -464,7 +450,7 @@ private fun createPluginClassLoader(parentLoaders: Array<ClassLoader>,
|
||||
resolveScopeManager = createModuleContentBasedScope(descriptor))
|
||||
}
|
||||
else if (descriptor.packagePrefix != null) {
|
||||
return createPluginClassloader(parentLoaders = parentLoaders,
|
||||
return createPluginClassloader(parents = parents,
|
||||
descriptor = descriptor,
|
||||
files = files,
|
||||
coreLoader = coreLoader,
|
||||
@@ -474,7 +460,7 @@ private fun createPluginClassLoader(parentLoaders: Array<ClassLoader>,
|
||||
}
|
||||
|
||||
return createPluginClassloader(
|
||||
parentLoaders = parentLoaders,
|
||||
parents = parents,
|
||||
descriptor = descriptor,
|
||||
files = files,
|
||||
coreLoader = coreLoader,
|
||||
@@ -492,25 +478,25 @@ private fun createModuleResolveScopeManager(): PluginClassLoader.ResolveScopeMan
|
||||
}
|
||||
}
|
||||
|
||||
private fun createPluginClassloader(parentLoaders: Array<ClassLoader>,
|
||||
private fun createPluginClassloader(parents: Array<IdeaPluginDescriptorImpl>,
|
||||
descriptor: IdeaPluginDescriptorImpl,
|
||||
files: List<Path>,
|
||||
libDirectories: MutableList<String>,
|
||||
coreLoader: ClassLoader,
|
||||
classPath: ClassPath,
|
||||
resolveScopeManager: PluginClassLoader.ResolveScopeManager?): PluginClassLoader {
|
||||
return PluginClassLoader(files, classPath, parentLoaders, null, descriptor, coreLoader, resolveScopeManager, descriptor.packagePrefix,
|
||||
return PluginClassLoader(files, classPath, parents, descriptor, coreLoader, resolveScopeManager, descriptor.packagePrefix,
|
||||
libDirectories)
|
||||
}
|
||||
|
||||
private fun createPluginClassLoaderWithExtraPackage(parentLoaders: Array<ClassLoader>,
|
||||
private fun createPluginClassLoaderWithExtraPackage(parents: Array<IdeaPluginDescriptorImpl>,
|
||||
descriptor: IdeaPluginDescriptorImpl,
|
||||
files: List<Path>,
|
||||
libDirectories: MutableList<String>,
|
||||
coreLoader: ClassLoader,
|
||||
classPath: ClassPath,
|
||||
customPackage: String): PluginClassLoader {
|
||||
return createPluginClassloader(parentLoaders = parentLoaders,
|
||||
return createPluginClassloader(parents = parents,
|
||||
descriptor = descriptor,
|
||||
files = files,
|
||||
coreLoader = coreLoader,
|
||||
|
||||
@@ -11,20 +11,14 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.PluginId;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.util.graph.Graph;
|
||||
import com.intellij.util.graph.GraphAlgorithms;
|
||||
import com.intellij.util.graph.GraphGenerator;
|
||||
import com.intellij.util.lang.Java11Shim;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -183,32 +177,6 @@ public final class PluginManager {
|
||||
return !Registry.is("plugins.show.implementation.details");
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void setPlugins(@NotNull List<IdeaPluginDescriptorImpl> descriptors) {
|
||||
PluginManagerCore.doSetPlugins(Java11Shim.INSTANCE.copyOf(descriptors));
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public boolean processAllBackwardDependencies(@NotNull IdeaPluginDescriptorImpl rootDescriptor,
|
||||
boolean withOptionalDeps,
|
||||
@NotNull Function<IdeaPluginDescriptorImpl, FileVisitResult> consumer) {
|
||||
@NotNull PluginSet pluginSet = PluginManagerCore.getPluginSet();
|
||||
CachingSemiGraph<IdeaPluginDescriptorImpl> semiGraph = CachingSemiGraphKt.createPluginIdGraph(pluginSet.enabledPlugins, pluginSet,
|
||||
withOptionalDeps);
|
||||
Graph<IdeaPluginDescriptorImpl> graph = GraphGenerator.generate(semiGraph);
|
||||
Set<IdeaPluginDescriptorImpl> dependencies = new LinkedHashSet<>();
|
||||
GraphAlgorithms.getInstance().collectOutsRecursively(graph, rootDescriptor, dependencies);
|
||||
for (IdeaPluginDescriptorImpl dependency : dependencies) {
|
||||
if (dependency == rootDescriptor) {
|
||||
continue;
|
||||
}
|
||||
if (consumer.apply(dependency) == FileVisitResult.TERMINATE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public @NotNull Disposable createDisposable(@NotNull Class<?> requestor) {
|
||||
ClassLoader classLoader = requestor.getClassLoader();
|
||||
if (!(classLoader instanceof PluginAwareClassLoader)) {
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.PlatformUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.graph.DFSTBuilder;
|
||||
import com.intellij.util.graph.GraphGenerator;
|
||||
import com.intellij.util.lang.Java11Shim;
|
||||
import com.intellij.util.lang.UrlClassLoader;
|
||||
import org.jetbrains.annotations.*;
|
||||
@@ -151,8 +150,9 @@ public final class PluginManagerCore {
|
||||
return pluginSet != null;
|
||||
}
|
||||
|
||||
static synchronized void doSetPlugins(@Nullable List<IdeaPluginDescriptorImpl> value) {
|
||||
pluginSet = value == null ? null : new PluginSet(value, getOnlyEnabledPlugins(value), true);
|
||||
@ApiStatus.Internal
|
||||
public static void setPluginSet(@NotNull PluginSet value) {
|
||||
pluginSet = value;
|
||||
}
|
||||
|
||||
public static boolean isDisabled(@NotNull PluginId pluginId) {
|
||||
@@ -434,7 +434,7 @@ public final class PluginManagerCore {
|
||||
}
|
||||
|
||||
public static synchronized void invalidatePlugins() {
|
||||
doSetPlugins(null);
|
||||
pluginSet = null;
|
||||
DisabledPluginsState.invalidate();
|
||||
ourShadowedBundledPlugins = null;
|
||||
}
|
||||
@@ -551,26 +551,32 @@ public final class PluginManagerCore {
|
||||
|
||||
private static void checkPluginCycles(@NotNull List<IdeaPluginDescriptorImpl> descriptors,
|
||||
@NotNull PluginSet pluginSet,
|
||||
@NotNull List<Supplier<@Nls String>> errors) {
|
||||
CachingSemiGraph<IdeaPluginDescriptorImpl> graph = CachingSemiGraphKt.createPluginIdGraph(descriptors, pluginSet, true);
|
||||
DFSTBuilder<IdeaPluginDescriptorImpl> builder = new DFSTBuilder<>(GraphGenerator.generate(graph));
|
||||
@NotNull List<Supplier<@Nls String>> errors,
|
||||
@NotNull Map<PluginId, IdeaPluginDescriptorImpl> idMap) {
|
||||
CachingSemiGraph<PluginId> graph = CachingSemiGraphKt.createPluginIdGraph(descriptors, pluginSet, true);
|
||||
DFSTBuilder<PluginId> builder = new DFSTBuilder<>(graph);
|
||||
if (builder.isAcyclic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Collection<IdeaPluginDescriptorImpl> component : builder.getComponents()) {
|
||||
for (Collection<PluginId> component : builder.getComponents()) {
|
||||
if (component.size() < 2) {
|
||||
continue;
|
||||
}
|
||||
for (IdeaPluginDescriptor descriptor : component) {
|
||||
descriptor.setEnabled(false);
|
||||
for (PluginId id : component) {
|
||||
IdeaPluginDescriptorImpl plugin = pluginSet.findEnabledPlugin(id);
|
||||
if (plugin != null) {
|
||||
plugin.setEnabled(false);
|
||||
}
|
||||
}
|
||||
String pluginsString = component.stream().map(it -> "'" + it.getName() + "'").collect(Collectors.joining(", "));
|
||||
|
||||
String pluginsString = component.stream().map(it -> "'" + idMap.get(it).getName() + "'").collect(Collectors.joining(", "));
|
||||
errors.add(message("plugin.loading.error.plugins.cannot.be.loaded.because.they.form.a.dependency.cycle", pluginsString));
|
||||
|
||||
StringBuilder detailedMessage = new StringBuilder();
|
||||
Function<IdeaPluginDescriptorImpl, String> pluginToString = plugin -> {
|
||||
return "id = " + plugin.getPluginId().getIdString() + " (" + plugin.getName() + ")";
|
||||
Function<PluginId, String> pluginToString = id -> {
|
||||
IdeaPluginDescriptorImpl descriptor = idMap.get(id);
|
||||
return "id = " + id.getIdString() + " (" + descriptor.getName() + ")";
|
||||
};
|
||||
|
||||
detailedMessage.append("Detected plugin dependencies cycle details (only related dependencies are included):\n");
|
||||
@@ -893,51 +899,34 @@ public final class PluginManagerCore {
|
||||
}
|
||||
|
||||
List<IdeaPluginDescriptorImpl> descriptors = loadingResult.getEnabledPlugins();
|
||||
PluginSet rawPluginSet = new PluginSet(descriptors, descriptors, false);
|
||||
PluginSet rawPluginSet = PluginSet.Companion.createRawPluginSet(descriptors);
|
||||
disableIncompatiblePlugins(descriptors, idMap, pluginErrorsById);
|
||||
checkPluginCycles(descriptors, rawPluginSet, globalErrors);
|
||||
checkPluginCycles(descriptors, rawPluginSet, globalErrors, idMap);
|
||||
|
||||
Map<PluginId, String> disabledIds = new HashMap<>();
|
||||
|
||||
// topological sort based on required dependencies only
|
||||
List<IdeaPluginDescriptorImpl> sortedRequired = CachingSemiGraphKt.getTopologicallySorted(descriptors, rawPluginSet, false);
|
||||
|
||||
Set<PluginId> enabledPluginIds = new HashSet<>();
|
||||
Set<String> enabledModuleV2Ids = new HashSet<>();
|
||||
Map<PluginId, IdeaPluginDescriptorImpl> enabledPluginIds = new HashMap<>();
|
||||
Map<String, PluginContentDescriptor.ModuleItem> enabledModuleV2Ids = new HashMap<>();
|
||||
Set<PluginId> disabledRequiredIds = new HashSet<>();
|
||||
|
||||
Logger logger = getLogger();
|
||||
boolean isDebugLogEnabled = logger.isDebugEnabled() || !System.getProperty("plugin.classloader.debug", "").isEmpty();
|
||||
boolean isDebugLogEnabled = logger.isDebugEnabled() || !System.getProperty("plugin.classloader.debug", "").isEmpty() || isUnitTestMode;
|
||||
for (IdeaPluginDescriptorImpl descriptor : sortedRequired) {
|
||||
boolean wasEnabled = descriptor.isEnabled();
|
||||
if (wasEnabled && computePluginEnabled(descriptor,
|
||||
enabledPluginIds, enabledModuleV2Ids,
|
||||
idMap, disabledRequiredIds, context.disabledPlugins, pluginErrorsById)) {
|
||||
enabledPluginIds.add(descriptor.getPluginId());
|
||||
enabledPluginIds.addAll(descriptor.modules);
|
||||
|
||||
m: for (PluginContentDescriptor.ModuleItem item : descriptor.content.modules) {
|
||||
for (ModuleDependenciesDescriptor.ModuleReference ref : item.requireDescriptor().dependencies.modules) {
|
||||
if (!enabledModuleV2Ids.contains(ref.name)) {
|
||||
if (isDebugLogEnabled) {
|
||||
logger.info("Module " + item.name + " is not enabled because dependency " + ref.name + " is not available");
|
||||
}
|
||||
continue m;
|
||||
}
|
||||
}
|
||||
for (ModuleDependenciesDescriptor.PluginReference ref : item.requireDescriptor().dependencies.plugins) {
|
||||
if (!enabledPluginIds.contains(ref.id)) {
|
||||
if (isDebugLogEnabled) {
|
||||
logger.info("Module " + item.name + " is not enabled because dependency " + ref.id + " is not available");
|
||||
}
|
||||
continue m;
|
||||
}
|
||||
}
|
||||
|
||||
enabledModuleV2Ids.add(item.name);
|
||||
PluginSet.Companion.addWithV1Modules(enabledPluginIds, descriptor);
|
||||
if (!CORE_ID.equals(descriptor.getPluginId())) {
|
||||
PluginSet.Companion.checkModules(descriptor, enabledPluginIds, enabledModuleV2Ids, isDebugLogEnabled, logger);
|
||||
}
|
||||
if (descriptor.packagePrefix != null) {
|
||||
enabledModuleV2Ids.add(descriptor.getId().getIdString());
|
||||
else {
|
||||
for (PluginContentDescriptor.ModuleItem item : descriptor.content.modules) {
|
||||
enabledModuleV2Ids.put(item.name, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -961,20 +950,24 @@ public final class PluginManagerCore {
|
||||
|
||||
// topological sort based on all (required and optional) dependencies
|
||||
List<IdeaPluginDescriptorImpl> allPlugins = CachingSemiGraphKt.getTopologicallySorted(sortedRequired, rawPluginSet, true);
|
||||
List<IdeaPluginDescriptorImpl> enabledPlugins = getOnlyEnabledPlugins(allPlugins);
|
||||
List<IdeaPluginDescriptorImpl> enabledPlugins = PluginSet.Companion.getOnlyEnabledPlugins(allPlugins);
|
||||
Java11Shim java11Shim = Java11Shim.INSTANCE;
|
||||
if (!context.result.incompletePlugins.isEmpty()) {
|
||||
allPlugins.addAll(context.result.incompletePlugins.values());
|
||||
List<IdeaPluginDescriptorImpl> result = new ArrayList<>(allPlugins.size() + context.result.incompletePlugins.size());
|
||||
result.addAll(allPlugins);
|
||||
result.addAll(context.result.incompletePlugins.values());
|
||||
allPlugins = java11Shim.copyOf(result);
|
||||
}
|
||||
|
||||
PluginSet pluginSet = new PluginSet(allPlugins, enabledPlugins, false);
|
||||
PluginSet pluginSet = new PluginSet(allPlugins, java11Shim.copyOf(enabledPlugins),
|
||||
java11Shim.copyOf(enabledModuleV2Ids), java11Shim.copyOf(enabledPluginIds));
|
||||
new ClassLoaderConfigurator(pluginSet, coreLoader).configureAll();
|
||||
|
||||
if (checkEssentialPlugins) {
|
||||
checkEssentialPluginsAreAvailable(idMap);
|
||||
}
|
||||
|
||||
Set<PluginId> effectiveDisabledIds = disabledIds.isEmpty() ? Collections.emptySet() : Java11Shim.INSTANCE.copyOf(disabledIds.keySet());
|
||||
return new PluginManagerState(pluginSet, disabledRequiredIds, effectiveDisabledIds);
|
||||
return new PluginManagerState(pluginSet, disabledRequiredIds, java11Shim.copyOf(disabledIds.keySet()));
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@@ -1030,8 +1023,8 @@ public final class PluginManagerCore {
|
||||
}
|
||||
|
||||
private static boolean computePluginEnabled(@NotNull IdeaPluginDescriptorImpl descriptor,
|
||||
@NotNull Set<PluginId> enabledPluginIds,
|
||||
@NotNull Set<String> enabledModuleV2Ids,
|
||||
@NotNull Map<PluginId, IdeaPluginDescriptorImpl> enabledPluginIds,
|
||||
@NotNull Map<String, PluginContentDescriptor.ModuleItem> enabledModuleV2Ids,
|
||||
@NotNull Map<PluginId, IdeaPluginDescriptorImpl> idMap,
|
||||
@NotNull Set<PluginId> disabledRequiredIds,
|
||||
@NotNull Set<PluginId> disabledPlugins,
|
||||
@@ -1043,7 +1036,7 @@ public final class PluginManagerCore {
|
||||
boolean notifyUser = !descriptor.isImplementationDetail();
|
||||
boolean result = true;
|
||||
for (PluginId incompatibleId : descriptor.incompatibilities) {
|
||||
if (!enabledPluginIds.contains(incompatibleId) || disabledPlugins.contains(incompatibleId)) {
|
||||
if (!enabledPluginIds.containsKey(incompatibleId) || disabledPlugins.contains(incompatibleId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1059,7 +1052,7 @@ public final class PluginManagerCore {
|
||||
|
||||
for (PluginDependency dependency : descriptor.pluginDependencies) {
|
||||
PluginId depId = dependency.getPluginId();
|
||||
if (dependency.isOptional() || enabledPluginIds.contains(depId)) {
|
||||
if (dependency.isOptional() || enabledPluginIds.containsKey(depId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1072,8 +1065,9 @@ public final class PluginManagerCore {
|
||||
|
||||
addCannotLoadError(descriptor, errors, notifyUser, depId, dep);
|
||||
}
|
||||
|
||||
for (ModuleDependenciesDescriptor.PluginReference item : descriptor.dependencies.plugins) {
|
||||
if (enabledPluginIds.contains(item.id)) {
|
||||
if (enabledPluginIds.containsKey(item.id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1086,8 +1080,9 @@ public final class PluginManagerCore {
|
||||
|
||||
addCannotLoadError(descriptor, errors, notifyUser, item.id, dep);
|
||||
}
|
||||
|
||||
for (ModuleDependenciesDescriptor.ModuleReference item : descriptor.dependencies.modules) {
|
||||
if (enabledModuleV2Ids.contains(item.name)) {
|
||||
if (enabledModuleV2Ids.containsKey(item.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1301,9 +1296,8 @@ public final class PluginManagerCore {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static @NotNull List<PluginId> getNonOptionalDependenciesIds(@NotNull IdeaPluginDescriptorImpl descriptor) {
|
||||
ArrayList<PluginId> dependencies = new ArrayList<>();
|
||||
private static @NotNull List<PluginId> getNonOptionalDependenciesIds(@NotNull IdeaPluginDescriptorImpl descriptor) {
|
||||
List<PluginId> dependencies = new ArrayList<>();
|
||||
for (PluginDependency dependency : descriptor.pluginDependencies) {
|
||||
if (dependency.isOptional()) {
|
||||
continue;
|
||||
@@ -1314,17 +1308,7 @@ public final class PluginManagerCore {
|
||||
for (ModuleDependenciesDescriptor.PluginReference plugin : descriptor.dependencies.plugins) {
|
||||
dependencies.add(plugin.id);
|
||||
}
|
||||
return Collections.unmodifiableList(dependencies);
|
||||
}
|
||||
|
||||
private static @NotNull List<IdeaPluginDescriptorImpl> getOnlyEnabledPlugins(@NotNull List<IdeaPluginDescriptorImpl> sortedAll) {
|
||||
List<IdeaPluginDescriptorImpl> enabledPlugins = new ArrayList<>(sortedAll.size());
|
||||
for (IdeaPluginDescriptorImpl descriptor : sortedAll) {
|
||||
if (descriptor.isEnabled()) {
|
||||
enabledPlugins.add(descriptor);
|
||||
}
|
||||
}
|
||||
return enabledPlugins;
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
public static synchronized boolean isUpdatedBundledPlugin(@NotNull PluginDescriptor plugin) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ide.plugins
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.extensions.PluginId
|
||||
import com.intellij.util.lang.Java11Shim
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
@@ -8,73 +9,116 @@ import org.jetbrains.annotations.TestOnly
|
||||
|
||||
// if otherwise not specified, `module` in terms of v2 plugin model
|
||||
@ApiStatus.Internal
|
||||
class PluginSet(
|
||||
class PluginSet internal constructor(
|
||||
@JvmField val allPlugins: List<IdeaPluginDescriptorImpl>,
|
||||
enabledPlugins: List<IdeaPluginDescriptorImpl>,
|
||||
checkModulesDependencies: Boolean = true,
|
||||
@JvmField val enabledPlugins: List<IdeaPluginDescriptorImpl>,
|
||||
private val enabledModuleMap: Map<String, PluginContentDescriptor.ModuleItem>,
|
||||
private val enabledPluginAndV1ModuleMap: Map<PluginId, IdeaPluginDescriptorImpl>,
|
||||
) {
|
||||
@JvmField val enabledPlugins: List<IdeaPluginDescriptorImpl> = Java11Shim.INSTANCE.copyOf(enabledPlugins)
|
||||
private val enabledPluginAndV1ModuleMap: Map<PluginId, IdeaPluginDescriptorImpl>
|
||||
// module map in a new v2 format
|
||||
private val moduleMap: Map<String, PluginContentDescriptor.ModuleItem>
|
||||
|
||||
init {
|
||||
val enabledPluginAndV1ModuleMap = HashMap<PluginId, IdeaPluginDescriptorImpl>(enabledPlugins.size)
|
||||
val moduleMap = HashMap<String, PluginContentDescriptor.ModuleItem>()
|
||||
for (descriptor in enabledPlugins) {
|
||||
addWithV1Modules(enabledPluginAndV1ModuleMap, descriptor)
|
||||
|
||||
m@ for (item in descriptor.content.modules) {
|
||||
if (checkModulesDependencies) {
|
||||
for (ref in item.requireDescriptor().dependencies.modules) {
|
||||
if (!moduleMap.containsKey(ref.name)) {
|
||||
continue@m
|
||||
}
|
||||
}
|
||||
for (ref in item.requireDescriptor().dependencies.plugins) {
|
||||
if (!enabledPluginAndV1ModuleMap.containsKey(ref.id)) {
|
||||
continue@m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moduleMap.putIfAbsent(item.name, item)?.let {
|
||||
throw RuntimeException("Duplicated module name (first=$it, second=$item)")
|
||||
companion object {
|
||||
// special case - raw plugin set where everything is enabled and resolved
|
||||
fun createRawPluginSet(plugins: List<IdeaPluginDescriptorImpl>): PluginSet {
|
||||
val java11Shim = Java11Shim.INSTANCE
|
||||
val enabledModuleV2Ids = HashMap<String, PluginContentDescriptor.ModuleItem>()
|
||||
val enabledPluginAndModuleV1Map = HashMap<PluginId, IdeaPluginDescriptorImpl>(plugins.size)
|
||||
for (descriptor in plugins) {
|
||||
addWithV1Modules(enabledPluginAndModuleV1Map, descriptor)
|
||||
for (item in descriptor.content.modules) {
|
||||
enabledModuleV2Ids.put(item.name, item)
|
||||
}
|
||||
}
|
||||
return PluginSet(
|
||||
allPlugins = plugins,
|
||||
enabledPlugins = plugins,
|
||||
enabledModuleMap = java11Shim.copyOf(enabledModuleV2Ids),
|
||||
enabledPluginAndV1ModuleMap = java11Shim.copyOf(enabledPluginAndModuleV1Map),
|
||||
)
|
||||
}
|
||||
|
||||
if (descriptor.packagePrefix != null) {
|
||||
val pluginAsModuleItem = PluginContentDescriptor.ModuleItem(name = descriptor.id.idString, configFile = null)
|
||||
pluginAsModuleItem.descriptor = descriptor
|
||||
moduleMap[pluginAsModuleItem.name] = pluginAsModuleItem
|
||||
fun createPluginSet(allPlugins: List<IdeaPluginDescriptorImpl>, enabledPlugins: List<IdeaPluginDescriptorImpl>): PluginSet {
|
||||
val enabledModuleV2Ids = HashMap<String, PluginContentDescriptor.ModuleItem>()
|
||||
val enabledPluginAndModuleV1Map = HashMap<PluginId, IdeaPluginDescriptorImpl>(enabledPlugins.size)
|
||||
|
||||
val log = PluginManagerCore.getLogger()
|
||||
val isDebugLogEnabled = log.isDebugEnabled || !System.getProperty("plugin.classloader.debug", "").isEmpty()
|
||||
for (descriptor in enabledPlugins) {
|
||||
addWithV1Modules(enabledPluginAndModuleV1Map, descriptor)
|
||||
checkModules(descriptor, enabledPluginAndModuleV1Map, enabledModuleV2Ids, isDebugLogEnabled, log)
|
||||
}
|
||||
|
||||
val java11Shim = Java11Shim.INSTANCE
|
||||
return PluginSet(
|
||||
allPlugins = java11Shim.copyOf(allPlugins),
|
||||
enabledPlugins = java11Shim.copyOf(enabledPlugins),
|
||||
enabledModuleMap = java11Shim.copyOf(enabledModuleV2Ids),
|
||||
enabledPluginAndV1ModuleMap = java11Shim.copyOf(enabledPluginAndModuleV1Map),
|
||||
)
|
||||
}
|
||||
|
||||
fun addWithV1Modules(result: MutableMap<PluginId, IdeaPluginDescriptorImpl>, descriptor: IdeaPluginDescriptorImpl) {
|
||||
result.put(descriptor.id, descriptor)
|
||||
for (module in descriptor.modules) {
|
||||
result.put(module, descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
val java11Shim = Java11Shim.INSTANCE
|
||||
this.enabledPluginAndV1ModuleMap = java11Shim.copyOf(enabledPluginAndV1ModuleMap)
|
||||
this.moduleMap = java11Shim.copyOf(moduleMap)
|
||||
fun getOnlyEnabledPlugins(sortedAll: Collection<IdeaPluginDescriptorImpl>): List<IdeaPluginDescriptorImpl> {
|
||||
return sortedAll.filterTo(ArrayList(sortedAll.size)) { it.isEnabled }
|
||||
}
|
||||
|
||||
fun checkModules(descriptor: IdeaPluginDescriptorImpl,
|
||||
enabledPluginIds: Map<PluginId, IdeaPluginDescriptorImpl>,
|
||||
enabledModuleV2Ids: MutableMap<String, PluginContentDescriptor.ModuleItem>,
|
||||
isDebugLogEnabled: Boolean,
|
||||
log: Logger) {
|
||||
m@ for (item in descriptor.content.modules) {
|
||||
for (ref in item.requireDescriptor().dependencies.modules) {
|
||||
if (!enabledModuleV2Ids.containsKey(ref.name)) {
|
||||
if (isDebugLogEnabled) {
|
||||
log.info("Module ${item.name} is not enabled because dependency ${ref.name} is not available")
|
||||
}
|
||||
continue@m
|
||||
}
|
||||
}
|
||||
for (ref in item.requireDescriptor().dependencies.plugins) {
|
||||
if (!enabledPluginIds.containsKey(ref.id)) {
|
||||
if (isDebugLogEnabled) {
|
||||
log.info("Module ${item.name} is not enabled because dependency ${ref.id} is not available")
|
||||
}
|
||||
continue@m
|
||||
}
|
||||
}
|
||||
enabledModuleV2Ids.put(item.name, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun getUnsortedEnabledModules(): Collection<PluginContentDescriptor.ModuleItem> = ArrayList(moduleMap.values)
|
||||
fun getUnsortedEnabledModules(): Collection<PluginContentDescriptor.ModuleItem> = ArrayList(enabledModuleMap.values)
|
||||
|
||||
fun isPluginEnabled(id: PluginId) = enabledPluginAndV1ModuleMap.containsKey(id)
|
||||
|
||||
fun findEnabledPlugin(id: PluginId): IdeaPluginDescriptorImpl? = enabledPluginAndV1ModuleMap[id]
|
||||
fun findEnabledPlugin(id: PluginId): IdeaPluginDescriptorImpl? = enabledPluginAndV1ModuleMap.get(id)
|
||||
|
||||
fun findEnabledModule(id: String): IdeaPluginDescriptorImpl? = moduleMap[id]?.requireDescriptor()
|
||||
fun findEnabledModule(id: String): IdeaPluginDescriptorImpl? = enabledModuleMap.get(id)?.requireDescriptor()
|
||||
|
||||
fun isModuleEnabled(id: String) = moduleMap.containsKey(id)
|
||||
fun isModuleEnabled(id: String) = enabledModuleMap.containsKey(id)
|
||||
|
||||
fun concat(descriptor: IdeaPluginDescriptorImpl): PluginSet {
|
||||
return PluginSet(allPlugins = allPlugins.plus(descriptor),
|
||||
enabledPlugins = Java11Shim.INSTANCE.copyOf(enabledPlugins.plus(descriptor)))
|
||||
fun enablePlugin(descriptor: IdeaPluginDescriptorImpl): PluginSet {
|
||||
// in tests or on install plugin is not in all plugins
|
||||
// linear search is ok here - not a hot method
|
||||
PluginManagerCore.getLogger().assertTrue(!enabledPlugins.contains(descriptor))
|
||||
return createPluginSet(allPlugins = if (allPlugins.contains(descriptor)) allPlugins else allPlugins.plus(descriptor),
|
||||
enabledPlugins = enabledPlugins.plus(descriptor))
|
||||
}
|
||||
|
||||
private fun addWithV1Modules(result: MutableMap<PluginId, IdeaPluginDescriptorImpl>, descriptor: IdeaPluginDescriptorImpl) {
|
||||
result[descriptor.id] = descriptor
|
||||
for (module in descriptor.modules) {
|
||||
result[module] = descriptor
|
||||
}
|
||||
fun updateEnabledPlugins(): PluginSet {
|
||||
return createPluginSet(allPlugins = allPlugins, enabledPlugins = getOnlyEnabledPlugins(allPlugins))
|
||||
}
|
||||
|
||||
fun removePluginAndUpdateEnabledPlugins(descriptor: IdeaPluginDescriptorImpl): PluginSet {
|
||||
// not just remove from enabledPlugins - maybe another plugins in list also disabled as result of plugin unloading
|
||||
val allPlugins = allPlugins.minus(descriptor)
|
||||
return createPluginSet(allPlugins = allPlugins, enabledPlugins = getOnlyEnabledPlugins(allPlugins))
|
||||
}
|
||||
}
|
||||
@@ -113,8 +113,7 @@ public final class PluginClassLoader extends UrlClassLoader implements PluginAwa
|
||||
logStream = logStreamCandidate;
|
||||
}
|
||||
|
||||
private ClassLoader[] parents;
|
||||
private IdeaPluginDescriptorImpl[] dependencies;
|
||||
private IdeaPluginDescriptorImpl[] parents;
|
||||
|
||||
// cache of computed list of all parents (not only direct)
|
||||
private volatile ClassLoader[] allParents;
|
||||
@@ -143,7 +142,7 @@ public final class PluginClassLoader extends UrlClassLoader implements PluginAwa
|
||||
}
|
||||
|
||||
public PluginClassLoader(@NotNull UrlClassLoader.Builder builder,
|
||||
@NotNull ClassLoader @NotNull [] parents,
|
||||
@NotNull IdeaPluginDescriptorImpl @NotNull [] dependencies,
|
||||
@NotNull PluginDescriptor pluginDescriptor,
|
||||
@Nullable Path pluginRoot,
|
||||
@NotNull ClassLoader coreLoader) {
|
||||
@@ -152,12 +151,11 @@ public final class PluginClassLoader extends UrlClassLoader implements PluginAwa
|
||||
instanceId = instanceIdProducer.incrementAndGet();
|
||||
|
||||
this.resolveScopeManager = (p1, p2, p3) -> null;
|
||||
this.parents = parents;
|
||||
this.pluginDescriptor = pluginDescriptor;
|
||||
pluginId = pluginDescriptor.getPluginId();
|
||||
this.packagePrefix = null;
|
||||
this.coreLoader = coreLoader;
|
||||
checkNoCoreInParents(parents, coreLoader);
|
||||
this.parents = dependencies;
|
||||
|
||||
libDirectories = new SmartList<>();
|
||||
if (pluginRoot != null) {
|
||||
@@ -170,8 +168,7 @@ public final class PluginClassLoader extends UrlClassLoader implements PluginAwa
|
||||
|
||||
public PluginClassLoader(@NotNull List<Path> files,
|
||||
@NotNull ClassPath classPath,
|
||||
ClassLoader[] parents,
|
||||
IdeaPluginDescriptorImpl[] dependencies,
|
||||
@NotNull IdeaPluginDescriptorImpl @NotNull [] dependencies,
|
||||
@NotNull PluginDescriptor pluginDescriptor,
|
||||
@NotNull ClassLoader coreLoader,
|
||||
@Nullable ResolveScopeManager resolveScopeManager,
|
||||
@@ -182,16 +179,11 @@ public final class PluginClassLoader extends UrlClassLoader implements PluginAwa
|
||||
instanceId = instanceIdProducer.incrementAndGet();
|
||||
|
||||
this.resolveScopeManager = resolveScopeManager == null ? (p1, p2, p3) -> null : resolveScopeManager;
|
||||
this.parents = parents;
|
||||
this.dependencies = dependencies;
|
||||
this.parents = dependencies;
|
||||
this.pluginDescriptor = pluginDescriptor;
|
||||
pluginId = pluginDescriptor.getPluginId();
|
||||
this.packagePrefix = (packagePrefix == null || packagePrefix.endsWith(".")) ? packagePrefix : (packagePrefix + '.');
|
||||
this.coreLoader = coreLoader;
|
||||
if (parents != null) {
|
||||
checkNoCoreInParents(parents, coreLoader);
|
||||
}
|
||||
|
||||
this.libDirectories = libDirectories;
|
||||
}
|
||||
|
||||
@@ -199,17 +191,6 @@ public final class PluginClassLoader extends UrlClassLoader implements PluginAwa
|
||||
return libDirectories;
|
||||
}
|
||||
|
||||
private static void checkNoCoreInParents(@NotNull ClassLoader @NotNull [] parents, @NotNull ClassLoader coreLoader) {
|
||||
if (PluginClassLoader.class.desiredAssertionStatus()) {
|
||||
for (ClassLoader parent : parents) {
|
||||
if (parent == coreLoader) {
|
||||
Logger.getInstance(PluginClassLoader.class).error("Core loader must be not specified in parents " +
|
||||
"(parents=" + Arrays.toString(parents) + ", coreLoader=" + coreLoader + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getPackagePrefix() {
|
||||
return packagePrefix;
|
||||
@@ -352,9 +333,6 @@ public final class PluginClassLoader extends UrlClassLoader implements PluginAwa
|
||||
return result;
|
||||
}
|
||||
|
||||
initParents();
|
||||
|
||||
assert parents != null;
|
||||
if (parents.length == 0) {
|
||||
result = new ClassLoader[]{coreLoader};
|
||||
allParents = result;
|
||||
@@ -363,17 +341,15 @@ public final class PluginClassLoader extends UrlClassLoader implements PluginAwa
|
||||
|
||||
Set<ClassLoader> parentSet = new LinkedHashSet<>();
|
||||
Deque<ClassLoader> queue = new ArrayDeque<>();
|
||||
Collections.addAll(queue, parents);
|
||||
collectClassLoaders(queue);
|
||||
ClassLoader classLoader;
|
||||
while ((classLoader = queue.pollFirst()) != null) {
|
||||
if (classLoader == coreLoader || !parentSet.add(classLoader)) {
|
||||
if (!parentSet.add(classLoader)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (classLoader instanceof PluginClassLoader) {
|
||||
PluginClassLoader parent = (PluginClassLoader)classLoader;
|
||||
parent.initParents();
|
||||
Collections.addAll(queue, parent.parents);
|
||||
((PluginClassLoader)classLoader).collectClassLoaders(queue);
|
||||
}
|
||||
}
|
||||
parentSet.add(coreLoader);
|
||||
@@ -383,21 +359,13 @@ public final class PluginClassLoader extends UrlClassLoader implements PluginAwa
|
||||
return result;
|
||||
}
|
||||
|
||||
private void initParents() {
|
||||
IdeaPluginDescriptorImpl[] dependencies = this.dependencies;
|
||||
if (dependencies == null || parents != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ClassLoader> list = new ArrayList<>(dependencies.length);
|
||||
for (IdeaPluginDescriptorImpl dependency : dependencies) {
|
||||
ClassLoader loader = dependency.classLoader;
|
||||
if (loader != null) {
|
||||
list.add(loader);
|
||||
private void collectClassLoaders(@NotNull Deque<ClassLoader> queue) {
|
||||
for (IdeaPluginDescriptorImpl parent : parents) {
|
||||
ClassLoader classLoader = parent.classLoader;
|
||||
if (classLoader != null && classLoader != coreLoader) {
|
||||
queue.add(classLoader);
|
||||
}
|
||||
}
|
||||
parents = list.toArray(EMPTY_CLASS_LOADER_ARRAY);
|
||||
this.dependencies = null;
|
||||
}
|
||||
|
||||
public void clearParentListCache() {
|
||||
@@ -615,22 +583,24 @@ public final class PluginClassLoader extends UrlClassLoader implements PluginAwa
|
||||
|
||||
@TestOnly
|
||||
@ApiStatus.Internal
|
||||
public @Nullable List<ClassLoader> _getParents() {
|
||||
ClassLoader[] parents = this.parents;
|
||||
public @NotNull List<IdeaPluginDescriptorImpl> _getParents() {
|
||||
//noinspection SSBasedInspection
|
||||
return parents == null ? null : Collections.unmodifiableList(Arrays.asList(parents));
|
||||
return Collections.unmodifiableList(Arrays.asList(parents));
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void attachParent(@NotNull ClassLoader classLoader) {
|
||||
if (parents == null) {
|
||||
initParents();
|
||||
public void attachParent(@NotNull IdeaPluginDescriptorImpl parent) {
|
||||
//noinspection SSBasedInspection
|
||||
if (Arrays.stream(parents).anyMatch(it -> it == parent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int length = parents.length;
|
||||
ClassLoader[] result = new ClassLoader[length + 1];
|
||||
IdeaPluginDescriptorImpl[] result = new IdeaPluginDescriptorImpl[length + 1];
|
||||
System.arraycopy(parents, 0, result, 0, length);
|
||||
result[length] = classLoader;
|
||||
result[length] = parent;
|
||||
parents = result;
|
||||
allParents = null;
|
||||
parentListCacheIdCounter.incrementAndGet();
|
||||
}
|
||||
|
||||
@@ -638,21 +608,18 @@ public final class PluginClassLoader extends UrlClassLoader implements PluginAwa
|
||||
* You must clear allParents cache for all loaded plugins.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public boolean detachParent(@NotNull ClassLoader classLoader) {
|
||||
if (parents == null) {
|
||||
initParents();
|
||||
}
|
||||
|
||||
public boolean detachParent(@NotNull IdeaPluginDescriptorImpl parent) {
|
||||
for (int i = 0; i < parents.length; i++) {
|
||||
if (classLoader != parents[i]) {
|
||||
if (parent != parents[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int length = parents.length;
|
||||
ClassLoader[] result = new ClassLoader[length - 1];
|
||||
IdeaPluginDescriptorImpl[] result = new IdeaPluginDescriptorImpl[length - 1];
|
||||
System.arraycopy(parents, 0, result, 0, i);
|
||||
System.arraycopy(parents, i + 1, result, i, length - i - 1);
|
||||
parents = result;
|
||||
allParents = null;
|
||||
parentListCacheIdCounter.incrementAndGet();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -45,16 +45,15 @@ internal class ClassLoaderTreeChecker(private val unloadedMainDescriptor: IdeaPl
|
||||
}
|
||||
|
||||
@Suppress("TestOnlyProblems")
|
||||
val parents = classLoader._getParents() ?: return
|
||||
|
||||
val parents = classLoader._getParents()
|
||||
for (unloadedClassLoader in classLoaders) {
|
||||
if (parents.contains(unloadedClassLoader)) {
|
||||
if (parents.any { it.classLoader === unloadedClassLoader }) {
|
||||
LOG.error("$classLoader references via parents $unloadedClassLoader that must be unloaded")
|
||||
}
|
||||
}
|
||||
|
||||
for (parent in parents) {
|
||||
if (parent is PluginClassLoader && parent.pluginId == unloadedMainDescriptor.pluginId) {
|
||||
if (parent.pluginId == unloadedMainDescriptor.pluginId) {
|
||||
LOG.error("$classLoader references via parents $parent that must be unloaded")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,6 @@ import org.jetbrains.annotations.NonNls
|
||||
import java.awt.KeyboardFocusManager
|
||||
import java.awt.Window
|
||||
import java.nio.channels.FileChannel
|
||||
import java.nio.file.FileVisitResult
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.StandardOpenOption
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -94,7 +93,7 @@ import javax.swing.ToolTipManager
|
||||
import kotlin.collections.component1
|
||||
import kotlin.collections.component2
|
||||
|
||||
private val LOG = logger<DynamicPlugins>()
|
||||
private val LOG = logger<DynamicPlugins>()
|
||||
private val classloadersFromUnloadedPlugins = mutableMapOf<PluginId, WeakList<PluginClassLoader>>()
|
||||
|
||||
object DynamicPlugins {
|
||||
@@ -294,9 +293,9 @@ object DynamicPlugins {
|
||||
if (dependencyMessage == null && checkImplementationDetailDependencies) {
|
||||
val contextWithImplementationDetails = context.toMutableList()
|
||||
contextWithImplementationDetails.add(descriptor)
|
||||
processImplementationDetailDependenciesOnPlugin(descriptor, contextWithImplementationDetails::add)
|
||||
processImplementationDetailDependenciesOnPlugin(descriptor, pluginSet, contextWithImplementationDetails::add)
|
||||
|
||||
processImplementationDetailDependenciesOnPlugin(descriptor) { dependentDescriptor ->
|
||||
processImplementationDetailDependenciesOnPlugin(descriptor, pluginSet) { dependentDescriptor ->
|
||||
// don't check a plugin that is an implementation-detail dependency on the current plugin if it has other disabled dependencies
|
||||
// and won't be loaded anyway
|
||||
if (findMissingRequiredDependency(dependentDescriptor, contextWithImplementationDetails) == null) {
|
||||
@@ -387,13 +386,8 @@ object DynamicPlugins {
|
||||
return result
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getPluginUnloadingTask(pluginDescriptor: IdeaPluginDescriptorImpl, options: UnloadPluginOptions): Runnable {
|
||||
return Runnable { unloadPlugin(pluginDescriptor, options) }
|
||||
}
|
||||
|
||||
data class UnloadPluginOptions(
|
||||
var disable: Boolean = false,
|
||||
var disable: Boolean = true,
|
||||
var isUpdate: Boolean = false,
|
||||
var save: Boolean = true,
|
||||
var requireMemorySnapshot: Boolean = false,
|
||||
@@ -430,17 +424,21 @@ object DynamicPlugins {
|
||||
}
|
||||
}
|
||||
|
||||
fun unloadAndUninstallPlugin(pluginDescriptor: IdeaPluginDescriptorImpl): Boolean {
|
||||
return unloadPlugin(pluginDescriptor, UnloadPluginOptions(disable = false))
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun unloadPlugin(pluginDescriptor: IdeaPluginDescriptorImpl, options: UnloadPluginOptions = UnloadPluginOptions()): Boolean {
|
||||
fun unloadPlugin(pluginDescriptor: IdeaPluginDescriptorImpl,
|
||||
options: UnloadPluginOptions = UnloadPluginOptions(disable = true)): Boolean {
|
||||
val app = ApplicationManager.getApplication() as ApplicationImpl
|
||||
val pluginId = pluginDescriptor.pluginId
|
||||
val pluginSet = PluginManagerCore.getPluginSet()
|
||||
|
||||
if (options.checkImplementationDetailDependencies) {
|
||||
processImplementationDetailDependenciesOnPlugin(pluginDescriptor) { dependentDescriptor ->
|
||||
processImplementationDetailDependenciesOnPlugin(pluginDescriptor, pluginSet) { dependentDescriptor ->
|
||||
dependentDescriptor.isEnabled = false
|
||||
unloadPlugin(dependentDescriptor, UnloadPluginOptions(disable = true,
|
||||
save = false,
|
||||
unloadPlugin(dependentDescriptor, UnloadPluginOptions(save = false,
|
||||
waitForClassloaderUnload = false,
|
||||
checkImplementationDetailDependencies = false))
|
||||
true
|
||||
@@ -473,7 +471,7 @@ object DynamicPlugins {
|
||||
unloadDependencyDescriptors(pluginDescriptor, pluginSet, classLoaders)
|
||||
unloadPluginDescriptorNotRecursively(pluginDescriptor)
|
||||
|
||||
clearPluginClassLoaderParentListCache()
|
||||
clearPluginClassLoaderParentListCache(pluginSet)
|
||||
|
||||
app.extensionArea.clearUserCache()
|
||||
for (project in ProjectUtil.getOpenProjects()) {
|
||||
@@ -506,11 +504,10 @@ object DynamicPlugins {
|
||||
(ProjectManager.getInstanceIfCreated() as? ProjectManagerImpl)?.disposeDefaultProjectAndCleanupComponentsForDynamicPluginTests()
|
||||
|
||||
if (options.disable) {
|
||||
// update list of disabled plugins
|
||||
PluginManager.getInstance().setPlugins(PluginManagerCore.getPluginSet().allPlugins)
|
||||
PluginManagerCore.setPluginSet(pluginSet.updateEnabledPlugins())
|
||||
}
|
||||
else {
|
||||
PluginManager.getInstance().setPlugins(PluginManagerCore.getPluginSet().allPlugins.minus(pluginDescriptor))
|
||||
PluginManagerCore.setPluginSet(pluginSet.removePluginAndUpdateEnabledPlugins(pluginDescriptor))
|
||||
}
|
||||
}
|
||||
finally {
|
||||
@@ -628,7 +625,7 @@ object DynamicPlugins {
|
||||
classLoaders.add(classLoader)
|
||||
classLoader.state = PluginClassLoader.UNLOAD_IN_PROGRESS
|
||||
}
|
||||
else if (!classLoader.detachParent(dependencyClassloader)) {
|
||||
else if (!classLoader.detachParent(dependencyPlugin)) {
|
||||
LOG.warn("Classloader $dependencyClassloader doesn't have $classLoader as parent")
|
||||
}
|
||||
}
|
||||
@@ -806,18 +803,19 @@ object DynamicPlugins {
|
||||
|
||||
val loadStartTime = System.currentTimeMillis()
|
||||
val app = ApplicationManager.getApplication() as ApplicationImpl
|
||||
val pluginSet = PluginManagerCore.getPluginSet().concat(pluginDescriptor)
|
||||
|
||||
val pluginSet = PluginManagerCore.getPluginSet().enablePlugin(pluginDescriptor)
|
||||
val classLoaderConfigurator = ClassLoaderConfigurator(pluginSet)
|
||||
classLoaderConfigurator.configure(pluginDescriptor)
|
||||
|
||||
app.messageBus.syncPublisher(DynamicPluginListener.TOPIC).beforePluginLoaded(pluginDescriptor)
|
||||
app.runWriteAction {
|
||||
try {
|
||||
addToLoadedPlugins(pluginDescriptor)
|
||||
PluginManagerCore.setPluginSet(pluginSet)
|
||||
val listenerCallbacks = mutableListOf<Runnable>()
|
||||
loadPluginDescriptor(pluginDescriptor, app, listenerCallbacks)
|
||||
loadOptionalDependenciesOnPlugin(pluginDescriptor, classLoaderConfigurator, pluginSet, listenerCallbacks)
|
||||
clearPluginClassLoaderParentListCache()
|
||||
clearPluginClassLoaderParentListCache(pluginSet)
|
||||
|
||||
for (openProject in ProjectUtil.getOpenProjects()) {
|
||||
(CachedValuesManager.getManager(openProject) as CachedValuesManagerImpl).clearCachedValues()
|
||||
@@ -837,7 +835,7 @@ object DynamicPlugins {
|
||||
|
||||
if (checkImplementationDetailDependencies) {
|
||||
var implementationDetailsLoadedWithoutRestart = true
|
||||
processImplementationDetailDependenciesOnPlugin(pluginDescriptor) { dependentDescriptor ->
|
||||
processImplementationDetailDependenciesOnPlugin(pluginDescriptor, pluginSet) { dependentDescriptor ->
|
||||
val dependencies = dependentDescriptor.pluginDependencies
|
||||
if (dependencies.all { it.isOptional || PluginManagerCore.getPlugin(it.pluginId) != null }) {
|
||||
if (!loadPlugin(dependentDescriptor, checkImplementationDetailDependencies = false)) {
|
||||
@@ -851,26 +849,6 @@ object DynamicPlugins {
|
||||
return true
|
||||
}
|
||||
|
||||
private fun addToLoadedPlugins(pluginDescriptor: IdeaPluginDescriptorImpl) {
|
||||
var foundExistingPlugin = false
|
||||
val newPlugins = PluginManagerCore.getPluginSet().allPlugins.map {
|
||||
if (it.pluginId == pluginDescriptor.pluginId) {
|
||||
foundExistingPlugin = true
|
||||
pluginDescriptor
|
||||
}
|
||||
else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
if (foundExistingPlugin) {
|
||||
PluginManager.getInstance().setPlugins(newPlugins)
|
||||
}
|
||||
else {
|
||||
PluginManager.getInstance().setPlugins(PluginManagerCore.getPluginSet().allPlugins.plus(pluginDescriptor))
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onPluginUnload(parentDisposable: Disposable, callback: Runnable) {
|
||||
ApplicationManager.getApplication().messageBus.connect(parentDisposable)
|
||||
@@ -986,15 +964,19 @@ object DynamicPlugins {
|
||||
}
|
||||
|
||||
private fun processImplementationDetailDependenciesOnPlugin(pluginDescriptor: IdeaPluginDescriptorImpl,
|
||||
processor: (descriptor: IdeaPluginDescriptorImpl) -> Boolean) {
|
||||
PluginManager.getInstance().processAllBackwardDependencies(pluginDescriptor, false) { loadedDescriptor ->
|
||||
if (loadedDescriptor.isImplementationDetail) {
|
||||
if (processor(loadedDescriptor)) FileVisitResult.CONTINUE else FileVisitResult.TERMINATE
|
||||
}
|
||||
else {
|
||||
FileVisitResult.CONTINUE
|
||||
}
|
||||
}
|
||||
pluginSet: PluginSet,
|
||||
processor: (descriptor: IdeaPluginDescriptorImpl) -> Boolean) {
|
||||
processDependenciesOnPlugin(dependencyPlugin = pluginDescriptor,
|
||||
pluginSet = pluginSet,
|
||||
loadStateFilter = LoadStateFilter.ANY,
|
||||
onlyOptional = false) { _, module ->
|
||||
if (module.isImplementationDetail) {
|
||||
processor(module)
|
||||
}
|
||||
else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1027,18 +1009,10 @@ private fun loadOptionalDependenciesOnPlugin(dependencyPlugin: IdeaPluginDescrip
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearPluginClassLoaderParentListCache() {
|
||||
for (descriptor in PluginManagerCore.getLoadedPlugins(null)) {
|
||||
clearPluginClassLoaderParentListCache(descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearPluginClassLoaderParentListCache(descriptor: IdeaPluginDescriptorImpl) {
|
||||
(descriptor.classLoader as? PluginClassLoader ?: return).clearParentListCache()
|
||||
for (dependency in descriptor.pluginDependencies) {
|
||||
dependency.subDescriptor?.let {
|
||||
clearPluginClassLoaderParentListCache(it)
|
||||
}
|
||||
private fun clearPluginClassLoaderParentListCache(pluginSet: PluginSet) {
|
||||
// yes, clear not only enabled plugins, but all, just to be sure, it is cheap operation
|
||||
for (descriptor in pluginSet.allPlugins) {
|
||||
(descriptor.classLoader as? PluginClassLoader ?: continue).clearParentListCache()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1084,11 +1058,28 @@ private fun createDisposeTreePredicate(pluginDescriptor: IdeaPluginDescriptorImp
|
||||
}
|
||||
}
|
||||
|
||||
private fun processOptionalDependenciesOnPlugin(dependencyPlugin: IdeaPluginDescriptorImpl,
|
||||
pluginSet: PluginSet,
|
||||
isLoaded: Boolean,
|
||||
processor: (pluginDescriptor: IdeaPluginDescriptorImpl,
|
||||
moduleDescriptor: IdeaPluginDescriptorImpl) -> Boolean) {
|
||||
private fun processOptionalDependenciesOnPlugin(
|
||||
dependencyPlugin: IdeaPluginDescriptorImpl,
|
||||
pluginSet: PluginSet,
|
||||
isLoaded: Boolean,
|
||||
processor: (pluginDescriptor: IdeaPluginDescriptorImpl, moduleDescriptor: IdeaPluginDescriptorImpl) -> Boolean,
|
||||
) {
|
||||
processDependenciesOnPlugin(
|
||||
dependencyPlugin = dependencyPlugin,
|
||||
pluginSet = pluginSet,
|
||||
onlyOptional = true,
|
||||
loadStateFilter = if (isLoaded) LoadStateFilter.LOADED else LoadStateFilter.NOT_LOADED,
|
||||
processor = processor,
|
||||
)
|
||||
}
|
||||
|
||||
private fun processDependenciesOnPlugin(
|
||||
dependencyPlugin: IdeaPluginDescriptorImpl,
|
||||
pluginSet: PluginSet,
|
||||
loadStateFilter: LoadStateFilter,
|
||||
onlyOptional: Boolean,
|
||||
processor: (pluginDescriptor: IdeaPluginDescriptorImpl, moduleDescriptor: IdeaPluginDescriptorImpl) -> Boolean,
|
||||
) {
|
||||
val wantedIds = HashSet<String>(1 + dependencyPlugin.content.modules.size)
|
||||
wantedIds.add(dependencyPlugin.id.idString)
|
||||
for (module in dependencyPlugin.content.modules) {
|
||||
@@ -1100,16 +1091,22 @@ private fun processOptionalDependenciesOnPlugin(dependencyPlugin: IdeaPluginDesc
|
||||
continue
|
||||
}
|
||||
|
||||
if (!processOptionalDependenciesInOldFormatOnPlugin(dependencyPlugin.id, plugin, isLoaded, processor)) {
|
||||
if (!processOptionalDependenciesInOldFormatOnPlugin(dependencyPluginId = dependencyPlugin.id,
|
||||
mainDescriptor = plugin,
|
||||
loadStateFilter = loadStateFilter,
|
||||
onlyOptional = onlyOptional,
|
||||
processor = processor)) {
|
||||
return
|
||||
}
|
||||
|
||||
for (moduleItem in plugin.content.modules) {
|
||||
val module = moduleItem.requireDescriptor()
|
||||
|
||||
val isModuleLoaded = module.classLoader != null
|
||||
if (isModuleLoaded != isLoaded) {
|
||||
continue
|
||||
if (loadStateFilter != LoadStateFilter.ANY) {
|
||||
val isModuleLoaded = module.classLoader != null
|
||||
if (isModuleLoaded != (loadStateFilter == LoadStateFilter.LOADED)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
for (item in module.dependencies.modules) {
|
||||
@@ -1117,30 +1114,52 @@ private fun processOptionalDependenciesOnPlugin(dependencyPlugin: IdeaPluginDesc
|
||||
return
|
||||
}
|
||||
}
|
||||
for (item in module.dependencies.plugins) {
|
||||
if (dependencyPlugin.id == item.id && !processor(plugin, module)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processOptionalDependenciesInOldFormatOnPlugin(dependencyPluginId: PluginId,
|
||||
mainDescriptor: IdeaPluginDescriptorImpl,
|
||||
isLoaded: Boolean,
|
||||
processor: (main: IdeaPluginDescriptorImpl, sub: IdeaPluginDescriptorImpl) -> Boolean): Boolean {
|
||||
private enum class LoadStateFilter {
|
||||
LOADED, NOT_LOADED, ANY
|
||||
}
|
||||
|
||||
private fun processOptionalDependenciesInOldFormatOnPlugin(
|
||||
dependencyPluginId: PluginId,
|
||||
mainDescriptor: IdeaPluginDescriptorImpl,
|
||||
loadStateFilter: LoadStateFilter,
|
||||
onlyOptional: Boolean,
|
||||
processor: (main: IdeaPluginDescriptorImpl, sub: IdeaPluginDescriptorImpl) -> Boolean
|
||||
): Boolean {
|
||||
for (dependency in mainDescriptor.pluginDependencies) {
|
||||
if (!dependency.isOptional) {
|
||||
if (!onlyOptional && dependency.pluginId == dependencyPluginId && !processor(mainDescriptor, mainDescriptor)) {
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
val subDescriptor = dependency.subDescriptor ?: continue
|
||||
val isModuleLoaded = subDescriptor.classLoader != null
|
||||
if (isModuleLoaded != isLoaded) {
|
||||
continue
|
||||
if (loadStateFilter != LoadStateFilter.ANY) {
|
||||
val isModuleLoaded = subDescriptor.classLoader != null
|
||||
if (isModuleLoaded != (loadStateFilter == LoadStateFilter.LOADED)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if (dependency.pluginId == dependencyPluginId && !processor(mainDescriptor, subDescriptor)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!processOptionalDependenciesInOldFormatOnPlugin(dependencyPluginId, subDescriptor, isLoaded, processor)) {
|
||||
if (!processOptionalDependenciesInOldFormatOnPlugin(
|
||||
dependencyPluginId = dependencyPluginId,
|
||||
mainDescriptor = subDescriptor,
|
||||
loadStateFilter = loadStateFilter,
|
||||
onlyOptional = onlyOptional,
|
||||
processor = processor)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public final class PluginInstaller {
|
||||
boolean isUpdate) {
|
||||
boolean uninstalledWithoutRestart = true;
|
||||
if (pluginDescriptor.isEnabled()) {
|
||||
DynamicPlugins.UnloadPluginOptions options = new DynamicPlugins.UnloadPluginOptions()
|
||||
DynamicPlugins.UnloadPluginOptions options = new DynamicPlugins.UnloadPluginOptions().withDisable(false)
|
||||
.withUpdate(isUpdate)
|
||||
.withWaitForClassloaderUnload(true);
|
||||
|
||||
|
||||
@@ -990,13 +990,18 @@ public final class StartupUtil {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> Set<E> copyOf(Set<? extends E> collection) {
|
||||
public <E> @NotNull Set<E> copyOf(Set<? extends E> collection) {
|
||||
return Set.copyOf(collection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> List<E> copyOf(List<? extends E> collection) {
|
||||
public <E> @NotNull List<E> copyOf(List<? extends E> collection) {
|
||||
return List.copyOf(collection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull <E> List<E> listOf(E[] collection) {
|
||||
return List.of(collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,26 +313,24 @@ public final class PluginDownloader {
|
||||
|
||||
public boolean tryInstallWithoutRestart(@Nullable JComponent ownerComponent) {
|
||||
assert myDescriptor instanceof IdeaPluginDescriptorImpl;
|
||||
final IdeaPluginDescriptorImpl descriptorImpl = (IdeaPluginDescriptorImpl)myDescriptor;
|
||||
if (!DynamicPlugins.allowLoadUnloadWithoutRestart(descriptorImpl)) {
|
||||
IdeaPluginDescriptorImpl descriptor = (IdeaPluginDescriptorImpl)myDescriptor;
|
||||
if (!DynamicPlugins.allowLoadUnloadWithoutRestart(descriptor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (myOldFile != null) {
|
||||
IdeaPluginDescriptor installedPlugin = PluginManagerCore.getPlugin(myDescriptor.getPluginId());
|
||||
IdeaPluginDescriptorImpl fullDescriptor = installedPlugin instanceof IdeaPluginDescriptorImpl ?
|
||||
(IdeaPluginDescriptorImpl)installedPlugin :
|
||||
null;
|
||||
if (fullDescriptor == null ||
|
||||
!DynamicPlugins.INSTANCE.unloadPlugin(fullDescriptor,
|
||||
new DynamicPlugins.UnloadPluginOptions()
|
||||
.withUpdate(true)
|
||||
.withWaitForClassloaderUnload(true))) {
|
||||
IdeaPluginDescriptorImpl installedPlugin = (IdeaPluginDescriptorImpl)PluginManagerCore.getPlugin(myDescriptor.getPluginId());
|
||||
// yes, if no installed plugin by id, it means that something goes wrong, so do not try to install and load
|
||||
if (installedPlugin == null || !DynamicPlugins.INSTANCE.unloadPlugin(descriptor,
|
||||
new DynamicPlugins.UnloadPluginOptions()
|
||||
.withDisable(false)
|
||||
.withUpdate(true)
|
||||
.withWaitForClassloaderUnload(true))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return PluginInstaller.installAndLoadDynamicPlugin(myFile.toPath(), ownerComponent, descriptorImpl);
|
||||
return PluginInstaller.installAndLoadDynamicPlugin(myFile.toPath(), ownerComponent, descriptor);
|
||||
}
|
||||
|
||||
private @Nullable File tryDownloadPlugin(@NotNull ProgressIndicator indicator, boolean showMessageOnError) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
package com.intellij.ide.plugins
|
||||
|
||||
import com.intellij.ide.plugins.cl.PluginAwareClassLoader
|
||||
import com.intellij.ide.plugins.cl.PluginClassLoader
|
||||
import com.intellij.openapi.util.BuildNumber
|
||||
import com.intellij.testFramework.assertions.Assertions.assertThat
|
||||
import com.intellij.testFramework.assertions.Assertions.assertThatThrownBy
|
||||
@@ -91,7 +90,7 @@ internal class ClassLoaderConfiguratorTest {
|
||||
val plugins = loadResult.getEnabledPlugins()
|
||||
assertThat(plugins).hasSize(2)
|
||||
|
||||
val classLoaderConfigurator = ClassLoaderConfigurator(PluginSet(plugins, plugins))
|
||||
val classLoaderConfigurator = ClassLoaderConfigurator(PluginSet.createPluginSet(plugins, plugins))
|
||||
plugins.forEach(classLoaderConfigurator::configure)
|
||||
return loadResult
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationEditorProv
|
||||
import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationState
|
||||
import com.intellij.openapi.startup.StartupActivity
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.use
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.testFramework.EdtRule
|
||||
import com.intellij.testFramework.ProjectRule
|
||||
@@ -90,7 +91,7 @@ class DynamicPluginsTest {
|
||||
app.messageBus.syncPublisher(UISettingsListener.TOPIC).uiSettingsChanged(UISettings())
|
||||
assertThat(receivedNotifications).hasSize(1)
|
||||
|
||||
DynamicPlugins.unloadPlugin(descriptor)
|
||||
DynamicPlugins.unloadAndUninstallPlugin(descriptor)
|
||||
app.messageBus.syncPublisher(UISettingsListener.TOPIC).uiSettingsChanged(UISettings())
|
||||
assertThat(receivedNotifications).hasSize(1)
|
||||
}
|
||||
@@ -105,18 +106,18 @@ class DynamicPluginsTest {
|
||||
DynamicPlugins.loadPlugin(descriptor)
|
||||
|
||||
DisabledPluginsState.saveDisabledPlugins(PathManager.getConfigDir(), builder.id)
|
||||
DynamicPlugins.unloadPlugin(descriptor, DynamicPlugins.UnloadPluginOptions(disable = true))
|
||||
DynamicPlugins.unloadAndUninstallPlugin(descriptor)
|
||||
assertThat(PluginManagerCore.getPlugin(descriptor.pluginId)?.pluginClassLoader as? PluginClassLoader).isNull()
|
||||
|
||||
DisabledPluginsState.saveDisabledPlugins(PathManager.getConfigDir())
|
||||
val newDescriptor = loadDescriptorInTest(path)
|
||||
ClassLoaderConfigurator(PluginManagerCore.getPluginSet().concat(newDescriptor)).configure(newDescriptor)
|
||||
ClassLoaderConfigurator(PluginManagerCore.getPluginSet().enablePlugin(newDescriptor)).configure(newDescriptor)
|
||||
DynamicPlugins.loadPlugin(newDescriptor)
|
||||
try {
|
||||
assertThat(PluginManagerCore.getPlugin(descriptor.pluginId)?.pluginClassLoader as? PluginClassLoader).isNotNull()
|
||||
}
|
||||
finally {
|
||||
DynamicPlugins.unloadPlugin(newDescriptor)
|
||||
DynamicPlugins.unloadAndUninstallPlugin(newDescriptor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,12 +126,12 @@ class DynamicPluginsTest {
|
||||
val data = System.currentTimeMillis().toString()
|
||||
|
||||
val extensionTag = "<applicationService serviceImplementation=\"${MyPersistentComponent::class.java.name}\"/>"
|
||||
val disposable = loadExtensionWithText(extensionTag, DynamicPlugins::class.java.classLoader)
|
||||
val disposable = loadExtensionWithText(extensionTag)
|
||||
val service = ApplicationManager.getApplication().getService(MyPersistentComponent::class.java)
|
||||
service.myState.stateData = data
|
||||
Disposer.dispose(disposable)
|
||||
|
||||
val disposable2 = loadExtensionWithText(extensionTag, DynamicPlugins::class.java.classLoader)
|
||||
val disposable2 = loadExtensionWithText(extensionTag)
|
||||
val service2 = ApplicationManager.getApplication().getService(MyPersistentComponent::class.java)
|
||||
assertThat(service2.myState.stateData).isEqualTo(data)
|
||||
Disposer.dispose(disposable2)
|
||||
@@ -208,32 +209,26 @@ class DynamicPluginsTest {
|
||||
// match production - on plugin load/unload ActionManager is already initialized
|
||||
val actionManager = ActionManager.getInstance()
|
||||
|
||||
val beforeList = PluginManagerCore.getLoadedPlugins()
|
||||
val plugin2Builder = PluginBuilder().randomId("bar").packagePrefix("bar")
|
||||
val pluginDescriptor = PluginBuilder()
|
||||
.randomId("foo")
|
||||
.packagePrefix("org.foo")
|
||||
.module(
|
||||
"intellij.org.foo",
|
||||
PluginBuilder().actions("""<group id="FooBarGroup"></group>""").packagePrefix("org.foo.bar").dependency(plugin2Builder.id)
|
||||
)
|
||||
val plugin1Disposable = loadPluginWithText(pluginDescriptor)
|
||||
assertThat(actionManager.getAction("FooBarGroup")).isNull()
|
||||
try {
|
||||
val pluginToDisposable = loadPluginWithText(plugin2Builder)
|
||||
try {
|
||||
assertThat(actionManager.getAction("FooBarGroup")).isNotNull()
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(pluginToDisposable)
|
||||
}
|
||||
assertThat(actionManager.getAction("FooBarGroup")).isNull()
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(plugin1Disposable)
|
||||
}
|
||||
runAndCheckThatNoNewPlugins {
|
||||
val dependency = PluginBuilder().randomId("dependency").packagePrefix("org.dependency")
|
||||
val dependent = PluginBuilder()
|
||||
.randomId("dependent")
|
||||
.packagePrefix("org.dependent")
|
||||
.module(
|
||||
"org.dependent",
|
||||
PluginBuilder().actions("""<group id="FooBarGroup"></group>""").packagePrefix("org.dependent.sub").pluginDependency(dependency.id)
|
||||
)
|
||||
loadPluginWithText(dependent).use {
|
||||
assertThat(actionManager.getAction("FooBarGroup")).isNull()
|
||||
|
||||
assertThat(PluginManagerCore.getLoadedPlugins()).isEqualTo(beforeList)
|
||||
runAndCheckThatNoNewPlugins {
|
||||
loadPluginWithText(dependency).use {
|
||||
assertThat(actionManager.getAction("FooBarGroup")).isNotNull()
|
||||
}
|
||||
}
|
||||
assertThat(actionManager.getAction("FooBarGroup")).isNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -305,54 +300,38 @@ class DynamicPluginsTest {
|
||||
PluginBuilder()
|
||||
.extensions("""<barExtension key="foo" implementationClass="y"/>""", "foo")
|
||||
.packagePrefix("foo1")
|
||||
.dependency(barBuilder.id)
|
||||
.pluginDependency(barBuilder.id)
|
||||
)
|
||||
val plugin1Disposable = loadPluginWithText(fooBuilder)
|
||||
try {
|
||||
loadPluginWithText(fooBuilder).use {
|
||||
val ep = ApplicationManager.getApplication().extensionArea.getExtensionPointIfRegistered<KeyedLazyInstanceEP<*>>("foo.barExtension")
|
||||
assertThat(ep).isNotNull()
|
||||
val plugin2Disposable = loadPluginWithText(barBuilder)
|
||||
try {
|
||||
loadPluginWithText(barBuilder).use {
|
||||
val extension = ep!!.extensionList.single()
|
||||
assertThat(extension.key).isEqualTo("foo")
|
||||
assertThat(extension.pluginDescriptor)
|
||||
.isEqualTo(PluginManagerCore.getPluginSet().findEnabledModule(fooBuilder.id)!!)
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(plugin2Disposable)
|
||||
.isEqualTo(PluginManagerCore.getPluginSet().findEnabledPlugin(PluginId.findId(fooBuilder.id)!!)!!)
|
||||
}
|
||||
assertThat(ep!!.extensionList).isEmpty()
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(plugin1Disposable)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadOptionalDependencyDescriptor() {
|
||||
val pluginOneBuilder = PluginBuilder().randomId("optionalDependencyDescriptor-one")
|
||||
val pluginOneDisposable = loadPluginWithText(pluginOneBuilder)
|
||||
val app = ApplicationManager.getApplication()
|
||||
try {
|
||||
loadPluginWithText(pluginOneBuilder).use {
|
||||
assertThat(app.getService(MyPersistentComponent::class.java)).isNull()
|
||||
val pluginTwoId = "optionalDependencyDescriptor-two_${Ksuid.generate()}"
|
||||
val pluginTwoDisposable = loadPluginWithOptionalDependency(
|
||||
loadPluginWithOptionalDependency(
|
||||
PluginBuilder().id(pluginTwoId),
|
||||
PluginBuilder().extensions("""<applicationService serviceImplementation="${MyPersistentComponent::class.java.name}"/>"""),
|
||||
pluginOneBuilder
|
||||
)
|
||||
try {
|
||||
).use {
|
||||
assertThat(app.getService(MyPersistentComponent::class.java)).isNotNull()
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(pluginTwoDisposable)
|
||||
}
|
||||
assertThat(PluginManagerCore.getPlugin(PluginId.getId(pluginTwoId))).isNull()
|
||||
assertThat(app.getService(MyPersistentComponent::class.java)).isNull()
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(pluginOneDisposable)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -371,31 +350,23 @@ class DynamicPluginsTest {
|
||||
.applicationListeners(
|
||||
"""<listener class="${MyUISettingsListener2::class.java.name}" topic="com.intellij.ide.ui.UISettingsListener"/>""")
|
||||
.packagePrefix("org.foo")
|
||||
.dependency(pluginTwoBuilder.id),
|
||||
.pluginDependency(pluginTwoBuilder.id),
|
||||
)
|
||||
val pluginOneDisposable = loadPluginWithText(pluginDescriptor)
|
||||
try {
|
||||
loadPluginWithText(pluginDescriptor).use {
|
||||
val app = ApplicationManager.getApplication()
|
||||
app.messageBus.syncPublisher(UISettingsListener.TOPIC).uiSettingsChanged(UISettings())
|
||||
assertThat(receivedNotifications).hasSize(1)
|
||||
|
||||
val pluginToDisposable = loadPluginWithText(pluginTwoBuilder)
|
||||
try {
|
||||
loadPluginWithText(pluginTwoBuilder).use {
|
||||
app.messageBus.syncPublisher(UISettingsListener.TOPIC).uiSettingsChanged(UISettings())
|
||||
assertThat(receivedNotifications).hasSize(2)
|
||||
assertThat(receivedNotifications2).hasSize(1)
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(pluginToDisposable)
|
||||
}
|
||||
|
||||
app.messageBus.syncPublisher(UISettingsListener.TOPIC).uiSettingsChanged(UISettings())
|
||||
assertThat(receivedNotifications).hasSize(3)
|
||||
assertThat(receivedNotifications2).hasSize(1)
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(pluginOneDisposable)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -444,44 +415,31 @@ class DynamicPluginsTest {
|
||||
@Test
|
||||
fun testProjectService() {
|
||||
val project = projectRule.project
|
||||
val disposable = loadExtensionWithText("""
|
||||
loadExtensionWithText("""
|
||||
<projectService serviceImplementation="${MyProjectService::class.java.name}"/>
|
||||
""".trimIndent(), DynamicPluginsTest::class.java.classLoader)
|
||||
try {
|
||||
""".trimIndent()).use {
|
||||
assertThat(project.getService(MyProjectService::class.java)).isNotNull()
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(disposable)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun extensionOnServiceDependency() {
|
||||
val project = projectRule.project
|
||||
StartupManagerImpl.addActivityEpListener(project)
|
||||
val disposable = loadExtensionWithText("""
|
||||
loadExtensionWithText("""
|
||||
<postStartupActivity implementation="${MyStartupActivity::class.java.name}"/>
|
||||
<projectService serviceImplementation="${MyProjectService::class.java.name}"/>
|
||||
""", DynamicPluginsTest::class.java.classLoader)
|
||||
try {
|
||||
""").use {
|
||||
assertThat(project.service<MyProjectService>().executed).isTrue()
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(disposable)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unloadEPWithDefaultAttributes() {
|
||||
val disposable = loadExtensionWithText(
|
||||
"<globalInspection implementationClass=\"${MyInspectionTool::class.java.name}\" cleanupTool=\"false\"/>",
|
||||
DynamicPlugins::class.java.classLoader)
|
||||
try {
|
||||
loadExtensionWithText(
|
||||
"<globalInspection implementationClass=\"${MyInspectionTool::class.java.name}\" cleanupTool=\"false\"/>").use {
|
||||
assertThat(InspectionEP.GLOBAL_INSPECTION.extensionList.any { it.implementationClass == MyInspectionTool::class.java.name }).isTrue()
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(disposable)
|
||||
}
|
||||
assertThat(InspectionEP.GLOBAL_INSPECTION.extensionList.any { it.implementationClass == MyInspectionTool::class.java.name }).isFalse()
|
||||
}
|
||||
|
||||
@@ -493,8 +451,7 @@ class DynamicPluginsTest {
|
||||
<bundleName>messages.CommonBundle</bundleName>
|
||||
<categoryKey>button.add</categoryKey>
|
||||
<className>${MyIntentionAction::class.java.name}</className>
|
||||
</intentionAction>""",
|
||||
DynamicPlugins::class.java.classLoader)
|
||||
</intentionAction>""")
|
||||
try {
|
||||
val intention = IntentionManagerImpl.EP_INTENTION_ACTIONS.extensionList.find { it.className == MyIntentionAction::class.java.name }
|
||||
assertThat(intention).isNotNull
|
||||
@@ -517,8 +474,7 @@ class DynamicPluginsTest {
|
||||
checked.incrementAndGet()
|
||||
}, listenerDisposable)
|
||||
|
||||
val disposable = loadExtensionWithText("<projectConfigurable instance=\"${MyConfigurable::class.java.name}\" displayName=\"foo\"/>",
|
||||
DynamicPlugins::class.java.classLoader)
|
||||
val disposable = loadExtensionWithText("<projectConfigurable instance=\"${MyConfigurable::class.java.name}\" displayName=\"foo\"/>")
|
||||
try {
|
||||
assertThat(checked.get()).isEqualTo(1)
|
||||
assertThat(
|
||||
@@ -544,11 +500,11 @@ class DynamicPluginsTest {
|
||||
fun loadExistingFileTypeModification() {
|
||||
@Suppress("SpellCheckingInspection")
|
||||
val textToLoad = "<fileType name=\"PLAIN_TEXT\" language=\"PLAIN_TEXT\" fileNames=\".texttest\"/>"
|
||||
var disposable = loadExtensionWithText(textToLoad, DynamicPlugins::class.java.classLoader)
|
||||
var disposable = loadExtensionWithText(textToLoad)
|
||||
Disposer.dispose(disposable)
|
||||
|
||||
UIUtil.dispatchAllInvocationEvents()
|
||||
disposable = loadExtensionWithText(textToLoad, DynamicPlugins::class.java.classLoader)
|
||||
disposable = loadExtensionWithText(textToLoad)
|
||||
Disposer.dispose(disposable)
|
||||
}
|
||||
|
||||
@@ -595,10 +551,8 @@ class DynamicPluginsTest {
|
||||
val barDependencyDescriptor = PluginBuilder().depends(quuxBuilder.id, quuxDependencyDescriptor)
|
||||
val mainDescriptor = PluginBuilder().randomId("main").depends(barBuilder.id, barDependencyDescriptor)
|
||||
|
||||
val barDisposable = loadPluginWithText(barBuilder)
|
||||
try {
|
||||
val quuxDisposable = loadPluginWithText(quuxBuilder)
|
||||
try {
|
||||
loadPluginWithText(barBuilder).use {
|
||||
loadPluginWithText(quuxBuilder).use {
|
||||
val descriptor = loadDescriptorInTest(
|
||||
mainDescriptor,
|
||||
Files.createTempDirectory(inMemoryFs.fs.getPath("/"), null),
|
||||
@@ -607,12 +561,6 @@ class DynamicPluginsTest {
|
||||
assertThat(DynamicPlugins.checkCanUnloadWithoutRestart(descriptor)).isEqualTo(
|
||||
"Plugin ${mainDescriptor.id} is not unload-safe because of extension to non-dynamic EP foo.barExtension in optional dependency on ${quuxBuilder.id} in optional dependency on ${barBuilder.id}")
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(quuxDisposable)
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(barDisposable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,6 +640,7 @@ private class MyAction2 : AnAction() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DialogTitleCapitalization")
|
||||
private class MyIntentionAction : IntentionAction {
|
||||
override fun startInWriteAction() = false
|
||||
override fun getFamilyName() = "foo"
|
||||
@@ -709,3 +658,9 @@ private class MyRunnable : Runnable {
|
||||
private class MyModuleConfigurationEditorProvider : ModuleConfigurationEditorProvider {
|
||||
override fun createEditors(state: ModuleConfigurationState?): Array<ModuleConfigurationEditor> = arrayOf()
|
||||
}
|
||||
|
||||
private inline fun runAndCheckThatNoNewPlugins(block: () -> Unit) {
|
||||
val beforeList = PluginManagerCore.getLoadedPlugins()
|
||||
block()
|
||||
assertThat(PluginManagerCore.getLoadedPlugins()).isEqualTo(beforeList)
|
||||
}
|
||||
@@ -39,19 +39,12 @@ internal fun loadDescriptorInTest(dir: Path, disabledPlugins: Set<PluginId> = em
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun loadExtensionWithText(
|
||||
extensionTag: String,
|
||||
loader: ClassLoader = DynamicPlugins::class.java.classLoader,
|
||||
ns: String = "com.intellij"
|
||||
): Disposable {
|
||||
fun loadExtensionWithText(extensionTag: String, ns: String = "com.intellij"): Disposable {
|
||||
val builder = PluginBuilder().extensions(extensionTag, ns)
|
||||
return loadPluginWithText(builder, FileSystems.getDefault())
|
||||
}
|
||||
|
||||
internal fun loadPluginWithText(
|
||||
pluginBuilder: PluginBuilder,
|
||||
fs: FileSystem,
|
||||
): Disposable {
|
||||
internal fun loadPluginWithText(pluginBuilder: PluginBuilder, fs: FileSystem): Disposable {
|
||||
val directory = if (fs == FileSystems.getDefault()) {
|
||||
FileUtil.createTempDirectory("test", "test", true).toPath()
|
||||
}
|
||||
@@ -68,13 +61,13 @@ internal fun loadPluginWithText(
|
||||
DynamicPlugins.loadPlugin(pluginDescriptor = descriptor)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
DynamicPlugins.unloadPlugin(descriptor)
|
||||
DynamicPlugins.unloadAndUninstallPlugin(descriptor)
|
||||
throw e
|
||||
}
|
||||
|
||||
return Disposable {
|
||||
val reason = DynamicPlugins.checkCanUnloadWithoutRestart(descriptor)
|
||||
DynamicPlugins.unloadPlugin(descriptor)
|
||||
DynamicPlugins.unloadAndUninstallPlugin(descriptor)
|
||||
assertThat(reason).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ide.plugins
|
||||
|
||||
import com.intellij.openapi.extensions.PluginId
|
||||
import com.intellij.util.io.Compressor
|
||||
import com.intellij.util.io.write
|
||||
import org.intellij.lang.annotations.Language
|
||||
@@ -32,19 +33,6 @@ fun module(outDir: Path, ownerId: String, moduleId: String, @Language("XML") des
|
||||
outDir.resolve("$ownerId/$moduleId.xml").write(descriptor.trimIndent())
|
||||
}
|
||||
|
||||
//class PluginV2Builder(val id: String, private var b: PluginBuilder) {
|
||||
// var packagePrefix: String
|
||||
// get() = throw IllegalStateException("set only")
|
||||
// set(value) {
|
||||
// b.packagePrefix(value)
|
||||
// }
|
||||
//
|
||||
// @Language("XML")
|
||||
// val extensionPoints = mutableListOf<@Language("XML") String>()
|
||||
//
|
||||
// val pluginDependencies
|
||||
//}
|
||||
|
||||
class PluginBuilder {
|
||||
private data class ExtensionBlock(val ns: String, val text: String)
|
||||
private data class DependsTag(val pluginId: String, val configFile: String?)
|
||||
@@ -67,6 +55,7 @@ class PluginBuilder {
|
||||
|
||||
private val content = mutableListOf<PluginContentDescriptor.ModuleItem>()
|
||||
private val dependencies = mutableListOf<ModuleDependenciesDescriptor.ModuleReference>()
|
||||
private val pluginDependencies = mutableListOf<ModuleDependenciesDescriptor.PluginReference>()
|
||||
|
||||
private val subDescriptors = HashMap<String, PluginBuilder>()
|
||||
|
||||
@@ -126,6 +115,11 @@ class PluginBuilder {
|
||||
return this
|
||||
}
|
||||
|
||||
fun pluginDependency(pluginId: String): PluginBuilder {
|
||||
pluginDependencies.add(ModuleDependenciesDescriptor.PluginReference(PluginId.getId(pluginId)))
|
||||
return this
|
||||
}
|
||||
|
||||
fun noDepends(): PluginBuilder {
|
||||
dependsTags.clear()
|
||||
return this
|
||||
@@ -206,9 +200,15 @@ class PluginBuilder {
|
||||
content.joinTo(this, separator = "\n ") { """<module name="${it.name}" />""" }
|
||||
append("\n</content>")
|
||||
}
|
||||
if (dependencies.isNotEmpty()) {
|
||||
|
||||
if (dependencies.isNotEmpty() || pluginDependencies.isNotEmpty()) {
|
||||
append("\n<dependencies>\n ")
|
||||
dependencies.joinTo(this, separator = "\n ") { """<module name="${it.name}" />""" }
|
||||
if (dependencies.isNotEmpty()) {
|
||||
dependencies.joinTo(this, separator = "\n ") { """<module name="${it.name}" />""" }
|
||||
}
|
||||
if (pluginDependencies.isNotEmpty()) {
|
||||
pluginDependencies.joinTo(this, separator = "\n ") { """<plugin id="${it.id}" />""" }
|
||||
}
|
||||
append("\n</dependencies>")
|
||||
}
|
||||
|
||||
|
||||
@@ -364,7 +364,7 @@ class PluginDescriptorTest {
|
||||
|
||||
assertThat(foo.pluginId.idString).isEqualTo("foo")
|
||||
val fooClassLoader = foo.pluginClassLoader as PluginClassLoader
|
||||
assertThat(fooClassLoader._getParents()!!).containsExactly(bar.pluginClassLoader)
|
||||
assertThat(fooClassLoader._getParents()).containsExactly(bar)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.vfs.newvfs.persistent;
|
||||
|
||||
import com.intellij.ide.plugins.DynamicPluginsTestUtil;
|
||||
@@ -751,7 +751,7 @@ public class PersistentFsTest extends BareTestFixtureTestCase {
|
||||
@Test
|
||||
public void testReadOnlyFsCachesLength() throws IOException {
|
||||
String text = "<virtualFileSystem implementationClass=\"" + TracingJarFileSystemTestWrapper.class.getName() + "\" key=\"jar-wrapper\" physical=\"true\"/>";
|
||||
Disposable disposable = runInEdtAndGet(() -> DynamicPluginsTestUtil.loadExtensionWithText(text, TracingJarFileSystemTestWrapper.class.getClassLoader()));
|
||||
Disposable disposable = runInEdtAndGet(() -> DynamicPluginsTestUtil.loadExtensionWithText(text, "com.intellij"));
|
||||
|
||||
try {
|
||||
File generationDir = tempDirectory.newDirectory("gen");
|
||||
@@ -796,7 +796,7 @@ public class PersistentFsTest extends BareTestFixtureTestCase {
|
||||
@Test
|
||||
public void testDoNotRecalculateLengthIfEndOfInputStreamIsNotReached() throws IOException {
|
||||
String text = "<virtualFileSystem implementationClass=\"" + TracingJarFileSystemTestWrapper.class.getName() + "\" key=\"jar-wrapper\" physical=\"true\"/>";
|
||||
Disposable disposable = runInEdtAndGet(() -> DynamicPluginsTestUtil.loadExtensionWithText(text, TracingJarFileSystemTestWrapper.class.getClassLoader()));
|
||||
Disposable disposable = runInEdtAndGet(() -> DynamicPluginsTestUtil.loadExtensionWithText(text, "com.intellij"));
|
||||
|
||||
try {
|
||||
File generationDir = tempDirectory.newDirectory("gen");
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.testGuiFramework.recorder.compile
|
||||
|
||||
import com.intellij.ide.plugins.IdeaPluginDescriptorImpl
|
||||
import com.intellij.ide.plugins.PluginManagerCore
|
||||
import com.intellij.ide.plugins.RawPluginDescriptor
|
||||
import com.intellij.ide.plugins.cl.PluginClassLoader
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.PathManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.extensions.DefaultPluginDescriptor
|
||||
import com.intellij.openapi.extensions.PluginId
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.testGuiFramework.recorder.GuiRecorderManager
|
||||
@@ -64,21 +67,32 @@ internal class LocalCompiler {
|
||||
|
||||
}
|
||||
|
||||
//alternative way to run compiled code with pluginClassloader built especially for this file
|
||||
// alternative way to run compiled code with pluginClassloader built especially for this file
|
||||
private fun run() {
|
||||
Notifier.updateStatus("${Notifier.LONG_OPERATION_PREFIX}Script running...")
|
||||
var classLoadersArray: Array<ClassLoader>
|
||||
try {
|
||||
//run testGuiTest gradle configuration
|
||||
// run testGuiTest gradle configuration
|
||||
ApplicationManager::class.java.classLoader.loadClass("com.intellij.testGuiFramework.impl.GuiTestCase")
|
||||
classLoadersArray = arrayOf(ApplicationManager::class.java.classLoader)
|
||||
}
|
||||
catch (cfe: ClassNotFoundException) {
|
||||
classLoadersArray = arrayOf(ApplicationManager::class.java.classLoader, this.javaClass.classLoader)
|
||||
}
|
||||
val coreLoader = PluginManagerCore::class.java.classLoader
|
||||
val pluginClassLoader = PluginClassLoader(UrlClassLoader.build().files(listOf(tempDir.toPath())).useCache(),
|
||||
classLoadersArray, DefaultPluginDescriptor("SubGuiScriptRecorder"), null as Path?,
|
||||
PluginManagerCore::class.java.classLoader)
|
||||
classLoadersArray
|
||||
.asSequence()
|
||||
.filter { it !== coreLoader }
|
||||
.map {
|
||||
val descriptor = IdeaPluginDescriptorImpl(raw = RawPluginDescriptor(),
|
||||
path = Path.of(""),
|
||||
isBundled = false,
|
||||
id = PluginId.getId(it.toString()))
|
||||
descriptor.classLoader = it
|
||||
descriptor
|
||||
}.toList().toTypedArray(), DefaultPluginDescriptor("SubGuiScriptRecorder"), null as Path?,
|
||||
coreLoader)
|
||||
val currentTest = pluginClassLoader.loadClass(TEST_CLASS_NAME)
|
||||
?: throw Exception("Unable to load by pluginClassLoader $TEST_CLASS_NAME.class file")
|
||||
val testCase = currentTest.getDeclaredConstructor().newInstance()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.lang;
|
||||
|
||||
import com.intellij.ReviseWhenPortedToJDK;
|
||||
@@ -19,18 +19,26 @@ public abstract class Java11Shim {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> Set<E> copyOf(Set<? extends E> collection) {
|
||||
public <E> @NotNull Set<E> copyOf(Set<? extends E> collection) {
|
||||
return Collections.unmodifiableSet(collection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> List<E> copyOf(List<? extends E> collection) {
|
||||
public <E> @NotNull List<E> copyOf(List<? extends E> collection) {
|
||||
return Collections.unmodifiableList(new ArrayList<>(collection));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull <E> List<E> listOf(E[] collection) {
|
||||
return Arrays.asList(collection);
|
||||
}
|
||||
};
|
||||
|
||||
public abstract <@NotNull K, @NotNull V> Map<K, V> copyOf(Map<? extends K, ? extends V> map);
|
||||
|
||||
public abstract <@NotNull E> Set<E> copyOf(Set<? extends E> collection);
|
||||
public abstract <@NotNull E> List<E> copyOf(List<? extends E> collection);
|
||||
public abstract <@NotNull E> @NotNull Set<E> copyOf(Set<? extends E> collection);
|
||||
|
||||
public abstract <@NotNull E> @NotNull List<E> copyOf(List<? extends E> collection);
|
||||
|
||||
public abstract <@NotNull E> @NotNull List<E> listOf(E[] collection);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.graph;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
@@ -35,13 +35,7 @@ public final class GraphGenerator<Node> implements Graph<Node> {
|
||||
// Duplicate edge
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Node> edgesFromInNode = myOuts.get(inNode);
|
||||
if (edgesFromInNode == null) {
|
||||
edgesFromInNode = new ArrayList<>();
|
||||
myOuts.put(inNode, edgesFromInNode);
|
||||
}
|
||||
edgesFromInNode.add(node);
|
||||
myOuts.computeIfAbsent(inNode, __ -> new ArrayList<>()).add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler
|
||||
|
||||
import com.intellij.application.options.CodeStyle
|
||||
@@ -100,10 +100,9 @@ class IdeaDecompiler : ClassFileDecompilers.Light() {
|
||||
PluginManagerCore.disablePlugin(id)
|
||||
|
||||
val plugin = PluginManagerCore.getPlugin(id)
|
||||
if (plugin is IdeaPluginDescriptorImpl) {
|
||||
if (DynamicPlugins.allowLoadUnloadWithoutRestart(plugin)) {
|
||||
val task = DynamicPlugins.getPluginUnloadingTask(plugin, DynamicPlugins.UnloadPluginOptions(disable = true, save = false))
|
||||
ApplicationManager.getApplication().invokeLater(task)
|
||||
if (plugin is IdeaPluginDescriptorImpl && DynamicPlugins.allowLoadUnloadWithoutRestart(plugin)) {
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
DynamicPlugins.unloadPlugin(plugin, DynamicPlugins.UnloadPluginOptions(save = false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user