From 77b89c8bb01436b537036312dea7b90f42b396f0 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Tue, 20 Oct 2020 14:58:45 +0200 Subject: [PATCH] cleanup, reuse PluginStateChecker GitOrigin-RevId: 763952172c4edd93d9779af7ca5328093b119b9a --- .../intellij/ide/plugins/PluginManager.java | 37 ++++++++++++-- .../ide/plugins/PluginManagerCore.java | 37 ++++---------- .../util/graph/impl/GraphAlgorithmsImpl.java | 4 +- .../intellij/ide/plugins/DynamicPlugins.kt | 50 ++++++++----------- .../intellij/plugin/PluginEnvironment.kt | 16 +++--- 5 files changed, 77 insertions(+), 67 deletions(-) diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginManager.java b/platform/core-impl/src/com/intellij/ide/plugins/PluginManager.java index a73d75ffe9a0..998e8f01b33a 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginManager.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/PluginManager.java @@ -16,6 +16,9 @@ import com.intellij.openapi.util.SafeJdomFactory; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.graph.Graph; +import com.intellij.util.graph.GraphAlgorithms; +import com.intellij.util.graph.GraphGenerator; import org.jdom.JDOMException; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -23,12 +26,12 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.lang.reflect.Method; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collection; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Function; @Service public final class PluginManager { @@ -228,6 +231,34 @@ public final class PluginManager { PluginManagerCore.doSetPlugins(list); } + public boolean processAllBackwardDependencies(@NotNull IdeaPluginDescriptorImpl rootDescriptor, + boolean withOptionalDeps, + @NotNull Function consumer) { + Map idMap = new HashMap<>(); + IdeaPluginDescriptorImpl[] allPlugins = (IdeaPluginDescriptorImpl[])PluginManagerCore.getPlugins(); + for (IdeaPluginDescriptorImpl plugin : allPlugins) { + idMap.put(plugin.getPluginId(), plugin); + } + + CachingSemiGraph semiGraph = PluginManagerCore + .createPluginIdGraph(Arrays.asList(allPlugins), + idMap::get, + withOptionalDeps, + PluginManagerCore.findPluginByModuleDependency(PluginManagerCore.ALL_MODULES_MARKER) != null); + Graph graph = GraphGenerator.generate(semiGraph); + Set 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)) { diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java index 8cf7523c1fbb..23c72ab7cfa9 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java @@ -29,7 +29,9 @@ import com.intellij.util.*; import com.intellij.util.concurrency.AppExecutorUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.execution.ParametersListUtil; -import com.intellij.util.graph.*; +import com.intellij.util.graph.DFSTBuilder; +import com.intellij.util.graph.GraphGenerator; +import com.intellij.util.graph.InboundSemiGraph; import com.intellij.util.lang.UrlClassLoader; import org.jetbrains.annotations.*; @@ -577,10 +579,10 @@ public final class PluginManagerCore { return null; } - private static @NotNull CachingSemiGraph createPluginIdGraph(@NotNull List descriptors, - @NotNull Function idToDescriptorMap, - boolean withOptional, - boolean hasAllModules) { + static @NotNull CachingSemiGraph createPluginIdGraph(@NotNull List descriptors, + @NotNull Function idToDescriptorMap, + boolean withOptional, + boolean hasAllModules) { Supplier javaDep = () -> idToDescriptorMap.apply(JAVA_MODULE_ID); Set uniqueCheck = new HashSet<>(); Map> in = new HashMap<>(descriptors.size()); @@ -1393,11 +1395,9 @@ public final class PluginManagerCore { } public static @Nullable IdeaPluginDescriptor findPluginByModuleDependency(@NotNull PluginId id) { - for (IdeaPluginDescriptor descriptor : getPlugins()) { - if (descriptor instanceof IdeaPluginDescriptorImpl) { - if (((IdeaPluginDescriptorImpl)descriptor).getModules().contains(id)) { - return descriptor; - } + for (IdeaPluginDescriptorImpl descriptor : ourPlugins) { + if (descriptor.getModules().contains(id)) { + return descriptor; } } return null; @@ -1482,23 +1482,6 @@ public final class PluginManagerCore { return true; } - public static boolean processAllBackwardDependencies(@NotNull IdeaPluginDescriptorImpl rootDescriptor, - boolean withOptionalDeps, - @NotNull Function consumer) { - CachingSemiGraph semiGraph = createPluginIdGraph(Arrays.asList(ourPlugins), - (id) -> (IdeaPluginDescriptorImpl)getPlugin(id), - withOptionalDeps, - findPluginByModuleDependency(ALL_MODULES_MARKER) != null); - Graph graph = GraphGenerator.generate(semiGraph); - Set 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; - } - private static @NotNull List getOnlyEnabledPlugins(@NotNull IdeaPluginDescriptorImpl @NotNull[] sortedAll) { List enabledPlugins = new ArrayList<>(sortedAll.length); for (IdeaPluginDescriptorImpl descriptor : sortedAll) { diff --git a/platform/core-impl/src/com/intellij/util/graph/impl/GraphAlgorithmsImpl.java b/platform/core-impl/src/com/intellij/util/graph/impl/GraphAlgorithmsImpl.java index c965f4dc9032..becb79755686 100644 --- a/platform/core-impl/src/com/intellij/util/graph/impl/GraphAlgorithmsImpl.java +++ b/platform/core-impl/src/com/intellij/util/graph/impl/GraphAlgorithmsImpl.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 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-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. package com.intellij.util.graph.impl; import com.intellij.openapi.progress.ProgressIndicator; @@ -10,7 +10,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; -public class GraphAlgorithmsImpl extends GraphAlgorithms { +public final class GraphAlgorithmsImpl extends GraphAlgorithms { @Override public List findShortestPath(@NotNull Graph graph, @NotNull Node start, @NotNull Node finish) { return findShortestPath((InboundSemiGraph)graph, start, finish); diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/DynamicPlugins.kt b/platform/platform-impl/src/com/intellij/ide/plugins/DynamicPlugins.kt index 44a4d045e48f..b2e18b49ac5a 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/DynamicPlugins.kt +++ b/platform/platform-impl/src/com/intellij/ide/plugins/DynamicPlugins.kt @@ -183,8 +183,9 @@ object DynamicPlugins { return e.cause?.localizedMessage ?: "checkUnloadPlugin listener blocked plugin unload" } + val pluginStateChecker = PluginStateChecker() if (!Registry.`is`("ide.plugins.allow.unload.from.sources")) { - if (loadedPluginDescriptor != null && isPluginOrModuleLoaded(loadedPluginDescriptor.pluginId) && !descriptor.isUseIdeaClassLoader) { + if (loadedPluginDescriptor != null && pluginStateChecker.isPluginOrModuleLoaded(loadedPluginDescriptor.pluginId) && !descriptor.isUseIdeaClassLoader) { val pluginClassLoader = loadedPluginDescriptor.pluginClassLoader if (pluginClassLoader !is PluginClassLoader && !app.isUnitTestMode) { val loader = baseDescriptor ?: descriptor @@ -203,7 +204,7 @@ object DynamicPlugins { ActionManagerImpl.checkUnloadActions(pluginId, descriptor)?.let { return it } descriptor.pluginDependencies?.forEach { dependency -> - if (isPluginOrModuleLoaded(dependency.id)) { + if (pluginStateChecker.isPluginOrModuleLoaded(dependency.id)) { val message = checkCanUnloadWithoutRestart(dependency.subDescriptor ?: return@forEach, baseDescriptor ?: descriptor, null, context) if (message != null) { return "$message in optional dependency on ${dependency.id}" @@ -289,7 +290,7 @@ object DynamicPlugins { val newPluginDescriptor = dependency.configFile?.let { loader.value.load(contextDescriptor, it) } ?: return true // todo classloader per partial descriptor - newPluginDescriptor.setClassLoader(contextDescriptor.pluginClassLoader) + newPluginDescriptor.classLoader = contextDescriptor.pluginClassLoader if (dependency.id == dependencyPluginId) { dependency.subDescriptor = newPluginDescriptor @@ -309,7 +310,7 @@ object DynamicPlugins { private fun processImplementationDetailDependenciesOnPlugin(pluginDescriptor: IdeaPluginDescriptorImpl, processor: (loadedDescriptor: IdeaPluginDescriptorImpl, fullDescriptor: IdeaPluginDescriptorImpl) -> Boolean) { - PluginManagerCore.processAllBackwardDependencies(pluginDescriptor, false) { loadedDescriptor -> + PluginManager.getInstance().processAllBackwardDependencies(pluginDescriptor, false) { loadedDescriptor -> if (loadedDescriptor.isImplementationDetail) { val fullDescriptor = PluginDescriptorLoader.loadFullDescriptor(loadedDescriptor as IdeaPluginDescriptorImpl) if (processor(loadedDescriptor, fullDescriptor)) FileVisitResult.CONTINUE else FileVisitResult.TERMINATE @@ -485,7 +486,7 @@ object DynamicPlugins { Language.unregisterLanguages(loadedPluginDescriptor.pluginClassLoader) } - unloadDependencyDescriptors(pluginDescriptor) + unloadDependencyDescriptors(pluginDescriptor, PluginStateChecker()) unloadPluginDescriptorNotRecursively(pluginDescriptor) for (project in ProjectUtil.getOpenProjects()) { @@ -566,17 +567,18 @@ object DynamicPlugins { val eventId = if (classLoaderUnloaded) "unload.success" else "unload.fail" val fuData = FeatureUsageData().addPluginInfo(getPluginInfoByDescriptor(loadedPluginDescriptor)) + @Suppress("DEPRECATION") FUCounterUsageLogger.getInstance().logEvent("plugins.dynamic", eventId, fuData) } return classLoaderUnloaded } - private fun unloadDependencyDescriptors(pluginDescriptor: IdeaPluginDescriptorImpl) { + private fun unloadDependencyDescriptors(pluginDescriptor: IdeaPluginDescriptorImpl, pluginStateChecker: PluginStateChecker) { for (dependency in (pluginDescriptor.pluginDependencies ?: return)) { - if (isPluginOrModuleLoaded(dependency.id)) { + if (pluginStateChecker.isPluginOrModuleLoaded(dependency.id)) { val subDescriptor = dependency.subDescriptor ?: continue unloadPluginDescriptorNotRecursively(subDescriptor) - unloadDependencyDescriptors(subDescriptor) + unloadDependencyDescriptors(subDescriptor, pluginStateChecker) } } } @@ -750,6 +752,7 @@ object DynamicPlugins { } val fuData = FeatureUsageData().addPluginInfo(getPluginInfoByDescriptor(pluginDescriptor)) + @Suppress("DEPRECATION") FUCounterUsageLogger.getInstance().logEvent("plugins.dynamic", "load", fuData) LOG.info("Plugin ${pluginDescriptor.pluginId} loaded without restart in ${System.currentTimeMillis() - loadStartTime} ms") } @@ -827,22 +830,14 @@ object DynamicPlugins { } } - private fun isPluginOrModuleLoaded(pluginId: PluginId?): Boolean { - if (pluginId != null && PluginManagerCore.isModuleDependency(pluginId)) { - return PluginManagerCore.findPluginByModuleDependency(pluginId) != null - } - return PluginManagerCore.getLoadedPlugins(null).any { it.pluginId == pluginId } - } - @JvmStatic fun onPluginUnload(parentDisposable: Disposable, callback: Runnable) { - ApplicationManager.getApplication().messageBus.connect(parentDisposable).subscribe(DynamicPluginListener.TOPIC, - object : DynamicPluginListener { - override fun beforePluginUnload(pluginDescriptor: IdeaPluginDescriptor, - isUpdate: Boolean) { - callback.run() - } - }) + ApplicationManager.getApplication().messageBus.connect(parentDisposable) + .subscribe(DynamicPluginListener.TOPIC, object : DynamicPluginListener { + override fun beforePluginUnload(pluginDescriptor: IdeaPluginDescriptor, isUpdate: Boolean) { + callback.run() + } + }) } private fun clearTemporaryLostComponent() { @@ -892,7 +887,8 @@ object DynamicPlugins { if (Registry.`is`("ide.plugins.analyze.snapshot")) { val analysisResult = analyzeSnapshot(snapshotPath, pluginId) - if (analysisResult.isEmpty()) { + @Suppress("ReplaceSizeZeroCheckWithIsEmpty") + if (analysisResult.length == 0) { LOG.info("Successfully unloaded plugin $pluginId (no strong references to classloader in .hprof file)") classloadersFromUnloadedPlugins.remove(pluginId) return true @@ -939,11 +935,9 @@ private class PluginStateChecker { private fun analyzeSnapshot(hprofPath: String, pluginId: PluginId): String { FileChannel.open(Paths.get(hprofPath), StandardOpenOption.READ).use { channel -> - val analysis = HProfAnalysis( - channel, - SystemTempFilenameSupplier() - ) { analysisContext, progressIndicator -> AnalyzeClassloaderReferencesGraph(analysisContext, pluginId.idString).analyze( - progressIndicator) } + val analysis = HProfAnalysis(channel, SystemTempFilenameSupplier()) { analysisContext, progressIndicator -> + AnalyzeClassloaderReferencesGraph(analysisContext, pluginId.idString).analyze(progressIndicator) + } analysis.onlyStrongReferences = true analysis.includeClassesAsRoots = false analysis.setIncludeMetaInfo(false) diff --git a/plugins/package-search/src/com/jetbrains/packagesearch/intellij/plugin/PluginEnvironment.kt b/plugins/package-search/src/com/jetbrains/packagesearch/intellij/plugin/PluginEnvironment.kt index b9144cb04220..ba49e9d170d8 100644 --- a/plugins/package-search/src/com/jetbrains/packagesearch/intellij/plugin/PluginEnvironment.kt +++ b/plugins/package-search/src/com/jetbrains/packagesearch/intellij/plugin/PluginEnvironment.kt @@ -1,17 +1,19 @@ package com.jetbrains.packagesearch.intellij.plugin -import com.intellij.ide.plugins.PluginManager +import com.intellij.ide.plugins.PluginManagerCore import com.intellij.openapi.application.ApplicationInfo import com.intellij.openapi.extensions.PluginId +import com.intellij.openapi.util.NlsSafe const val PACKAGE_SEARCH_NOTIFICATION_GROUP_ID = "PACKAGESEARCH.NOTIFICATION" class PluginEnvironment { + val pluginVersion: @NlsSafe String + get() { + return PluginManagerCore.getPlugin(PluginId.getId("com.jetbrains.packagesearch.intellij-plugin"))?.version + ?: PackageSearchBundle.message("packagesearch.version.undefined") + } - val pluginVersion - get() = PluginManager.getPlugin(PluginId.getId("com.jetbrains.packagesearch.intellij-plugin"))?.version - ?: PackageSearchBundle.message("packagesearch.version.undefined") - - val ideVersion - get() = ApplicationInfo.getInstance().strictVersion!! + val ideVersion + get() = ApplicationInfo.getInstance().strictVersion }