diff --git a/jadx-core/src/main/java/jadx/api/plugins/JadxPluginManager.java b/jadx-core/src/main/java/jadx/api/plugins/JadxPluginManager.java index 9414b0c84..d50fd79fb 100644 --- a/jadx-core/src/main/java/jadx/api/plugins/JadxPluginManager.java +++ b/jadx-core/src/main/java/jadx/api/plugins/JadxPluginManager.java @@ -119,11 +119,12 @@ public class JadxPluginManager { init(context, resolvedPlugins); } - private void init(PluginsContext context, List plugins) { + public void init(PluginsContext context, List 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); diff --git a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java index 0950132cc..d07e28826 100644 --- a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java +++ b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java @@ -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 getCurrentDecompiler() { + synchronized (DECOMPILER_UPDATE_SYNC) { + return Optional.ofNullable(decompiler); } } diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java index d79ea4967..013212915 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java @@ -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 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 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 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 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; diff --git a/jadx-gui/src/main/java/jadx/gui/utils/plugins/CollectPluginOptions.java b/jadx-gui/src/main/java/jadx/gui/utils/plugins/CollectPluginOptions.java new file mode 100644 index 000000000..e61077119 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/utils/plugins/CollectPluginOptions.java @@ -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, PluginWithOptions> plugins; + + public CollectPluginOptions(JadxWrapper wrapper) { + this.wrapper = wrapper; + this.plugins = new HashMap<>(); + } + + public List build() { + wrapper.getCurrentDecompiler().ifPresent(decompiler -> { + List 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 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 loadedPlugins) { + Map 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); + } + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/utils/plugins/PluginWithOptions.java b/jadx-gui/src/main/java/jadx/gui/utils/plugins/PluginWithOptions.java new file mode 100644 index 000000000..4109632b2 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/utils/plugins/PluginWithOptions.java @@ -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 { + 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 + '}'; + } +}