feat(api): add 'unload' method to JadxPlugin (#2463)
This commit is contained in:
@@ -244,13 +244,17 @@ public class JCommanderWrapper {
|
||||
JadxPluginManager pluginManager = decompiler.getPluginManager();
|
||||
pluginManager.load(new JadxExternalPluginsLoader());
|
||||
pluginManager.initAll();
|
||||
for (PluginContext context : pluginManager.getAllPluginContexts()) {
|
||||
JadxPluginOptions options = context.getOptions();
|
||||
if (options != null) {
|
||||
if (appendPlugin(context.getPluginInfo(), context.getOptions(), sb, maxNamesLen)) {
|
||||
k++;
|
||||
try {
|
||||
for (PluginContext context : pluginManager.getAllPluginContexts()) {
|
||||
JadxPluginOptions options = context.getOptions();
|
||||
if (options != null) {
|
||||
if (appendPlugin(context.getPluginInfo(), context.getOptions(), sb, maxNamesLen)) {
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
pluginManager.unloadAll();
|
||||
}
|
||||
}
|
||||
if (sb.length() == 0) {
|
||||
|
||||
@@ -170,6 +170,7 @@ public final class JadxDecompiler implements Closeable {
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
unloadPlugins();
|
||||
root = null;
|
||||
classes = null;
|
||||
resources = null;
|
||||
@@ -215,6 +216,10 @@ public final class JadxDecompiler implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
private void unloadPlugins() {
|
||||
pluginManager.unloadResolved();
|
||||
}
|
||||
|
||||
private void loadFinished() {
|
||||
LOG.debug("Load finished");
|
||||
List<JadxPass> list = customPasses.get(JadxAfterLoadPass.TYPE);
|
||||
|
||||
@@ -23,4 +23,12 @@ public interface JadxPlugin {
|
||||
* For long operation, prefer {@link JadxPreparePass} or {@link JadxAfterLoadPass} instead.
|
||||
*/
|
||||
void init(JadxPluginContext context);
|
||||
|
||||
/**
|
||||
* Plugin unload handler.
|
||||
* Can be used to clean up resources on plugin unloading.
|
||||
*/
|
||||
default void unload() {
|
||||
// optional method
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,4 +26,13 @@ public interface ISettingsGroup {
|
||||
default List<ISettingsGroup> getSubGroups() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings close handler.
|
||||
* Apply settings if 'save' param set to true.
|
||||
* It can be used to clean up resources.
|
||||
*/
|
||||
default void close(boolean save) {
|
||||
// optional method
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import jadx.api.plugins.loader.JadxPluginLoader;
|
||||
import jadx.api.plugins.options.JadxPluginOptions;
|
||||
import jadx.api.plugins.options.OptionDescription;
|
||||
import jadx.core.plugins.versions.VerifyRequiredVersion;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class JadxPluginManager {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxPluginManager.class);
|
||||
@@ -149,7 +148,7 @@ public class JadxPluginManager {
|
||||
}
|
||||
context.init();
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Failed to init plugin: " + context.getPluginId(), e);
|
||||
LOG.warn("Failed to init plugin: {}", context.getPluginId(), e);
|
||||
}
|
||||
}
|
||||
for (PluginContext context : pluginContexts) {
|
||||
@@ -160,6 +159,24 @@ public class JadxPluginManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void unloadAll() {
|
||||
unload(allPlugins);
|
||||
}
|
||||
|
||||
public void unloadResolved() {
|
||||
unload(resolvedPlugins);
|
||||
}
|
||||
|
||||
public void unload(SortedSet<PluginContext> pluginContexts) {
|
||||
for (PluginContext context : pluginContexts) {
|
||||
try {
|
||||
context.unload();
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Failed to unload plugin: {}", context.getPluginId(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AppContext buildDefaultAppContext() {
|
||||
AppContext appContext = new AppContext();
|
||||
appContext.setGuiContext(null);
|
||||
|
||||
@@ -55,11 +55,17 @@ public class PluginContext implements JadxPluginContext, JadxPluginRuntimeData,
|
||||
this.pluginInfo = plugin.getPluginInfo();
|
||||
}
|
||||
|
||||
void init() {
|
||||
public void init() {
|
||||
plugin.init(this);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
if (initialized) {
|
||||
plugin.unload();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
|
||||
@@ -91,6 +91,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
|
||||
private transient boolean needReload = false;
|
||||
private transient SettingsTree tree;
|
||||
private List<ISettingsGroup> groups;
|
||||
|
||||
public JadxSettingsWindow(MainWindow mainWindow, JadxSettings settings) {
|
||||
this.mainWindow = mainWindow;
|
||||
@@ -116,6 +117,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
|
||||
private void reloadUI() {
|
||||
int[] selection = tree.getSelectionRows();
|
||||
closeGroups(false);
|
||||
getContentPane().removeAll();
|
||||
initUI();
|
||||
// wait for other events to process
|
||||
@@ -128,7 +130,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
private void initUI() {
|
||||
JPanel wrapGroupPanel = new JPanel(new BorderLayout(10, 10));
|
||||
|
||||
List<ISettingsGroup> groups = new ArrayList<>();
|
||||
groups = new ArrayList<>();
|
||||
groups.add(makeDecompilationGroup());
|
||||
groups.add(makeDeobfuscationGroup());
|
||||
groups.add(makeRenameGroup());
|
||||
@@ -690,7 +692,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
sizeLimit.addChangeListener(ev -> settings.setSrhResourceSkipSize((Integer) sizeLimit.getValue()));
|
||||
|
||||
JTextField fileExtField = new JTextField();
|
||||
fileExtField.getDocument().addDocumentListener(new DocumentUpdateListener((ev) -> {
|
||||
fileExtField.getDocument().addDocumentListener(new DocumentUpdateListener(ev -> {
|
||||
String ext = fileExtField.getText();
|
||||
settings.setSrhResourceFileExt(ext);
|
||||
}));
|
||||
@@ -703,7 +705,14 @@ public class JadxSettingsWindow extends JDialog {
|
||||
return searchGroup;
|
||||
}
|
||||
|
||||
private void closeGroups(boolean save) {
|
||||
for (ISettingsGroup group : groups) {
|
||||
group.close(save);
|
||||
}
|
||||
}
|
||||
|
||||
private void save() {
|
||||
closeGroups(true);
|
||||
settings.sync();
|
||||
enableComponents(this, false);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
@@ -723,6 +732,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
}
|
||||
|
||||
private void cancel() {
|
||||
closeGroups(false);
|
||||
JadxSettingsAdapter.fill(settings, startSettings);
|
||||
mainWindow.loadSettings();
|
||||
dispose();
|
||||
|
||||
@@ -35,7 +35,9 @@ import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.settings.ui.SettingsGroup;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.plugins.CloseablePlugins;
|
||||
import jadx.gui.utils.plugins.CollectPlugins;
|
||||
import jadx.gui.utils.plugins.SettingsGroupPluginWrap;
|
||||
import jadx.gui.utils.ui.DocumentUpdateListener;
|
||||
import jadx.plugins.tools.JadxPluginsTools;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
@@ -53,12 +55,12 @@ public class PluginSettings {
|
||||
}
|
||||
|
||||
public ISettingsGroup build() {
|
||||
List<PluginContext> collectedPlugins = new CollectPlugins(mainWindow).build();
|
||||
CloseablePlugins collectedPlugins = new CollectPlugins(mainWindow).build();
|
||||
ISettingsGroup pluginsGroup = new PluginSettingsGroup(this, mainWindow, collectedPlugins);
|
||||
for (PluginContext context : collectedPlugins) {
|
||||
for (PluginContext context : collectedPlugins.getList()) {
|
||||
ISettingsGroup pluginGroup = addPluginGroup(context);
|
||||
if (pluginGroup != null) {
|
||||
pluginsGroup.getSubGroups().add(pluginGroup);
|
||||
pluginsGroup.getSubGroups().add(new SettingsGroupPluginWrap(context.getPluginId(), pluginGroup));
|
||||
}
|
||||
}
|
||||
return pluginsGroup;
|
||||
|
||||
@@ -40,6 +40,7 @@ import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.utils.Link;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
import jadx.gui.utils.plugins.CloseablePlugins;
|
||||
import jadx.plugins.tools.JadxPluginsList;
|
||||
import jadx.plugins.tools.JadxPluginsTools;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
@@ -51,11 +52,11 @@ class PluginSettingsGroup implements ISettingsGroup {
|
||||
private final MainWindow mainWindow;
|
||||
private final String title;
|
||||
private final List<ISettingsGroup> subGroups = new ArrayList<>();
|
||||
private final List<PluginContext> collectedPlugins;
|
||||
private final CloseablePlugins collectedPlugins;
|
||||
|
||||
private JPanel detailsPanel;
|
||||
|
||||
public PluginSettingsGroup(PluginSettings pluginSettings, MainWindow mainWindow, List<PluginContext> collectedPlugins) {
|
||||
public PluginSettingsGroup(PluginSettings pluginSettings, MainWindow mainWindow, CloseablePlugins collectedPlugins) {
|
||||
this.pluginsSettings = pluginSettings;
|
||||
this.mainWindow = mainWindow;
|
||||
this.title = NLS.str("preferences.plugins");
|
||||
@@ -78,6 +79,12 @@ class PluginSettingsGroup implements ISettingsGroup {
|
||||
return buildMainSettingsPage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(boolean save) {
|
||||
subGroups.forEach(subGroup -> subGroup.close(save));
|
||||
collectedPlugins.close();
|
||||
}
|
||||
|
||||
private JPanel buildMainSettingsPage() {
|
||||
JButton installPluginBtn = new JButton(NLS.str("preferences.plugins.install"));
|
||||
installPluginBtn.addActionListener(ev -> pluginsSettings.addPlugin());
|
||||
@@ -124,13 +131,13 @@ class PluginSettingsGroup implements ISettingsGroup {
|
||||
|
||||
private void applyData(DefaultListModel<BasePluginListNode> listModel) {
|
||||
List<JadxPluginMetadata> installed = JadxPluginsTools.getInstance().getInstalled();
|
||||
List<BasePluginListNode> nodes = new ArrayList<>(installed.size() + collectedPlugins.size());
|
||||
List<BasePluginListNode> nodes = new ArrayList<>(installed.size() + collectedPlugins.getList().size());
|
||||
Set<String> installedSet = new HashSet<>(installed.size());
|
||||
for (JadxPluginMetadata pluginMetadata : installed) {
|
||||
installedSet.add(pluginMetadata.getPluginId());
|
||||
nodes.add(new InstalledPluginNode(pluginMetadata));
|
||||
}
|
||||
for (PluginContext plugin : collectedPlugins) {
|
||||
for (PluginContext plugin : collectedPlugins.getList()) {
|
||||
if (!installedSet.contains(plugin.getPluginId())) {
|
||||
nodes.add(new LoadedPluginNode(plugin));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package jadx.gui.utils.plugins;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.plugins.PluginContext;
|
||||
|
||||
public class CloseablePlugins {
|
||||
private final List<PluginContext> list;
|
||||
private final @Nullable Runnable closeable;
|
||||
|
||||
public CloseablePlugins(List<PluginContext> list, @Nullable Runnable closeable) {
|
||||
this.list = list;
|
||||
this.closeable = closeable;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (closeable != null) {
|
||||
closeable.run();
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable Runnable getCloseable() {
|
||||
return closeable;
|
||||
}
|
||||
|
||||
public List<PluginContext> getList() {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package jadx.gui.utils.plugins;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@@ -27,12 +26,13 @@ public class CollectPlugins {
|
||||
this.mainWindow = mainWindow;
|
||||
}
|
||||
|
||||
public List<PluginContext> build() {
|
||||
public CloseablePlugins build() {
|
||||
SortedSet<PluginContext> allPlugins = new TreeSet<>();
|
||||
mainWindow.getWrapper().getCurrentDecompiler()
|
||||
.ifPresent(decompiler -> allPlugins.addAll(decompiler.getPluginManager().getResolvedPluginContexts()));
|
||||
|
||||
// collect and init not loaded plugins in new temp context
|
||||
Runnable closeable = null;
|
||||
JadxArgs jadxArgs = mainWindow.getSettings().toJadxArgs();
|
||||
try (JadxDecompiler decompiler = new JadxDecompiler(jadxArgs)) {
|
||||
JadxPluginManager pluginManager = decompiler.getPluginManager();
|
||||
@@ -52,8 +52,9 @@ public class CollectPlugins {
|
||||
if (!missingPlugins.isEmpty()) {
|
||||
pluginManager.init(missingPlugins);
|
||||
allPlugins.addAll(missingPlugins);
|
||||
closeable = () -> pluginManager.unload(missingPlugins);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(allPlugins);
|
||||
return new CloseablePlugins(new ArrayList<>(allPlugins), closeable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package jadx.gui.utils.plugins;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.plugins.gui.ISettingsGroup;
|
||||
|
||||
public class SettingsGroupPluginWrap implements ISettingsGroup {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SettingsGroupPluginWrap.class);
|
||||
|
||||
private final String pluginId;
|
||||
private final ISettingsGroup pluginSettingGroup;
|
||||
|
||||
public SettingsGroupPluginWrap(String pluginId, ISettingsGroup pluginSettingGroup) {
|
||||
this.pluginId = pluginId;
|
||||
this.pluginSettingGroup = pluginSettingGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
try {
|
||||
return pluginSettingGroup.getTitle();
|
||||
} catch (Throwable t) {
|
||||
LOG.warn("Failed to get settings group title for plugin: {}", pluginId, t);
|
||||
return "<error>";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent buildComponent() {
|
||||
try {
|
||||
return pluginSettingGroup.buildComponent();
|
||||
} catch (Throwable t) {
|
||||
LOG.warn("Failed to build settings group component for plugin: {}", pluginId, t);
|
||||
return new JLabel("<error>");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISettingsGroup> getSubGroups() {
|
||||
try {
|
||||
return pluginSettingGroup.getSubGroups();
|
||||
} catch (Throwable t) {
|
||||
LOG.warn("Failed to get settings group sub-groups for plugin: {}", pluginId, t);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(boolean save) {
|
||||
try {
|
||||
pluginSettingGroup.close(save);
|
||||
} catch (Throwable t) {
|
||||
LOG.warn("Failed to close settings group for plugin: {}", pluginId, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user