refactor: make input plugin api similar to pass plugins

This commit is contained in:
Skylot 2022-08-22 17:47:58 +01:00
parent 0c4d46ead5
commit a89dbc1152
No known key found for this signature in database
GPG Key ID: 1E23F5B52567AA39
38 changed files with 370 additions and 379 deletions

View File

@ -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<T> {
}
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<T> {
int k = 1;
// load and init all options plugins to print all options
try (JadxDecompiler decompiler = new JadxDecompiler(argsObj.toJadxArgs())) {
Map<String, String> 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<JadxPlugin, JadxPluginOptions> entry : context.getOptionsMap().entrySet()) {
if (appendPlugin(entry.getKey(), entry.getValue(), sb, maxNamesLen, k)) {
k++;
}
}
}
@ -195,8 +190,8 @@ public class JCommanderWrapper<T> {
return "\nPlugin options (-P<name>=<value>):" + sb;
}
private boolean appendPlugin(JadxPluginOptions plugin, StringBuilder out, int maxNamesLen, int k) {
List<OptionDescription> descs = plugin.getOptionsDescriptions();
private boolean appendPlugin(JadxPlugin plugin, JadxPluginOptions options, StringBuilder out, int maxNamesLen, int k) {
List<OptionDescription> descs = options.getOptionsDescriptions();
if (descs.isEmpty()) {
return false;
}

View File

@ -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<Path> 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<ILoadResult> loadedInputs = new ArrayList<>();
for (JadxInputPlugin inputPlugin : pluginManager.getInputPlugins()) {
pluginManager.initResolved(pluginsContext);
List<ICodeLoader> loadedInputs = new ArrayList<>();
for (JadxCodeInput inputPlugin : pluginsContext.getCodeInputs()) {
loadedInputs.add(inputPlugin.loadFiles(inputPaths));
}

View File

@ -25,7 +25,7 @@ public class JadxArgsValidator {
private static void checkInputFiles(JadxDecompiler jadx, JadxArgs args) {
List<File> 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) {

View File

@ -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<ILoadResult> loadedInputs = new ArrayList<>();
private final List<ICodeLoader> loadedInputs = new ArrayList<>();
private RootNode root;
private List<JavaClass> classes;
@ -99,9 +97,9 @@ public final class JadxDecompiler implements Closeable {
private final IDecompileScheduler decompileScheduler = new DecompilerScheduler();
private final List<ILoadResult> customLoads = new ArrayList<>();
private final PluginsContext pluginsContext = new PluginsContext(this);
private final List<ICodeLoader> customCodeLoaders = new ArrayList<>();
private final Map<JadxPassType, List<JadxPass>> customPasses = new HashMap<>();
private @Nullable JadxGuiContext guiContext;
public JadxDecompiler() {
this(new JadxArgs());
@ -135,13 +133,13 @@ public final class JadxDecompiler implements Closeable {
List<Path> inputPaths = Utils.collectionMap(args.getInputFiles(), File::toPath);
List<Path> 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<String, String> 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<JadxPlugin> 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<String> 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<ILoadResult> getCustomLoads() {
return customLoads;
public List<ICodeLoader> 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

View File

@ -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<JadxCodeInput> codeInputs = new ArrayList<>();
private final Map<JadxPlugin, JadxPluginOptions> 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<JadxCodeInput> 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<JadxPlugin, JadxPluginOptions> getOptionsMap() {
return optionsMap;
}
@Override
public @Nullable JadxGuiContext getGuiContext() {
return guiContext;
}
public void setGuiContext(JadxGuiContext guiContext) {
this.guiContext = guiContext;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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();

View File

@ -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<OptionDescription> 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<String> 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<JadxInputPlugin> getInputPlugins() {
return getPluginsWithType(JadxInputPlugin.class);
}
public List<JadxPluginOptions> getPluginsWithOptions() {
return getPluginsWithType(JadxPluginOptions.class);
}
@SuppressWarnings("unchecked")
public <T extends JadxPlugin> List<T> getPluginsWithType(Class<T> type) {
return resolvedPlugins.stream()
.filter(p -> type.isAssignableFrom(p.getClass()))
.map(p -> (T) p)
.collect(Collectors.toList());
}
private synchronized void resolve() {
Map<String, List<PluginData>> 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<JadxPlugin> 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<JadxPlugin, JadxPluginOptions> entry : context.getOptionsMap().entrySet()) {
verifyOptions(entry.getKey(), entry.getValue());
}
}
private void verifyOptions(JadxPlugin plugin, JadxPluginOptions options) {
String pluginId = plugin.getPluginInfo().getPluginId();
List<OptionDescription> 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<String> 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<PluginData> {
private final JadxPlugin plugin;
private final JadxPluginInfo info;

View File

@ -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<IClassData> consumer);
boolean isEmpty();
}

View File

@ -0,0 +1,8 @@
package jadx.api.plugins.input;
import java.nio.file.Path;
import java.util.List;
public interface JadxCodeInput {
ICodeLoader loadFiles(List<Path> input);
}

View File

@ -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<Path> input);
}

View File

@ -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<IClassData> consumer);
void visitResources(Consumer<IResourceData> consumer);
boolean isEmpty();
}

View File

@ -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<IClassData> consumer) {
}
@Override
public void visitResources(Consumer<IResourceData> consumer) {
}
@Override
public void close() throws IOException {
}

View File

@ -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<String, String> options);

View File

@ -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<String, String> options, String key, boolean defValue) {
public abstract class BaseOptionsParser implements JadxPluginOptions {
protected Map<String, String> options;
@Override
public void setOptions(Map<String, String> 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> T getOption(Map<String, String> options, String key, Function<String, T> parse, T defValue) {
public <T> T getOption(String key, Function<String, T> parse, T defValue) {
String val = options.get(key);
if (val == null) {
return defValue;

View File

@ -1,6 +0,0 @@
package jadx.api.plugins.pass;
public interface JadxPassContext {
void addPass(JadxPass pass);
}

View File

@ -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<ILoadResult> loadedInputs) {
for (ILoadResult loadedInput : loadedInputs) {
loadedInput.visitClasses(cls -> {
public void loadClasses(List<ICodeLoader> loadedInputs) {
for (ICodeLoader codeLoader : loadedInputs) {
codeLoader.visitClasses(cls -> {
try {
addClassNode(new ClassNode(RootNode.this, cls));
} catch (Exception e) {

View File

@ -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());

View File

@ -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<JadxPlugin> 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();
}
/**

View File

@ -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;
}

View File

@ -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<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));

View File

@ -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<String, String> options) {
verifyChecksum = getBooleanOption(options, VERIFY_CHECKSUM_OPT, true);
@Override
public void parseOptions() {
verifyChecksum = getBooleanOption(VERIFY_CHECKSUM_OPT, true);
}
public List<OptionDescription> buildOptionsDescriptions() {
public List<OptionDescription> getOptionsDescriptions() {
return Collections.singletonList(
new JadxOptionDescription(
VERIFY_CHECKSUM_OPT,

View File

@ -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<Path> input) {
public void init(JadxPluginContext context) {
context.registerOptions(options);
context.addCodeInput(this::loadFiles);
}
public ICodeLoader loadFiles(List<Path> input) {
return loadFiles(input, null);
}
public ILoadResult loadFiles(List<Path> inputFiles, @Nullable Closeable closeable) {
public ICodeLoader loadFiles(List<Path> inputFiles, @Nullable Closeable closeable) {
List<DexReader> 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<String, String> options) {
this.options.apply(options);
}
@Override
public List<OptionDescription> getOptionsDescriptions() {
return this.options.buildOptionsDescriptions();
}
}

View File

@ -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<DexReader> dexReaders;
@Nullable
private final Closeable closeable;
@ -28,10 +27,6 @@ public class DexLoadResult implements ILoadResult {
}
}
@Override
public void visitResources(Consumer<IResourceData> consumer) {
}
@Override
public void close() throws IOException {
if (closeable != null) {

View File

@ -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<Path> 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();

View File

@ -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<String, String> 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<OptionDescription> buildOptionsDescriptions() {
@Override
public List<OptionDescription> getOptionsDescriptions() {
return Arrays.asList(
new JadxOptionDescription(
MODE_OPT,

View File

@ -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<Path> input) {
public void init(JadxPluginContext context) {
context.registerOptions(options);
context.addCodeInput(this);
}
@Override
public ICodeLoader loadFiles(List<Path> 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<String, String> options) {
this.options.apply(options);
}
@Override
public List<OptionDescription> getOptionsDescriptions() {
return this.options.buildOptionsDescriptions();
}
}

View File

@ -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<Path> inputFiles) {
public void init(JadxPluginContext context) {
context.addCodeInput(JavaInputPlugin::loadClassFiles);
}
public static ICodeLoader loadClassFiles(List<Path> inputFiles) {
return loadClassFiles(inputFiles, null);
}
public static ILoadResult loadClassFiles(List<Path> inputFiles, @Nullable Closeable closeable) {
public static ICodeLoader loadClassFiles(List<Path> inputFiles, @Nullable Closeable closeable) {
List<JavaClassReader> readers = new JavaInputLoader().collectFiles(inputFiles);
if (readers.isEmpty()) {
return EmptyLoadResult.INSTANCE;
return EmptyCodeLoader.INSTANCE;
}
return new JavaLoadResult(readers, closeable);
}
public static ILoadResult loadClassFiles(List<Path> inputFiles) {
return loadClassFiles(inputFiles, null);
}
/**
* Method for provide several inputs by using load methods from {@link JavaInputLoader} class.
*/
public static ILoadResult load(Function<JavaInputLoader, List<JavaClassReader>> loader) {
public static ICodeLoader load(Function<JavaInputLoader, List<JavaClassReader>> 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<JavaClassReader> readers) {
public static ICodeLoader wrapClassReaders(List<JavaClassReader> readers) {
if (readers.isEmpty()) {
return EmptyLoadResult.INSTANCE;
return EmptyCodeLoader.INSTANCE;
}
return new JavaLoadResult(readers);
}

View File

@ -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<JavaClassReader> readers;
@ -40,10 +39,6 @@ public class JavaLoadResult implements ILoadResult {
}
}
@Override
public void visitResources(Consumer<IResourceData> consumer) {
}
@Override
public boolean isEmpty() {
return readers.isEmpty();

View File

@ -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<Path> 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<JavaClassReader> 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);

View File

@ -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<Path> input) {
public void init(JadxPluginContext context) {
context.addCodeInput(this);
}
@Override
public ICodeLoader loadFiles(List<Path> input) {
RaungConvert convert = new RaungConvert();
if (!convert.execute(input)) {
return EmptyLoadResult.INSTANCE;
return EmptyCodeLoader.INSTANCE;
}
return JavaInputPlugin.loadClassFiles(convert.getFiles(), convert);
}

View File

@ -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));
}
}

View File

@ -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<String, String>) {
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<OptionDescription> = scriptOptions.descriptions
}

View File

@ -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<String, String>,
class JadxScriptAllOptions : JadxPluginOptions {
lateinit var values: Map<String, String>
val descriptions: MutableList<OptionDescription> = mutableListOf()
)
override fun setOptions(options: Map<String, String>) {
values = options
}
override fun getOptionsDescriptions(): MutableList<OptionDescription> = descriptions
}
class ScriptOption<T>(
val name: String,

View File

@ -69,7 +69,7 @@ class JadxScriptInstance(
}
fun addPass(pass: JadxPass) {
scriptData.pluginContext.passContext.addPass(pass)
scriptData.pluginContext.addPass(pass)
}
val internalDecompiler: JadxDecompiler

View File

@ -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<Path> input) {
public void init(JadxPluginContext context) {
context.addCodeInput(this);
}
@Override
public ICodeLoader loadFiles(List<Path> input) {
SmaliConvert convert = new SmaliConvert();
if (!convert.execute(input)) {
return EmptyLoadResult.INSTANCE;
return EmptyCodeLoader.INSTANCE;
}
return dexInput.loadFiles(convert.getDexFiles(), convert);
}