fix(gui): show options from all plugins

This commit is contained in:
Skylot
2023-03-30 17:12:27 +01:00
parent e8e7028792
commit ee3a653c1b
5 changed files with 143 additions and 43 deletions
@@ -119,11 +119,12 @@ public class JadxPluginManager {
init(context, resolvedPlugins);
}
private void init(PluginsContext context, List<JadxPlugin> plugins) {
public void init(PluginsContext context, List<JadxPlugin> plugins) {
for (JadxPlugin plugin : plugins) {
try {
context.setCurrentPlugin(plugin);
plugin.init(context);
context.setCurrentPlugin(null);
} catch (Exception e) {
String pluginId = plugin.getPluginInfo().getPluginId();
throw new JadxRuntimeException("Failed to init plugin: " + pluginId, e);
@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
@@ -19,7 +20,6 @@ import jadx.api.JavaNode;
import jadx.api.JavaPackage;
import jadx.api.ResourceFile;
import jadx.api.impl.InMemoryCodeCache;
import jadx.api.impl.plugins.PluginsContext;
import jadx.api.metadata.ICodeNodeRef;
import jadx.api.usage.impl.EmptyUsageInfoCache;
import jadx.api.usage.impl.InMemoryUsageInfoCache;
@@ -27,7 +27,6 @@ import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.ProcessState;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import jadx.gui.cache.code.CodeStringCache;
import jadx.gui.cache.code.disk.BufferCodeCache;
import jadx.gui.cache.code.disk.DiskCodeCache;
@@ -227,15 +226,9 @@ public class JadxWrapper {
getSettings().sync();
}
public PluginsContext getPluginsContext() {
if (decompiler != null) {
return decompiler.getPluginsContext();
}
try (JadxDecompiler tmpDecompiler = new JadxDecompiler()) {
// TODO: override input file checks
tmpDecompiler.getArgs().setInputFile(FileUtils.createTempFile("tmp.txt").toFile());
tmpDecompiler.load();
return tmpDecompiler.getPluginsContext();
public Optional<JadxDecompiler> getCurrentDecompiler() {
synchronized (DECOMPILER_UPDATE_SYNC) {
return Optional.ofNullable(decompiler);
}
}
@@ -21,7 +21,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -62,7 +62,6 @@ import jadx.api.JadxArgs;
import jadx.api.JadxArgs.UseKotlinMethodsForVarNames;
import jadx.api.args.GeneratedRenamesMappingFileMode;
import jadx.api.args.ResourceNameSource;
import jadx.api.impl.plugins.PluginsContext;
import jadx.api.plugins.JadxPlugin;
import jadx.api.plugins.options.JadxPluginOptions;
import jadx.api.plugins.options.OptionDescription;
@@ -75,6 +74,8 @@ import jadx.gui.utils.LafManager;
import jadx.gui.utils.LangLocale;
import jadx.gui.utils.NLS;
import jadx.gui.utils.UiUtils;
import jadx.gui.utils.plugins.CollectPluginOptions;
import jadx.gui.utils.plugins.PluginWithOptions;
import jadx.gui.utils.ui.DocumentUpdateListener;
public class JadxSettingsWindow extends JDialog {
@@ -612,39 +613,41 @@ public class JadxSettingsWindow extends JDialog {
private SettingsGroup makePluginOptionsGroup() {
SettingsGroup pluginsGroup = new SettingsGroup(NLS.str("preferences.plugins"));
PluginsContext pluginsContext = mainWindow.getWrapper().getPluginsContext();
for (Map.Entry<JadxPlugin, JadxPluginOptions> entry : pluginsContext.getOptionsMap().entrySet()) {
JadxPlugin plugin = entry.getKey();
JadxPluginOptions options = entry.getValue();
String pluginId = plugin.getPluginInfo().getPluginId();
for (OptionDescription opt : options.getOptionsDescriptions()) {
String title;
if (pluginId.equals("jadx-script")) {
title = '[' + opt.name().replace("jadx-script.", "script:") + "] " + opt.description();
} else {
title = '[' + pluginId + "] " + opt.description();
}
if (opt.values().isEmpty() || opt.getType() == OptionDescription.OptionType.BOOLEAN) {
try {
pluginsGroup.addRow(title, getPluginOptionEditor(opt));
} catch (Exception e) {
LOG.error("Failed to add editor for plugin option: {}", opt.name(), e);
}
} else {
String curValue = settings.getPluginOptions().get(opt.name());
JComboBox<String> combo = new JComboBox<>(opt.values().toArray(new String[0]));
combo.setSelectedItem(curValue != null ? curValue : opt.defaultValue());
combo.addActionListener(e -> {
settings.getPluginOptions().put(opt.name(), ((String) combo.getSelectedItem()));
needReload();
});
pluginsGroup.addRow(title, combo);
}
}
List<PluginWithOptions> list = new CollectPluginOptions(mainWindow.getWrapper()).build();
for (PluginWithOptions data : list) {
addPluginOptions(pluginsGroup, data.getPlugin(), data.getOptions());
}
return pluginsGroup;
}
private void addPluginOptions(SettingsGroup pluginsGroup, JadxPlugin plugin, JadxPluginOptions options) {
String pluginId = plugin.getPluginInfo().getPluginId();
for (OptionDescription opt : options.getOptionsDescriptions()) {
String title;
if (pluginId.equals("jadx-script")) {
title = '[' + opt.name().replace("jadx-script.", "script:") + "] " + opt.description();
} else {
title = '[' + pluginId + "] " + opt.description();
}
if (opt.values().isEmpty() || opt.getType() == OptionDescription.OptionType.BOOLEAN) {
try {
pluginsGroup.addRow(title, getPluginOptionEditor(opt));
} catch (Exception e) {
LOG.error("Failed to add editor for plugin option: {}", opt.name(), e);
}
} else {
String curValue = settings.getPluginOptions().get(opt.name());
JComboBox<String> combo = new JComboBox<>(opt.values().toArray(new String[0]));
combo.setSelectedItem(curValue != null ? curValue : opt.defaultValue());
combo.addActionListener(e -> {
settings.getPluginOptions().put(opt.name(), ((String) combo.getSelectedItem()));
needReload();
});
pluginsGroup.addRow(title, combo);
}
}
}
private JComponent getPluginOptionEditor(OptionDescription opt) {
String curValue = settings.getPluginOptions().get(opt.name());
String value = curValue == null ? opt.defaultValue() : curValue;
@@ -0,0 +1,67 @@
package jadx.gui.utils.plugins;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.plugins.JadxPlugin;
import jadx.api.plugins.JadxPluginManager;
import jadx.api.plugins.options.JadxPluginOptions;
import jadx.gui.JadxWrapper;
/**
* Collect options from all plugins.
* Init not yet loaded plugins in new temporary context.
* Support case if decompiler in wrapper not initialized yet.
*/
public class CollectPluginOptions {
private final JadxWrapper wrapper;
private final Map<Class<?>, PluginWithOptions> plugins;
public CollectPluginOptions(JadxWrapper wrapper) {
this.wrapper = wrapper;
this.plugins = new HashMap<>();
}
public List<PluginWithOptions> build() {
wrapper.getCurrentDecompiler().ifPresent(decompiler -> {
List<JadxPlugin> loadedPlugins = decompiler.getPluginManager().getResolvedPlugins();
addOptions(decompiler, loadedPlugins);
});
// collect and init not loaded plugins in new context
try (JadxDecompiler decompiler = new JadxDecompiler(new JadxArgs())) {
JadxPluginManager pluginManager = decompiler.getPluginManager();
List<JadxPlugin> missingPlugins = new ArrayList<>();
for (JadxPlugin plugin : pluginManager.getAllPlugins()) {
if (!plugins.containsKey(plugin.getClass())) {
missingPlugins.add(plugin);
}
}
pluginManager.init(decompiler.getPluginsContext(), missingPlugins);
addOptions(decompiler, missingPlugins);
}
return plugins.values().stream()
.filter(data -> data != PluginWithOptions.NULL)
.sorted()
.collect(Collectors.toList());
}
private void addOptions(JadxDecompiler decompiler, List<JadxPlugin> loadedPlugins) {
Map<JadxPlugin, JadxPluginOptions> optionsMap = decompiler.getPluginsContext().getOptionsMap();
for (JadxPlugin loadedPlugin : loadedPlugins) {
JadxPluginOptions pluginOptions = optionsMap.get(loadedPlugin);
PluginWithOptions options;
if (pluginOptions != null) {
options = new PluginWithOptions(loadedPlugin, pluginOptions);
} else {
options = PluginWithOptions.NULL;
}
plugins.put(loadedPlugin.getClass(), options);
}
}
}
@@ -0,0 +1,36 @@
package jadx.gui.utils.plugins;
import org.jetbrains.annotations.NotNull;
import jadx.api.plugins.JadxPlugin;
import jadx.api.plugins.options.JadxPluginOptions;
public class PluginWithOptions implements Comparable<PluginWithOptions> {
public static final PluginWithOptions NULL = new PluginWithOptions(null, null);
private final JadxPlugin plugin;
private final JadxPluginOptions options;
public PluginWithOptions(JadxPlugin plugin, JadxPluginOptions options) {
this.plugin = plugin;
this.options = options;
}
public JadxPlugin getPlugin() {
return plugin;
}
public JadxPluginOptions getOptions() {
return options;
}
@Override
public int compareTo(@NotNull PluginWithOptions other) {
return plugin.getClass().getName().compareTo(other.getClass().getName());
}
@Override
public String toString() {
return "PluginWithOptions{plugin=" + plugin + ", options=" + options + '}';
}
}