mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
cleanup, reuse PluginStateChecker
GitOrigin-RevId: 763952172c4edd93d9779af7ca5328093b119b9a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f0291f4254
commit
77b89c8bb0
@@ -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<? super IdeaPluginDescriptor, FileVisitResult> consumer) {
|
||||
Map<PluginId, IdeaPluginDescriptorImpl> idMap = new HashMap<>();
|
||||
IdeaPluginDescriptorImpl[] allPlugins = (IdeaPluginDescriptorImpl[])PluginManagerCore.getPlugins();
|
||||
for (IdeaPluginDescriptorImpl plugin : allPlugins) {
|
||||
idMap.put(plugin.getPluginId(), plugin);
|
||||
}
|
||||
|
||||
CachingSemiGraph<IdeaPluginDescriptorImpl> semiGraph = PluginManagerCore
|
||||
.createPluginIdGraph(Arrays.asList(allPlugins),
|
||||
idMap::get,
|
||||
withOptionalDeps,
|
||||
PluginManagerCore.findPluginByModuleDependency(PluginManagerCore.ALL_MODULES_MARKER) != null);
|
||||
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)) {
|
||||
|
||||
@@ -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<IdeaPluginDescriptorImpl> createPluginIdGraph(@NotNull List<IdeaPluginDescriptorImpl> descriptors,
|
||||
@NotNull Function<? super PluginId, IdeaPluginDescriptorImpl> idToDescriptorMap,
|
||||
boolean withOptional,
|
||||
boolean hasAllModules) {
|
||||
static @NotNull CachingSemiGraph<IdeaPluginDescriptorImpl> createPluginIdGraph(@NotNull List<IdeaPluginDescriptorImpl> descriptors,
|
||||
@NotNull Function<? super PluginId, IdeaPluginDescriptorImpl> idToDescriptorMap,
|
||||
boolean withOptional,
|
||||
boolean hasAllModules) {
|
||||
Supplier<IdeaPluginDescriptorImpl> javaDep = () -> idToDescriptorMap.apply(JAVA_MODULE_ID);
|
||||
Set<IdeaPluginDescriptorImpl> uniqueCheck = new HashSet<>();
|
||||
Map<IdeaPluginDescriptorImpl, List<IdeaPluginDescriptorImpl>> 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<? super IdeaPluginDescriptor, FileVisitResult> consumer) {
|
||||
CachingSemiGraph<IdeaPluginDescriptorImpl> semiGraph = createPluginIdGraph(Arrays.asList(ourPlugins),
|
||||
(id) -> (IdeaPluginDescriptorImpl)getPlugin(id),
|
||||
withOptionalDeps,
|
||||
findPluginByModuleDependency(ALL_MODULES_MARKER) != null);
|
||||
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;
|
||||
}
|
||||
|
||||
private static @NotNull List<IdeaPluginDescriptorImpl> getOnlyEnabledPlugins(@NotNull IdeaPluginDescriptorImpl @NotNull[] sortedAll) {
|
||||
List<IdeaPluginDescriptorImpl> enabledPlugins = new ArrayList<>(sortedAll.length);
|
||||
for (IdeaPluginDescriptorImpl descriptor : sortedAll) {
|
||||
|
||||
@@ -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 <Node> List<Node> findShortestPath(@NotNull Graph<Node> graph, @NotNull Node start, @NotNull Node finish) {
|
||||
return findShortestPath((InboundSemiGraph<Node>)graph, start, finish);
|
||||
|
||||
@@ -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)
|
||||
|
||||
+9
-7
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user