diff --git a/jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java b/jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java index febf7d71b..3377c48f9 100644 --- a/jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java +++ b/jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java @@ -18,7 +18,7 @@ import com.beust.jcommander.ParameterException; import com.beust.jcommander.Parameterized; import jadx.api.JadxDecompiler; -import jadx.api.impl.plugins.SimplePluginContext; +import jadx.api.impl.plugins.PluginsContext; import jadx.api.plugins.JadxPlugin; import jadx.api.plugins.JadxPluginInfo; import jadx.api.plugins.options.JadxPluginOptions; @@ -73,7 +73,7 @@ public class JCommanderWrapper { } public void printUsage() { - // print usage in not sorted fields order (by default its sorted by description) + // print usage in not sorted fields order (by default sorted by description) PrintStream out = System.out; out.println(); out.println("jadx - dex to java decompiler, version: " + JadxDecompiler.getVersion()); @@ -176,16 +176,11 @@ public class JCommanderWrapper { int k = 1; // load and init all options plugins to print all options try (JadxDecompiler decompiler = new JadxDecompiler(argsObj.toJadxArgs())) { - Map pluginOptions = decompiler.getArgs().getPluginOptions(); - SimplePluginContext context = new SimplePluginContext(decompiler); - for (JadxPlugin plugin : decompiler.getPluginManager().getAllPlugins()) { - if (plugin instanceof JadxPluginOptions) { - JadxPluginOptions optionsPlugin = (JadxPluginOptions) plugin; - optionsPlugin.setOptions(pluginOptions); - optionsPlugin.init(context); - if (appendPlugin(optionsPlugin, sb, maxNamesLen, k)) { - k++; - } + PluginsContext context = new PluginsContext(decompiler); + decompiler.getPluginManager().initAll(context); + for (Map.Entry entry : context.getOptionsMap().entrySet()) { + if (appendPlugin(entry.getKey(), entry.getValue(), sb, maxNamesLen, k)) { + k++; } } } @@ -195,8 +190,8 @@ public class JCommanderWrapper { return "\nPlugin options (-P=):" + sb; } - private boolean appendPlugin(JadxPluginOptions plugin, StringBuilder out, int maxNamesLen, int k) { - List descs = plugin.getOptionsDescriptions(); + private boolean appendPlugin(JadxPlugin plugin, JadxPluginOptions options, StringBuilder out, int maxNamesLen, int k) { + List descs = options.getOptionsDescriptions(); if (descs.isEmpty()) { return false; } diff --git a/jadx-cli/src/main/java/jadx/cli/clst/ConvertToClsSet.java b/jadx-cli/src/main/java/jadx/cli/clst/ConvertToClsSet.java index dad0546a7..439307b6e 100644 --- a/jadx-cli/src/main/java/jadx/cli/clst/ConvertToClsSet.java +++ b/jadx-cli/src/main/java/jadx/cli/clst/ConvertToClsSet.java @@ -12,9 +12,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jadx.api.JadxArgs; +import jadx.api.JadxDecompiler; +import jadx.api.impl.plugins.PluginsContext; import jadx.api.plugins.JadxPluginManager; -import jadx.api.plugins.input.JadxInputPlugin; -import jadx.api.plugins.input.data.ILoadResult; +import jadx.api.plugins.input.ICodeLoader; +import jadx.api.plugins.input.JadxCodeInput; import jadx.core.clsp.ClsSet; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.RootNode; @@ -38,10 +40,12 @@ public class ConvertToClsSet { List inputPaths = Stream.of(args).map(Paths::get).collect(Collectors.toList()); Path output = inputPaths.remove(0); + PluginsContext pluginsContext = new PluginsContext(new JadxDecompiler()); JadxPluginManager pluginManager = new JadxPluginManager(); pluginManager.load(); - List loadedInputs = new ArrayList<>(); - for (JadxInputPlugin inputPlugin : pluginManager.getInputPlugins()) { + pluginManager.initResolved(pluginsContext); + List loadedInputs = new ArrayList<>(); + for (JadxCodeInput inputPlugin : pluginsContext.getCodeInputs()) { loadedInputs.add(inputPlugin.loadFiles(inputPaths)); } diff --git a/jadx-core/src/main/java/jadx/api/JadxArgsValidator.java b/jadx-core/src/main/java/jadx/api/JadxArgsValidator.java index 6819892aa..71aa2980c 100644 --- a/jadx-core/src/main/java/jadx/api/JadxArgsValidator.java +++ b/jadx-core/src/main/java/jadx/api/JadxArgsValidator.java @@ -25,7 +25,7 @@ public class JadxArgsValidator { private static void checkInputFiles(JadxDecompiler jadx, JadxArgs args) { List inputFiles = args.getInputFiles(); - if (inputFiles.isEmpty() && jadx.getCustomLoads().isEmpty()) { + if (inputFiles.isEmpty() && jadx.getCustomCodeLoaders().isEmpty()) { throw new JadxArgsValidateException("Please specify input file"); } for (File inputFile : inputFiles) { diff --git a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java index 01ab230db..c98773e69 100644 --- a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java +++ b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java @@ -23,7 +23,7 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import jadx.api.impl.plugins.SimplePluginContext; +import jadx.api.impl.plugins.PluginsContext; import jadx.api.metadata.ICodeAnnotation; import jadx.api.metadata.ICodeNodeRef; import jadx.api.metadata.annotations.NodeDeclareRef; @@ -31,10 +31,8 @@ import jadx.api.metadata.annotations.VarNode; import jadx.api.metadata.annotations.VarRef; import jadx.api.plugins.JadxPlugin; import jadx.api.plugins.JadxPluginManager; -import jadx.api.plugins.gui.JadxGuiContext; -import jadx.api.plugins.input.JadxInputPlugin; -import jadx.api.plugins.input.data.ILoadResult; -import jadx.api.plugins.options.JadxPluginOptions; +import jadx.api.plugins.input.ICodeLoader; +import jadx.api.plugins.input.JadxCodeInput; import jadx.api.plugins.pass.JadxPass; import jadx.api.plugins.pass.types.JadxAfterLoadPass; import jadx.api.plugins.pass.types.JadxPassType; @@ -88,7 +86,7 @@ public final class JadxDecompiler implements Closeable { private final JadxArgs args; private final JadxPluginManager pluginManager = new JadxPluginManager(); - private final List loadedInputs = new ArrayList<>(); + private final List loadedInputs = new ArrayList<>(); private RootNode root; private List classes; @@ -99,9 +97,9 @@ public final class JadxDecompiler implements Closeable { private final IDecompileScheduler decompileScheduler = new DecompilerScheduler(); - private final List customLoads = new ArrayList<>(); + private final PluginsContext pluginsContext = new PluginsContext(this); + private final List customCodeLoaders = new ArrayList<>(); private final Map> customPasses = new HashMap<>(); - private @Nullable JadxGuiContext guiContext; public JadxDecompiler() { this(new JadxArgs()); @@ -135,13 +133,13 @@ public final class JadxDecompiler implements Closeable { List inputPaths = Utils.collectionMap(args.getInputFiles(), File::toPath); List inputFiles = FileUtils.expandDirs(inputPaths); long start = System.currentTimeMillis(); - for (JadxInputPlugin inputPlugin : pluginManager.getInputPlugins()) { - ILoadResult loadResult = inputPlugin.loadFiles(inputFiles); - if (loadResult != null && !loadResult.isEmpty()) { - loadedInputs.add(loadResult); + for (JadxCodeInput codeLoader : pluginsContext.getCodeInputs()) { + ICodeLoader loader = codeLoader.loadFiles(inputFiles); + if (loader != null && !loader.isEmpty()) { + loadedInputs.add(loader); } } - loadedInputs.addAll(customLoads); + loadedInputs.addAll(customCodeLoaders); if (LOG.isDebugEnabled()) { LOG.debug("Loaded using {} inputs plugin in {} ms", loadedInputs.size(), System.currentTimeMillis() - start); } @@ -180,39 +178,7 @@ public final class JadxDecompiler implements Closeable { LOG.debug("Resolved plugins: {}", Utils.collectionMap(pluginManager.getResolvedPlugins(), p -> p.getPluginInfo().getPluginId())); } - applyPluginOptions(); - initPlugins(); - } - - private void applyPluginOptions() { - Map pluginOptions = args.getPluginOptions(); - if (!pluginOptions.isEmpty()) { - LOG.debug("Applying plugin options: {}", pluginOptions); - for (JadxPluginOptions plugin : pluginManager.getPluginsWithOptions()) { - try { - plugin.setOptions(pluginOptions); - } catch (Exception e) { - String pluginId = plugin.getPluginInfo().getPluginId(); - throw new JadxRuntimeException("Failed to apply options for plugin: " + pluginId, e); - } - } - } - } - - private void initPlugins() { - customPasses.clear(); - - List plugins = pluginManager.getResolvedPlugins(); - SimplePluginContext context = new SimplePluginContext(this); - context.setGuiContext(guiContext); - for (JadxPlugin passPlugin : plugins) { - try { - passPlugin.init(context); - } catch (Exception e) { - String pluginId = passPlugin.getPluginInfo().getPluginId(); - throw new JadxRuntimeException("Failed to pass plugin: " + pluginId, e); - } - } + pluginManager.initResolved(pluginsContext); if (LOG.isDebugEnabled()) { List passes = customPasses.values().stream().flatMap(Collection::stream) .map(p -> p.getInfo().getName()).collect(Collectors.toList()); @@ -684,20 +650,20 @@ public final class JadxDecompiler implements Closeable { return decompileScheduler; } - public void addCustomLoad(ILoadResult customLoad) { - customLoads.add(customLoad); + public void addCustomCodeLoader(ICodeLoader customCodeLoader) { + customCodeLoaders.add(customCodeLoader); } - public List getCustomLoads() { - return customLoads; + public List getCustomCodeLoaders() { + return customCodeLoaders; } public void addCustomPass(JadxPass pass) { customPasses.computeIfAbsent(pass.getPassType(), l -> new ArrayList<>()).add(pass); } - public void setJadxGuiContext(JadxGuiContext guiContext) { - this.guiContext = guiContext; + public PluginsContext getPluginsContext() { + return pluginsContext; } @Override diff --git a/jadx-core/src/main/java/jadx/api/impl/plugins/PluginsContext.java b/jadx-core/src/main/java/jadx/api/impl/plugins/PluginsContext.java new file mode 100644 index 000000000..3086d447c --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/impl/plugins/PluginsContext.java @@ -0,0 +1,86 @@ +package jadx.api.impl.plugins; + +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.jetbrains.annotations.Nullable; + +import jadx.api.JadxArgs; +import jadx.api.JadxDecompiler; +import jadx.api.plugins.JadxPlugin; +import jadx.api.plugins.JadxPluginContext; +import jadx.api.plugins.gui.JadxGuiContext; +import jadx.api.plugins.input.JadxCodeInput; +import jadx.api.plugins.options.JadxPluginOptions; +import jadx.api.plugins.pass.JadxPass; +import jadx.core.utils.exceptions.JadxRuntimeException; + +public class PluginsContext implements JadxPluginContext { + + private final JadxDecompiler decompiler; + private final List codeInputs = new ArrayList<>(); + private final Map optionsMap = new IdentityHashMap<>(); + private @Nullable JadxGuiContext guiContext; + + private @Nullable JadxPlugin currentPlugin; + + public PluginsContext(JadxDecompiler decompiler) { + this.decompiler = decompiler; + } + + @Override + public JadxArgs getArgs() { + return decompiler.getArgs(); + } + + @Override + public JadxDecompiler getDecompiler() { + return decompiler; + } + + @Override + public void addPass(JadxPass pass) { + decompiler.addCustomPass(pass); + } + + @Override + public void addCodeInput(JadxCodeInput codeInput) { + codeInputs.add(codeInput); + } + + public List getCodeInputs() { + return codeInputs; + } + + public void setCurrentPlugin(JadxPlugin currentPlugin) { + this.currentPlugin = currentPlugin; + } + + @Override + public void registerOptions(JadxPluginOptions options) { + Objects.requireNonNull(currentPlugin); + try { + options.setOptions(decompiler.getArgs().getPluginOptions()); + optionsMap.put(currentPlugin, options); + } catch (Exception e) { + String pluginId = currentPlugin.getPluginInfo().getPluginId(); + throw new JadxRuntimeException("Failed to apply options for plugin: " + pluginId, e); + } + } + + public Map getOptionsMap() { + return optionsMap; + } + + @Override + public @Nullable JadxGuiContext getGuiContext() { + return guiContext; + } + + public void setGuiContext(JadxGuiContext guiContext) { + this.guiContext = guiContext; + } +} diff --git a/jadx-core/src/main/java/jadx/api/impl/plugins/SimplePassContext.java b/jadx-core/src/main/java/jadx/api/impl/plugins/SimplePassContext.java deleted file mode 100644 index ae60ce53f..000000000 --- a/jadx-core/src/main/java/jadx/api/impl/plugins/SimplePassContext.java +++ /dev/null @@ -1,19 +0,0 @@ -package jadx.api.impl.plugins; - -import jadx.api.JadxDecompiler; -import jadx.api.plugins.pass.JadxPass; -import jadx.api.plugins.pass.JadxPassContext; - -public class SimplePassContext implements JadxPassContext { - - private final JadxDecompiler jadxDecompiler; - - public SimplePassContext(JadxDecompiler jadxDecompiler) { - this.jadxDecompiler = jadxDecompiler; - } - - @Override - public void addPass(JadxPass pass) { - jadxDecompiler.addCustomPass(pass); - } -} diff --git a/jadx-core/src/main/java/jadx/api/impl/plugins/SimplePluginContext.java b/jadx-core/src/main/java/jadx/api/impl/plugins/SimplePluginContext.java deleted file mode 100644 index 38940e1ed..000000000 --- a/jadx-core/src/main/java/jadx/api/impl/plugins/SimplePluginContext.java +++ /dev/null @@ -1,45 +0,0 @@ -package jadx.api.impl.plugins; - -import org.jetbrains.annotations.Nullable; - -import jadx.api.JadxArgs; -import jadx.api.JadxDecompiler; -import jadx.api.plugins.JadxPluginContext; -import jadx.api.plugins.gui.JadxGuiContext; -import jadx.api.plugins.pass.JadxPassContext; - -public class SimplePluginContext implements JadxPluginContext { - - private final JadxDecompiler decompiler; - private final JadxPassContext passContext; - private @Nullable JadxGuiContext guiContext; - - public SimplePluginContext(JadxDecompiler decompiler) { - this.decompiler = decompiler; - this.passContext = new SimplePassContext(decompiler); - } - - @Override - public JadxArgs getArgs() { - return decompiler.getArgs(); - } - - @Override - public JadxDecompiler getDecompiler() { - return decompiler; - } - - @Override - public JadxPassContext getPassContext() { - return passContext; - } - - @Override - public @Nullable JadxGuiContext getGuiContext() { - return guiContext; - } - - public void setGuiContext(JadxGuiContext guiContext) { - this.guiContext = guiContext; - } -} diff --git a/jadx-core/src/main/java/jadx/api/plugins/JadxPlugin.java b/jadx-core/src/main/java/jadx/api/plugins/JadxPlugin.java index 52536a682..598361b40 100644 --- a/jadx-core/src/main/java/jadx/api/plugins/JadxPlugin.java +++ b/jadx-core/src/main/java/jadx/api/plugins/JadxPlugin.java @@ -1,9 +1,8 @@ package jadx.api.plugins; public interface JadxPlugin { + JadxPluginInfo getPluginInfo(); - default void init(JadxPluginContext context) { - // default to no-op - } + void init(JadxPluginContext context); } diff --git a/jadx-core/src/main/java/jadx/api/plugins/JadxPluginContext.java b/jadx-core/src/main/java/jadx/api/plugins/JadxPluginContext.java index ebbf48883..ead7d6a81 100644 --- a/jadx-core/src/main/java/jadx/api/plugins/JadxPluginContext.java +++ b/jadx-core/src/main/java/jadx/api/plugins/JadxPluginContext.java @@ -5,7 +5,9 @@ import org.jetbrains.annotations.Nullable; import jadx.api.JadxArgs; import jadx.api.JadxDecompiler; import jadx.api.plugins.gui.JadxGuiContext; -import jadx.api.plugins.pass.JadxPassContext; +import jadx.api.plugins.input.JadxCodeInput; +import jadx.api.plugins.options.JadxPluginOptions; +import jadx.api.plugins.pass.JadxPass; public interface JadxPluginContext { @@ -13,7 +15,11 @@ public interface JadxPluginContext { JadxDecompiler getDecompiler(); - JadxPassContext getPassContext(); + void addPass(JadxPass pass); + + void addCodeInput(JadxCodeInput codeInput); + + void registerOptions(JadxPluginOptions options); @Nullable JadxGuiContext getGuiContext(); 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 23a983edd..9414b0c84 100644 --- a/jadx-core/src/main/java/jadx/api/plugins/JadxPluginManager.java +++ b/jadx-core/src/main/java/jadx/api/plugins/JadxPluginManager.java @@ -15,9 +15,10 @@ import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import jadx.api.plugins.input.JadxInputPlugin; +import jadx.api.impl.plugins.PluginsContext; import jadx.api.plugins.options.JadxPluginOptions; import jadx.api.plugins.options.OptionDescription; +import jadx.core.utils.exceptions.JadxRuntimeException; public class JadxPluginManager { private static final Logger LOG = LoggerFactory.getLogger(JadxPluginManager.class); @@ -59,34 +60,9 @@ public class JadxPluginManager { if (!allPlugins.add(pluginData)) { throw new IllegalArgumentException("Duplicate plugin id: " + pluginData + ", class " + plugin.getClass()); } - if (plugin instanceof JadxPluginOptions) { - verifyOptions(((JadxPluginOptions) plugin), pluginData.getPluginId()); - } return pluginData; } - private void verifyOptions(JadxPluginOptions plugin, String pluginId) { - List descriptions = plugin.getOptionsDescriptions(); - if (descriptions == null) { - throw new IllegalArgumentException("Null option descriptions in plugin id: " + pluginId); - } - String prefix = pluginId + '.'; - descriptions.forEach(descObj -> { - String optName = descObj.name(); - if (optName == null || !optName.startsWith(prefix)) { - throw new IllegalArgumentException("Plugin option name should start with plugin id: '" + prefix + "', option: " + optName); - } - String desc = descObj.description(); - if (desc == null || desc.isEmpty()) { - throw new IllegalArgumentException("Plugin option description not set, plugin: " + pluginId); - } - List values = descObj.values(); - if (values == null) { - throw new IllegalArgumentException("Plugin option values is null, option: " + optName + ", plugin: " + pluginId); - } - }); - } - public boolean unload(String pluginId) { boolean result = allPlugins.removeIf(pd -> { String id = pd.getPluginId(); @@ -111,22 +87,6 @@ public class JadxPluginManager { return Collections.unmodifiableList(resolvedPlugins); } - public List getInputPlugins() { - return getPluginsWithType(JadxInputPlugin.class); - } - - public List getPluginsWithOptions() { - return getPluginsWithType(JadxPluginOptions.class); - } - - @SuppressWarnings("unchecked") - public List getPluginsWithType(Class type) { - return resolvedPlugins.stream() - .filter(p -> type.isAssignableFrom(p.getClass())) - .map(p -> (T) p) - .collect(Collectors.toList()); - } - private synchronized void resolve() { Map> provides = allPlugins.stream() .collect(Collectors.groupingBy(p -> p.getInfo().getProvides())); @@ -151,6 +111,52 @@ public class JadxPluginManager { resolvedPlugins = result.stream().map(PluginData::getPlugin).collect(Collectors.toList()); } + public void initAll(PluginsContext context) { + init(context, getAllPlugins()); + } + + public void initResolved(PluginsContext context) { + init(context, resolvedPlugins); + } + + private void init(PluginsContext context, List plugins) { + for (JadxPlugin plugin : plugins) { + try { + context.setCurrentPlugin(plugin); + plugin.init(context); + } catch (Exception e) { + String pluginId = plugin.getPluginInfo().getPluginId(); + throw new JadxRuntimeException("Failed to init plugin: " + pluginId, e); + } + } + for (Map.Entry entry : context.getOptionsMap().entrySet()) { + verifyOptions(entry.getKey(), entry.getValue()); + } + } + + private void verifyOptions(JadxPlugin plugin, JadxPluginOptions options) { + String pluginId = plugin.getPluginInfo().getPluginId(); + List descriptions = options.getOptionsDescriptions(); + if (descriptions == null) { + throw new IllegalArgumentException("Null option descriptions in plugin id: " + pluginId); + } + String prefix = pluginId + '.'; + descriptions.forEach(descObj -> { + String optName = descObj.name(); + if (optName == null || !optName.startsWith(prefix)) { + throw new IllegalArgumentException("Plugin option name should start with plugin id: '" + prefix + "', option: " + optName); + } + String desc = descObj.description(); + if (desc == null || desc.isEmpty()) { + throw new IllegalArgumentException("Plugin option description not set, plugin: " + pluginId); + } + List values = descObj.values(); + if (values == null) { + throw new IllegalArgumentException("Plugin option values is null, option: " + optName + ", plugin: " + pluginId); + } + }); + } + private static final class PluginData implements Comparable { private final JadxPlugin plugin; private final JadxPluginInfo info; diff --git a/jadx-core/src/main/java/jadx/api/plugins/input/ICodeLoader.java b/jadx-core/src/main/java/jadx/api/plugins/input/ICodeLoader.java new file mode 100644 index 000000000..ae7d93dd7 --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/plugins/input/ICodeLoader.java @@ -0,0 +1,13 @@ +package jadx.api.plugins.input; + +import java.io.Closeable; +import java.util.function.Consumer; + +import jadx.api.plugins.input.data.IClassData; + +public interface ICodeLoader extends Closeable { + + void visitClasses(Consumer consumer); + + boolean isEmpty(); +} diff --git a/jadx-core/src/main/java/jadx/api/plugins/input/JadxCodeInput.java b/jadx-core/src/main/java/jadx/api/plugins/input/JadxCodeInput.java new file mode 100644 index 000000000..9907dad0b --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/plugins/input/JadxCodeInput.java @@ -0,0 +1,8 @@ +package jadx.api.plugins.input; + +import java.nio.file.Path; +import java.util.List; + +public interface JadxCodeInput { + ICodeLoader loadFiles(List input); +} diff --git a/jadx-core/src/main/java/jadx/api/plugins/input/JadxInputPlugin.java b/jadx-core/src/main/java/jadx/api/plugins/input/JadxInputPlugin.java deleted file mode 100644 index 6f07f451f..000000000 --- a/jadx-core/src/main/java/jadx/api/plugins/input/JadxInputPlugin.java +++ /dev/null @@ -1,11 +0,0 @@ -package jadx.api.plugins.input; - -import java.nio.file.Path; -import java.util.List; - -import jadx.api.plugins.JadxPlugin; -import jadx.api.plugins.input.data.ILoadResult; - -public interface JadxInputPlugin extends JadxPlugin { - ILoadResult loadFiles(List input); -} diff --git a/jadx-core/src/main/java/jadx/api/plugins/input/data/ILoadResult.java b/jadx-core/src/main/java/jadx/api/plugins/input/data/ILoadResult.java deleted file mode 100644 index d4fd16f39..000000000 --- a/jadx-core/src/main/java/jadx/api/plugins/input/data/ILoadResult.java +++ /dev/null @@ -1,12 +0,0 @@ -package jadx.api.plugins.input.data; - -import java.io.Closeable; -import java.util.function.Consumer; - -public interface ILoadResult extends Closeable { - void visitClasses(Consumer consumer); - - void visitResources(Consumer consumer); - - boolean isEmpty(); -} diff --git a/jadx-core/src/main/java/jadx/api/plugins/input/data/impl/EmptyLoadResult.java b/jadx-core/src/main/java/jadx/api/plugins/input/data/impl/EmptyCodeLoader.java similarity index 53% rename from jadx-core/src/main/java/jadx/api/plugins/input/data/impl/EmptyLoadResult.java rename to jadx-core/src/main/java/jadx/api/plugins/input/data/impl/EmptyCodeLoader.java index 84d60cbc1..905aec115 100644 --- a/jadx-core/src/main/java/jadx/api/plugins/input/data/impl/EmptyLoadResult.java +++ b/jadx-core/src/main/java/jadx/api/plugins/input/data/impl/EmptyCodeLoader.java @@ -3,13 +3,12 @@ package jadx.api.plugins.input.data.impl; import java.io.IOException; import java.util.function.Consumer; +import jadx.api.plugins.input.ICodeLoader; import jadx.api.plugins.input.data.IClassData; -import jadx.api.plugins.input.data.ILoadResult; -import jadx.api.plugins.input.data.IResourceData; -public class EmptyLoadResult implements ILoadResult { +public class EmptyCodeLoader implements ICodeLoader { - public static final EmptyLoadResult INSTANCE = new EmptyLoadResult(); + public static final EmptyCodeLoader INSTANCE = new EmptyCodeLoader(); @Override public boolean isEmpty() { @@ -20,10 +19,6 @@ public class EmptyLoadResult implements ILoadResult { public void visitClasses(Consumer consumer) { } - @Override - public void visitResources(Consumer consumer) { - } - @Override public void close() throws IOException { } diff --git a/jadx-core/src/main/java/jadx/api/plugins/options/JadxPluginOptions.java b/jadx-core/src/main/java/jadx/api/plugins/options/JadxPluginOptions.java index 55ace7ebf..6daddbad5 100644 --- a/jadx-core/src/main/java/jadx/api/plugins/options/JadxPluginOptions.java +++ b/jadx-core/src/main/java/jadx/api/plugins/options/JadxPluginOptions.java @@ -3,9 +3,7 @@ package jadx.api.plugins.options; import java.util.List; import java.util.Map; -import jadx.api.plugins.JadxPlugin; - -public interface JadxPluginOptions extends JadxPlugin { +public interface JadxPluginOptions { void setOptions(Map options); diff --git a/jadx-core/src/main/java/jadx/api/plugins/options/impl/BaseOptionsParser.java b/jadx-core/src/main/java/jadx/api/plugins/options/impl/BaseOptionsParser.java index 1526580d6..912a51bf3 100644 --- a/jadx-core/src/main/java/jadx/api/plugins/options/impl/BaseOptionsParser.java +++ b/jadx-core/src/main/java/jadx/api/plugins/options/impl/BaseOptionsParser.java @@ -4,9 +4,21 @@ import java.util.Locale; import java.util.Map; import java.util.function.Function; -public class BaseOptionsParser { +import jadx.api.plugins.options.JadxPluginOptions; - public boolean getBooleanOption(Map options, String key, boolean defValue) { +public abstract class BaseOptionsParser implements JadxPluginOptions { + + protected Map options; + + @Override + public void setOptions(Map options) { + this.options = options; + parseOptions(); + } + + public abstract void parseOptions(); + + public boolean getBooleanOption(String key, boolean defValue) { String val = options.get(key); if (val == null) { return defValue; @@ -22,7 +34,7 @@ public class BaseOptionsParser { + ", expect: 'yes' or 'no'"); } - public T getOption(Map options, String key, Function parse, T defValue) { + public T getOption(String key, Function parse, T defValue) { String val = options.get(key); if (val == null) { return defValue; diff --git a/jadx-core/src/main/java/jadx/api/plugins/pass/JadxPassContext.java b/jadx-core/src/main/java/jadx/api/plugins/pass/JadxPassContext.java deleted file mode 100644 index fd8a7dba6..000000000 --- a/jadx-core/src/main/java/jadx/api/plugins/pass/JadxPassContext.java +++ /dev/null @@ -1,6 +0,0 @@ -package jadx.api.plugins.pass; - -public interface JadxPassContext { - - void addPass(JadxPass pass); -} diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java index 2ba6f7b71..b4bdbd342 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java @@ -23,8 +23,8 @@ import jadx.api.ResourcesLoader; import jadx.api.data.ICodeData; import jadx.api.impl.passes.DecompilePassWrapper; import jadx.api.impl.passes.PreparePassWrapper; +import jadx.api.plugins.input.ICodeLoader; import jadx.api.plugins.input.data.IClassData; -import jadx.api.plugins.input.data.ILoadResult; import jadx.api.plugins.pass.JadxPass; import jadx.api.plugins.pass.types.JadxDecompilePass; import jadx.api.plugins.pass.types.JadxPassType; @@ -115,9 +115,9 @@ public class RootNode { } } - public void loadClasses(List loadedInputs) { - for (ILoadResult loadedInput : loadedInputs) { - loadedInput.visitClasses(cls -> { + public void loadClasses(List loadedInputs) { + for (ICodeLoader codeLoader : loadedInputs) { + codeLoader.visitClasses(cls -> { try { addClassNode(new ClassNode(RootNode.this, cls)); } catch (Exception e) { diff --git a/jadx-core/src/test/java/jadx/api/JadxDecompilerTest.java b/jadx-core/src/test/java/jadx/api/JadxDecompilerTest.java index a061a6bab..adbd5ce1b 100644 --- a/jadx-core/src/test/java/jadx/api/JadxDecompilerTest.java +++ b/jadx-core/src/test/java/jadx/api/JadxDecompilerTest.java @@ -46,7 +46,7 @@ public class JadxDecompilerTest { public void testDirectDexInput() throws IOException { try (JadxDecompiler jadx = new JadxDecompiler(); InputStream in = new FileInputStream(getFileFromSampleDir("hello.dex"))) { - jadx.addCustomLoad(new DexInputPlugin().loadDexFromInputStream(in, "input")); + jadx.addCustomCodeLoader(new DexInputPlugin().loadDexFromInputStream(in, "input")); jadx.load(); for (JavaClass cls : jadx.getClasses()) { System.out.println(cls.getCode()); diff --git a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java index cb0ec3d0e..e2bca90f5 100644 --- a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java +++ b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java @@ -19,14 +19,13 @@ 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.plugins.JadxPlugin; -import jadx.api.plugins.JadxPluginManager; 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.gui.plugins.context.PluginsContext; +import jadx.gui.plugins.context.GuiPluginsContext; import jadx.gui.settings.JadxProject; import jadx.gui.settings.JadxSettings; import jadx.gui.ui.MainWindow; @@ -47,7 +46,7 @@ public class JadxWrapper { private final MainWindow mainWindow; private volatile @Nullable JadxDecompiler decompiler; - private PluginsContext pluginsContext; + private GuiPluginsContext guiPluginsContext; public JadxWrapper(MainWindow mainWindow) { this.mainWindow = mainWindow; @@ -62,8 +61,8 @@ public class JadxWrapper { project.fillJadxArgs(jadxArgs); this.decompiler = new JadxDecompiler(jadxArgs); - this.pluginsContext = new PluginsContext(mainWindow); - this.decompiler.setJadxGuiContext(pluginsContext); + this.guiPluginsContext = new GuiPluginsContext(mainWindow); + this.decompiler.getPluginsContext().setGuiContext(guiPluginsContext); this.decompiler.load(); initCodeCache(); } @@ -89,9 +88,9 @@ public class JadxWrapper { decompiler.close(); decompiler = null; } - if (pluginsContext != null) { - pluginsContext.reset(); - pluginsContext = null; + if (guiPluginsContext != null) { + guiPluginsContext.reset(); + guiPluginsContext = null; } } } catch (Exception e) { @@ -197,13 +196,14 @@ public class JadxWrapper { getSettings().sync(); } - public List getAllPlugins() { + public PluginsContext getPluginsContext() { if (decompiler != null) { - return decompiler.getPluginManager().getAllPlugins(); + return decompiler.getPluginsContext(); + } + try (JadxDecompiler tmpDecompiler = new JadxDecompiler()) { + tmpDecompiler.load(); + return tmpDecompiler.getPluginsContext(); } - JadxPluginManager pluginManager = new JadxPluginManager(); - pluginManager.load(); - return pluginManager.getAllPlugins(); } /** diff --git a/jadx-gui/src/main/java/jadx/gui/plugins/context/PluginsContext.java b/jadx-gui/src/main/java/jadx/gui/plugins/context/GuiPluginsContext.java similarity index 83% rename from jadx-gui/src/main/java/jadx/gui/plugins/context/PluginsContext.java rename to jadx-gui/src/main/java/jadx/gui/plugins/context/GuiPluginsContext.java index 2a55544a3..6c8fdc27a 100644 --- a/jadx-gui/src/main/java/jadx/gui/plugins/context/PluginsContext.java +++ b/jadx-gui/src/main/java/jadx/gui/plugins/context/GuiPluginsContext.java @@ -10,12 +10,12 @@ import jadx.gui.ui.MainWindow; import jadx.gui.utils.UiUtils; import jadx.gui.utils.ui.ActionHandler; -public class PluginsContext implements JadxGuiContext { - private static final Logger LOG = LoggerFactory.getLogger(PluginsContext.class); +public class GuiPluginsContext implements JadxGuiContext { + private static final Logger LOG = LoggerFactory.getLogger(GuiPluginsContext.class); private final MainWindow mainWindow; - public PluginsContext(MainWindow mainWindow) { + public GuiPluginsContext(MainWindow mainWindow) { this.mainWindow = mainWindow; } 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 32bcb087b..8f5543205 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java @@ -21,6 +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.Objects; import java.util.Set; @@ -61,8 +62,8 @@ 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.JadxPluginInfo; import jadx.api.plugins.options.JadxPluginOptions; import jadx.api.plugins.options.OptionDescription; import jadx.gui.ui.MainWindow; @@ -602,14 +603,18 @@ public class JadxSettingsWindow extends JDialog { private SettingsGroup makePluginOptionsGroup() { SettingsGroup pluginsGroup = new SettingsGroup(NLS.str("preferences.plugins")); - for (JadxPlugin plugin : mainWindow.getWrapper().getAllPlugins()) { - if (!(plugin instanceof JadxPluginOptions)) { - continue; - } - JadxPluginInfo pluginInfo = plugin.getPluginInfo(); - JadxPluginOptions optPlugin = (JadxPluginOptions) plugin; - for (OptionDescription opt : optPlugin.getOptionsDescriptions()) { - String title = "[" + pluginInfo.getPluginId() + "] " + opt.description(); + 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)); diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexInputOptions.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexInputOptions.java index 5672f30fa..17d49273d 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexInputOptions.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexInputOptions.java @@ -3,7 +3,6 @@ package jadx.plugins.input.dex; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Map; import jadx.api.plugins.options.OptionDescription; import jadx.api.plugins.options.impl.BaseOptionsParser; @@ -15,11 +14,12 @@ public class DexInputOptions extends BaseOptionsParser { private boolean verifyChecksum = true; - public void apply(Map options) { - verifyChecksum = getBooleanOption(options, VERIFY_CHECKSUM_OPT, true); + @Override + public void parseOptions() { + verifyChecksum = getBooleanOption(VERIFY_CHECKSUM_OPT, true); } - public List buildOptionsDescriptions() { + public List getOptionsDescriptions() { return Collections.singletonList( new JadxOptionDescription( VERIFY_CHECKSUM_OPT, diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexInputPlugin.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexInputPlugin.java index fcdfef11e..80a68005e 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexInputPlugin.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexInputPlugin.java @@ -5,19 +5,17 @@ import java.io.InputStream; import java.nio.file.Path; import java.util.Collections; import java.util.List; -import java.util.Map; import org.jetbrains.annotations.Nullable; +import jadx.api.plugins.JadxPlugin; +import jadx.api.plugins.JadxPluginContext; import jadx.api.plugins.JadxPluginInfo; -import jadx.api.plugins.input.JadxInputPlugin; -import jadx.api.plugins.input.data.ILoadResult; -import jadx.api.plugins.input.data.impl.EmptyLoadResult; -import jadx.api.plugins.options.JadxPluginOptions; -import jadx.api.plugins.options.OptionDescription; +import jadx.api.plugins.input.ICodeLoader; +import jadx.api.plugins.input.data.impl.EmptyCodeLoader; import jadx.api.plugins.utils.CommonFileUtils; -public class DexInputPlugin implements JadxInputPlugin, JadxPluginOptions { +public class DexInputPlugin implements JadxPlugin { public static final String PLUGIN_ID = "dex-input"; private final DexInputOptions options = new DexInputOptions(); @@ -29,39 +27,34 @@ public class DexInputPlugin implements JadxInputPlugin, JadxPluginOptions { } @Override - public ILoadResult loadFiles(List input) { + public void init(JadxPluginContext context) { + context.registerOptions(options); + context.addCodeInput(this::loadFiles); + } + + public ICodeLoader loadFiles(List input) { return loadFiles(input, null); } - public ILoadResult loadFiles(List inputFiles, @Nullable Closeable closeable) { + public ICodeLoader loadFiles(List inputFiles, @Nullable Closeable closeable) { List dexReaders = loader.collectDexFiles(inputFiles); if (dexReaders.isEmpty()) { - return EmptyLoadResult.INSTANCE; + return EmptyCodeLoader.INSTANCE; } return new DexLoadResult(dexReaders, closeable); } - public ILoadResult loadDex(byte[] content, @Nullable String fileName) { + public ICodeLoader loadDex(byte[] content, @Nullable String fileName) { String fileLabel = fileName == null ? "input.dex" : fileName; DexReader dexReader = loader.loadDexReader(fileLabel, content); return new DexLoadResult(Collections.singletonList(dexReader), null); } - public ILoadResult loadDexFromInputStream(InputStream in, @Nullable String fileLabel) { + public ICodeLoader loadDexFromInputStream(InputStream in, @Nullable String fileLabel) { try { return loadDex(CommonFileUtils.loadBytes(in), fileLabel); } catch (Exception e) { throw new DexException("Failed to read input stream", e); } } - - @Override - public void setOptions(Map options) { - this.options.apply(options); - } - - @Override - public List getOptionsDescriptions() { - return this.options.buildOptionsDescriptions(); - } } diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexLoadResult.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexLoadResult.java index 2d1870d15..f7a14f8fe 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexLoadResult.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexLoadResult.java @@ -7,11 +7,10 @@ import java.util.function.Consumer; import org.jetbrains.annotations.Nullable; +import jadx.api.plugins.input.ICodeLoader; import jadx.api.plugins.input.data.IClassData; -import jadx.api.plugins.input.data.ILoadResult; -import jadx.api.plugins.input.data.IResourceData; -public class DexLoadResult implements ILoadResult { +public class DexLoadResult implements ICodeLoader { private final List dexReaders; @Nullable private final Closeable closeable; @@ -28,10 +27,6 @@ public class DexLoadResult implements ILoadResult { } } - @Override - public void visitResources(Consumer consumer) { - } - @Override public void close() throws IOException { if (closeable != null) { diff --git a/jadx-plugins/jadx-dex-input/src/test/java/jadx/plugins/input/dex/DexInputPluginTest.java b/jadx-plugins/jadx-dex-input/src/test/java/jadx/plugins/input/dex/DexInputPluginTest.java index 48d0776bc..0fddabec0 100644 --- a/jadx-plugins/jadx-dex-input/src/test/java/jadx/plugins/input/dex/DexInputPluginTest.java +++ b/jadx-plugins/jadx-dex-input/src/test/java/jadx/plugins/input/dex/DexInputPluginTest.java @@ -9,10 +9,10 @@ import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Test; +import jadx.api.plugins.input.ICodeLoader; import jadx.api.plugins.input.data.AccessFlags; import jadx.api.plugins.input.data.AccessFlagsScope; import jadx.api.plugins.input.data.ICodeReader; -import jadx.api.plugins.input.data.ILoadResult; import jadx.plugins.input.dex.utils.SmaliTestUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -38,7 +38,7 @@ class DexInputPluginTest { System.out.println("Input file: " + sample.toAbsolutePath()); long start = System.currentTimeMillis(); List files = Collections.singletonList(sample); - try (ILoadResult result = new DexInputPlugin().loadFiles(files)) { + try (ICodeLoader result = new DexInputPlugin().loadFiles(files)) { AtomicInteger count = new AtomicInteger(); result.visitClasses(cls -> { System.out.println(); diff --git a/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertOptions.java b/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertOptions.java index 85391e61c..1ac71abd4 100644 --- a/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertOptions.java +++ b/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertOptions.java @@ -3,7 +3,6 @@ package jadx.plugins.input.javaconvert; import java.util.Arrays; import java.util.List; import java.util.Locale; -import java.util.Map; import jadx.api.plugins.options.OptionDescription; import jadx.api.plugins.options.impl.BaseOptionsParser; @@ -21,12 +20,14 @@ public class JavaConvertOptions extends BaseOptionsParser { private Mode mode = Mode.BOTH; private boolean d8Desugar = false; - public void apply(Map options) { - mode = getOption(options, MODE_OPT, name -> Mode.valueOf(name.toUpperCase(Locale.ROOT)), Mode.BOTH); - d8Desugar = getBooleanOption(options, D8_DESUGAR_OPT, false); + @Override + public void parseOptions() { + mode = getOption(MODE_OPT, name -> Mode.valueOf(name.toUpperCase(Locale.ROOT)), Mode.BOTH); + d8Desugar = getBooleanOption(D8_DESUGAR_OPT, false); } - public List buildOptionsDescriptions() { + @Override + public List getOptionsDescriptions() { return Arrays.asList( new JadxOptionDescription( MODE_OPT, diff --git a/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertPlugin.java b/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertPlugin.java index 34d0ba956..188ddfbb2 100644 --- a/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertPlugin.java +++ b/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertPlugin.java @@ -2,17 +2,16 @@ package jadx.plugins.input.javaconvert; import java.nio.file.Path; import java.util.List; -import java.util.Map; +import jadx.api.plugins.JadxPlugin; +import jadx.api.plugins.JadxPluginContext; import jadx.api.plugins.JadxPluginInfo; -import jadx.api.plugins.input.JadxInputPlugin; -import jadx.api.plugins.input.data.ILoadResult; -import jadx.api.plugins.input.data.impl.EmptyLoadResult; -import jadx.api.plugins.options.JadxPluginOptions; -import jadx.api.plugins.options.OptionDescription; +import jadx.api.plugins.input.ICodeLoader; +import jadx.api.plugins.input.JadxCodeInput; +import jadx.api.plugins.input.data.impl.EmptyCodeLoader; import jadx.plugins.input.dex.DexInputPlugin; -public class JavaConvertPlugin implements JadxInputPlugin, JadxPluginOptions { +public class JavaConvertPlugin implements JadxPlugin, JadxCodeInput { public static final String PLUGIN_ID = "java-convert"; @@ -30,22 +29,18 @@ public class JavaConvertPlugin implements JadxInputPlugin, JadxPluginOptions { } @Override - public ILoadResult loadFiles(List input) { + public void init(JadxPluginContext context) { + context.registerOptions(options); + context.addCodeInput(this); + } + + @Override + public ICodeLoader loadFiles(List input) { ConvertResult result = loader.process(input); if (result.isEmpty()) { result.close(); - return EmptyLoadResult.INSTANCE; + return EmptyCodeLoader.INSTANCE; } return dexInput.loadFiles(result.getConverted(), result); } - - @Override - public void setOptions(Map options) { - this.options.apply(options); - } - - @Override - public List getOptionsDescriptions() { - return this.options.buildOptionsDescriptions(); - } } diff --git a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaInputPlugin.java b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaInputPlugin.java index 57dc8a159..973fdeaa2 100644 --- a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaInputPlugin.java +++ b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaInputPlugin.java @@ -9,13 +9,14 @@ import java.util.function.Function; import org.jetbrains.annotations.Nullable; +import jadx.api.plugins.JadxPlugin; +import jadx.api.plugins.JadxPluginContext; import jadx.api.plugins.JadxPluginInfo; -import jadx.api.plugins.input.JadxInputPlugin; -import jadx.api.plugins.input.data.ILoadResult; -import jadx.api.plugins.input.data.impl.EmptyLoadResult; +import jadx.api.plugins.input.ICodeLoader; +import jadx.api.plugins.input.data.impl.EmptyCodeLoader; import jadx.plugins.input.java.utils.JavaClassParseException; -public class JavaInputPlugin implements JadxInputPlugin { +public class JavaInputPlugin implements JadxPlugin { public static final JadxPluginInfo PLUGIN_INFO = new JadxPluginInfo( "java-input", @@ -28,26 +29,26 @@ public class JavaInputPlugin implements JadxInputPlugin { } @Override - public ILoadResult loadFiles(List inputFiles) { + public void init(JadxPluginContext context) { + context.addCodeInput(JavaInputPlugin::loadClassFiles); + } + + public static ICodeLoader loadClassFiles(List inputFiles) { return loadClassFiles(inputFiles, null); } - public static ILoadResult loadClassFiles(List inputFiles, @Nullable Closeable closeable) { + public static ICodeLoader loadClassFiles(List inputFiles, @Nullable Closeable closeable) { List readers = new JavaInputLoader().collectFiles(inputFiles); if (readers.isEmpty()) { - return EmptyLoadResult.INSTANCE; + return EmptyCodeLoader.INSTANCE; } return new JavaLoadResult(readers, closeable); } - public static ILoadResult loadClassFiles(List inputFiles) { - return loadClassFiles(inputFiles, null); - } - /** * Method for provide several inputs by using load methods from {@link JavaInputLoader} class. */ - public static ILoadResult load(Function> loader) { + public static ICodeLoader load(Function> loader) { return wrapClassReaders(loader.apply(new JavaInputLoader())); } @@ -56,7 +57,7 @@ public class JavaInputPlugin implements JadxInputPlugin { * Should be used only once per JadxDecompiler instance. * For load several times use {@link JavaInputPlugin#load(Function)} method. */ - public static ILoadResult loadFromInputStream(InputStream in, String fileName) { + public static ICodeLoader loadFromInputStream(InputStream in, String fileName) { try { return wrapClassReaders(new JavaInputLoader().loadInputStream(in, fileName)); } catch (Exception e) { @@ -69,14 +70,14 @@ public class JavaInputPlugin implements JadxInputPlugin { * Should be used only once per JadxDecompiler instance. * For load several times use {@link JavaInputPlugin#load(Function)} method. */ - public static ILoadResult loadSingleClass(byte[] content, String fileName) { + public static ICodeLoader loadSingleClass(byte[] content, String fileName) { JavaClassReader reader = new JavaInputLoader().loadClass(content, fileName); return new JavaLoadResult(Collections.singletonList(reader)); } - public static ILoadResult wrapClassReaders(List readers) { + public static ICodeLoader wrapClassReaders(List readers) { if (readers.isEmpty()) { - return EmptyLoadResult.INSTANCE; + return EmptyCodeLoader.INSTANCE; } return new JavaLoadResult(readers); } diff --git a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaLoadResult.java b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaLoadResult.java index add098fc9..537007774 100644 --- a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaLoadResult.java +++ b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaLoadResult.java @@ -9,11 +9,10 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jadx.api.plugins.input.ICodeLoader; import jadx.api.plugins.input.data.IClassData; -import jadx.api.plugins.input.data.ILoadResult; -import jadx.api.plugins.input.data.IResourceData; -public class JavaLoadResult implements ILoadResult { +public class JavaLoadResult implements ICodeLoader { private static final Logger LOG = LoggerFactory.getLogger(JavaLoadResult.class); private final List readers; @@ -40,10 +39,6 @@ public class JavaLoadResult implements ILoadResult { } } - @Override - public void visitResources(Consumer consumer) { - } - @Override public boolean isEmpty() { return readers.isEmpty(); diff --git a/jadx-plugins/jadx-java-input/src/test/java/jadx/plugins/input/java/CustomLoadTest.java b/jadx-plugins/jadx-java-input/src/test/java/jadx/plugins/input/java/CustomLoadTest.java index 2069cf3c2..c457c538a 100644 --- a/jadx-plugins/jadx-java-input/src/test/java/jadx/plugins/input/java/CustomLoadTest.java +++ b/jadx-plugins/jadx-java-input/src/test/java/jadx/plugins/input/java/CustomLoadTest.java @@ -16,7 +16,7 @@ import org.junit.jupiter.api.Test; import jadx.api.JadxArgs; import jadx.api.JadxDecompiler; -import jadx.api.plugins.input.data.ILoadResult; +import jadx.api.plugins.input.ICodeLoader; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; @@ -40,7 +40,7 @@ class CustomLoadTest { List files = Stream.of("HelloWorld.class", "HelloWorld$HelloInner.class") .map(this::getSample) .collect(Collectors.toList()); - ILoadResult loadResult = JavaInputPlugin.loadClassFiles(files); + ICodeLoader loadResult = JavaInputPlugin.loadClassFiles(files); loadDecompiler(loadResult); assertThat(jadx.getClassesWithInners()) .hasSize(2) @@ -52,7 +52,7 @@ class CustomLoadTest { void loadFromInputStream() throws IOException { String fileName = "HelloWorld$HelloInner.class"; try (InputStream in = Files.newInputStream(getSample(fileName))) { - ILoadResult loadResult = JavaInputPlugin.loadFromInputStream(in, fileName); + ICodeLoader loadResult = JavaInputPlugin.loadFromInputStream(in, fileName); loadDecompiler(loadResult); assertThat(jadx.getClassesWithInners()) .hasSize(1) @@ -66,7 +66,7 @@ class CustomLoadTest { void loadSingleClass() throws IOException { String fileName = "HelloWorld.class"; byte[] content = Files.readAllBytes(getSample(fileName)); - ILoadResult loadResult = JavaInputPlugin.loadSingleClass(content, fileName); + ICodeLoader loadResult = JavaInputPlugin.loadSingleClass(content, fileName); loadDecompiler(loadResult); assertThat(jadx.getClassesWithInners()) .hasSize(1) @@ -77,7 +77,7 @@ class CustomLoadTest { @Test void load() { - ILoadResult loadResult = JavaInputPlugin.load(loader -> { + ICodeLoader loadResult = JavaInputPlugin.load(loader -> { List inputs = new ArrayList<>(2); try { String hello = "HelloWorld.class"; @@ -110,9 +110,9 @@ class CustomLoadTest { jadx.getClassesWithInners().forEach(cls -> System.out.println(cls.getCode())); } - public void loadDecompiler(ILoadResult load) { + public void loadDecompiler(ICodeLoader codeLoader) { try { - jadx.addCustomLoad(load); + jadx.addCustomCodeLoader(codeLoader); jadx.load(); } catch (Exception e) { fail(e); diff --git a/jadx-plugins/jadx-raung-input/src/main/java/jadx/plugins/input/raung/RaungInputPlugin.java b/jadx-plugins/jadx-raung-input/src/main/java/jadx/plugins/input/raung/RaungInputPlugin.java index c28d5ba96..604168e73 100644 --- a/jadx-plugins/jadx-raung-input/src/main/java/jadx/plugins/input/raung/RaungInputPlugin.java +++ b/jadx-plugins/jadx-raung-input/src/main/java/jadx/plugins/input/raung/RaungInputPlugin.java @@ -3,13 +3,15 @@ package jadx.plugins.input.raung; import java.nio.file.Path; import java.util.List; +import jadx.api.plugins.JadxPlugin; +import jadx.api.plugins.JadxPluginContext; import jadx.api.plugins.JadxPluginInfo; -import jadx.api.plugins.input.JadxInputPlugin; -import jadx.api.plugins.input.data.ILoadResult; -import jadx.api.plugins.input.data.impl.EmptyLoadResult; +import jadx.api.plugins.input.ICodeLoader; +import jadx.api.plugins.input.JadxCodeInput; +import jadx.api.plugins.input.data.impl.EmptyCodeLoader; import jadx.plugins.input.java.JavaInputPlugin; -public class RaungInputPlugin implements JadxInputPlugin { +public class RaungInputPlugin implements JadxPlugin, JadxCodeInput { @Override public JadxPluginInfo getPluginInfo() { @@ -20,10 +22,15 @@ public class RaungInputPlugin implements JadxInputPlugin { } @Override - public ILoadResult loadFiles(List input) { + public void init(JadxPluginContext context) { + context.addCodeInput(this); + } + + @Override + public ICodeLoader loadFiles(List input) { RaungConvert convert = new RaungConvert(); if (!convert.execute(input)) { - return EmptyLoadResult.INSTANCE; + return EmptyCodeLoader.INSTANCE; } return JavaInputPlugin.loadClassFiles(convert.getFiles(), convert); } diff --git a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsPlugin.java b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsPlugin.java index df63d09ac..7da01ecf1 100644 --- a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsPlugin.java +++ b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsPlugin.java @@ -8,12 +8,10 @@ import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.mappingio.tree.MemoryMappingTree; import jadx.api.JadxArgs; -import jadx.api.JadxDecompiler; import jadx.api.args.UserRenamesMappingsMode; import jadx.api.plugins.JadxPlugin; import jadx.api.plugins.JadxPluginContext; import jadx.api.plugins.JadxPluginInfo; -import jadx.api.plugins.pass.JadxPassContext; import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.plugins.mappings.load.CodeMappingsVisitor; import jadx.plugins.mappings.load.MappingsVisitor; @@ -27,12 +25,10 @@ public class RenameMappingsPlugin implements JadxPlugin { @Override public void init(JadxPluginContext context) { - JadxArgs args = ((JadxDecompiler) context.getDecompiler()).getArgs(); - MappingTree mappingTree = openMapping(args); + MappingTree mappingTree = openMapping(context.getArgs()); if (mappingTree != null) { - JadxPassContext passContext = context.getPassContext(); - passContext.addPass(new MappingsVisitor(mappingTree)); - passContext.addPass(new CodeMappingsVisitor(mappingTree)); + context.addPass(new MappingsVisitor(mappingTree)); + context.addPass(new CodeMappingsVisitor(mappingTree)); } } diff --git a/jadx-plugins/jadx-script/jadx-script-plugin/src/main/kotlin/jadx/plugins/script/JadxScriptPlugin.kt b/jadx-plugins/jadx-script/jadx-script-plugin/src/main/kotlin/jadx/plugins/script/JadxScriptPlugin.kt index 2b8a2166d..2775edd70 100644 --- a/jadx-plugins/jadx-script/jadx-script-plugin/src/main/kotlin/jadx/plugins/script/JadxScriptPlugin.kt +++ b/jadx-plugins/jadx-script/jadx-script-plugin/src/main/kotlin/jadx/plugins/script/JadxScriptPlugin.kt @@ -1,26 +1,20 @@ package jadx.plugins.script +import jadx.api.plugins.JadxPlugin import jadx.api.plugins.JadxPluginContext import jadx.api.plugins.JadxPluginInfo -import jadx.api.plugins.options.JadxPluginOptions -import jadx.api.plugins.options.OptionDescription import jadx.plugins.script.passes.JadxScriptAfterLoadPass import jadx.plugins.script.runner.ScriptEval import jadx.plugins.script.runtime.data.JadxScriptAllOptions -class JadxScriptPlugin : JadxPluginOptions { - var scriptOptions: JadxScriptAllOptions = JadxScriptAllOptions(emptyMap()) +class JadxScriptPlugin : JadxPlugin { + private val scriptOptions = JadxScriptAllOptions() override fun getPluginInfo() = JadxPluginInfo("jadx-script", "Jadx Script", "Scripting support for jadx") - override fun setOptions(options: Map) { - scriptOptions = JadxScriptAllOptions(options) - } - override fun init(init: JadxPluginContext) { + init.registerOptions(scriptOptions) val scriptStates = ScriptEval().process(init, scriptOptions) ?: return - init.passContext.addPass(JadxScriptAfterLoadPass(scriptStates)) + init.addPass(JadxScriptAfterLoadPass(scriptStates)) } - - override fun getOptionsDescriptions(): List = scriptOptions.descriptions } diff --git a/jadx-plugins/jadx-script/jadx-script-runtime/src/main/kotlin/jadx/plugins/script/runtime/data/options.kt b/jadx-plugins/jadx-script/jadx-script-runtime/src/main/kotlin/jadx/plugins/script/runtime/data/options.kt index afa88a5ce..e22dfac25 100644 --- a/jadx-plugins/jadx-script/jadx-script-runtime/src/main/kotlin/jadx/plugins/script/runtime/data/options.kt +++ b/jadx-plugins/jadx-script/jadx-script-runtime/src/main/kotlin/jadx/plugins/script/runtime/data/options.kt @@ -1,14 +1,21 @@ package jadx.plugins.script.runtime.data +import jadx.api.plugins.options.JadxPluginOptions import jadx.api.plugins.options.OptionDescription import jadx.api.plugins.options.OptionDescription.OptionType import jadx.api.plugins.options.impl.JadxOptionDescription import jadx.plugins.script.runtime.JadxScriptInstance -data class JadxScriptAllOptions( - val values: Map, +class JadxScriptAllOptions : JadxPluginOptions { + lateinit var values: Map val descriptions: MutableList = mutableListOf() -) + + override fun setOptions(options: Map) { + values = options + } + + override fun getOptionsDescriptions(): MutableList = descriptions +} class ScriptOption( val name: String, diff --git a/jadx-plugins/jadx-script/jadx-script-runtime/src/main/kotlin/jadx/plugins/script/runtime/runtime.kt b/jadx-plugins/jadx-script/jadx-script-runtime/src/main/kotlin/jadx/plugins/script/runtime/runtime.kt index 4d9fab1b4..241adbc75 100644 --- a/jadx-plugins/jadx-script/jadx-script-runtime/src/main/kotlin/jadx/plugins/script/runtime/runtime.kt +++ b/jadx-plugins/jadx-script/jadx-script-runtime/src/main/kotlin/jadx/plugins/script/runtime/runtime.kt @@ -69,7 +69,7 @@ class JadxScriptInstance( } fun addPass(pass: JadxPass) { - scriptData.pluginContext.passContext.addPass(pass) + scriptData.pluginContext.addPass(pass) } val internalDecompiler: JadxDecompiler diff --git a/jadx-plugins/jadx-smali-input/src/main/java/jadx/plugins/input/smali/SmaliInputPlugin.java b/jadx-plugins/jadx-smali-input/src/main/java/jadx/plugins/input/smali/SmaliInputPlugin.java index 502dddfec..e1d219107 100644 --- a/jadx-plugins/jadx-smali-input/src/main/java/jadx/plugins/input/smali/SmaliInputPlugin.java +++ b/jadx-plugins/jadx-smali-input/src/main/java/jadx/plugins/input/smali/SmaliInputPlugin.java @@ -3,13 +3,15 @@ package jadx.plugins.input.smali; import java.nio.file.Path; import java.util.List; +import jadx.api.plugins.JadxPlugin; +import jadx.api.plugins.JadxPluginContext; import jadx.api.plugins.JadxPluginInfo; -import jadx.api.plugins.input.JadxInputPlugin; -import jadx.api.plugins.input.data.ILoadResult; -import jadx.api.plugins.input.data.impl.EmptyLoadResult; +import jadx.api.plugins.input.ICodeLoader; +import jadx.api.plugins.input.JadxCodeInput; +import jadx.api.plugins.input.data.impl.EmptyCodeLoader; import jadx.plugins.input.dex.DexInputPlugin; -public class SmaliInputPlugin implements JadxInputPlugin { +public class SmaliInputPlugin implements JadxPlugin, JadxCodeInput { private final DexInputPlugin dexInput = new DexInputPlugin(); @@ -19,10 +21,15 @@ public class SmaliInputPlugin implements JadxInputPlugin { } @Override - public ILoadResult loadFiles(List input) { + public void init(JadxPluginContext context) { + context.addCodeInput(this); + } + + @Override + public ICodeLoader loadFiles(List input) { SmaliConvert convert = new SmaliConvert(); if (!convert.execute(input)) { - return EmptyLoadResult.INSTANCE; + return EmptyCodeLoader.INSTANCE; } return dexInput.loadFiles(convert.getDexFiles(), convert); }