Skip to content

Commit

Permalink
feat(plugins): add API for search/use other plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Jan 15, 2024
1 parent fdc3fe1 commit 8ed4818
Show file tree
Hide file tree
Showing 14 changed files with 248 additions and 61 deletions.
15 changes: 11 additions & 4 deletions jadx-core/src/main/java/jadx/api/JadxDecompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import jadx.core.dex.visitors.SaveCode;
import jadx.core.export.ExportGradleTask;
import jadx.core.plugins.JadxPluginManager;
import jadx.core.plugins.PluginContext;
import jadx.core.plugins.events.JadxEventsImpl;
import jadx.core.utils.DecompilerScheduler;
import jadx.core.utils.Utils;
Expand Down Expand Up @@ -147,10 +148,16 @@ private void loadInputFiles() {
List<Path> inputPaths = Utils.collectionMap(args.getInputFiles(), File::toPath);
List<Path> inputFiles = FileUtils.expandDirs(inputPaths);
long start = System.currentTimeMillis();
for (JadxCodeInput codeLoader : pluginManager.getCodeInputs()) {
ICodeLoader loader = codeLoader.loadFiles(inputFiles);
if (loader != null && !loader.isEmpty()) {
loadedInputs.add(loader);
for (PluginContext plugin : pluginManager.getResolvedPluginContexts()) {
for (JadxCodeInput codeLoader : plugin.getCodeInputs()) {
try {
ICodeLoader loader = codeLoader.loadFiles(inputFiles);
if (loader != null && !loader.isEmpty()) {
loadedInputs.add(loader);
}
} catch (Exception e) {
throw new JadxRuntimeException("Failed to load code for plugin: " + plugin, e);
}
}
}
loadedInputs.addAll(customCodeLoaders);
Expand Down
13 changes: 11 additions & 2 deletions jadx-core/src/main/java/jadx/api/plugins/JadxPluginContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.plugins.data.IJadxPlugins;
import jadx.api.plugins.events.IJadxEvents;
import jadx.api.plugins.gui.JadxGuiContext;
import jadx.api.plugins.input.JadxCodeInput;
Expand All @@ -31,11 +32,19 @@ public interface JadxPluginContext {
*/
void registerInputsHashSupplier(Supplier<String> supplier);

/**
* Access to jadx-gui specific methods
*/
@Nullable
JadxGuiContext getGuiContext();

/**
* Subscribe and send events
*/
IJadxEvents events();

@Nullable
JadxGuiContext getGuiContext();
/**
* Access to registered plugins and runtime data
*/
IJadxPlugins plugins();
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ public class JadxPluginInfoBuilder {
private String homepage = "";
private @Nullable String provides;

public JadxPluginInfoBuilder() {
/**
* Start building method
*/
public static JadxPluginInfoBuilder pluginId(String pluginId) {
JadxPluginInfoBuilder builder = new JadxPluginInfoBuilder();
builder.pluginId = Objects.requireNonNull(pluginId);
return builder;
}

public JadxPluginInfoBuilder pluginId(String pluginId) {
this.pluginId = Objects.requireNonNull(pluginId);
return this;
private JadxPluginInfoBuilder() {
}

public JadxPluginInfoBuilder name(String name) {
Expand Down
12 changes: 12 additions & 0 deletions jadx-core/src/main/java/jadx/api/plugins/data/IJadxPlugins.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package jadx.api.plugins.data;

import jadx.api.plugins.JadxPlugin;

public interface IJadxPlugins {

JadxPluginRuntimeData getById(String pluginId);

JadxPluginRuntimeData getProviding(String provideId);

<P extends JadxPlugin> P getInstance(Class<P> pluginCls);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package jadx.api.plugins.data;

import java.io.Closeable;
import java.nio.file.Path;
import java.util.List;

import org.jetbrains.annotations.Nullable;

import jadx.api.plugins.JadxPlugin;
import jadx.api.plugins.JadxPluginInfo;
import jadx.api.plugins.input.ICodeLoader;
import jadx.api.plugins.input.JadxCodeInput;
import jadx.api.plugins.options.JadxPluginOptions;

/**
* Runtime plugin data.
*/
public interface JadxPluginRuntimeData {
boolean isInitialized();

String getPluginId();

JadxPlugin getPluginInstance();

JadxPluginInfo getPluginInfo();

List<JadxCodeInput> getCodeInputs();

@Nullable
JadxPluginOptions getOptions();

String getInputsHash();

/**
* Convenient method to simplify code loading from custom files.
*/
ICodeLoader loadCodeFiles(List<Path> files, @Nullable Closeable closeable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class JadxPluginManager {
private static final Logger LOG = LoggerFactory.getLogger(JadxPluginManager.class);

private final JadxDecompiler decompiler;
private final JadxPluginsData pluginsData;
private final SortedSet<PluginContext> allPlugins = new TreeSet<>();
private final SortedSet<PluginContext> resolvedPlugins = new TreeSet<>();
private final Map<String, String> provideSuggestions = new TreeMap<>();
Expand All @@ -33,6 +34,7 @@ public class JadxPluginManager {

public JadxPluginManager(JadxDecompiler decompiler) {
this.decompiler = decompiler;
this.pluginsData = new JadxPluginsData(decompiler, this);
}

/**
Expand All @@ -58,7 +60,7 @@ public void register(JadxPlugin plugin) {
}

private PluginContext addPlugin(JadxPlugin plugin) {
PluginContext pluginContext = new PluginContext(decompiler, plugin);
PluginContext pluginContext = new PluginContext(decompiler, pluginsData, plugin);
LOG.debug("Loading plugin: {}", pluginContext);
if (!allPlugins.add(pluginContext)) {
throw new IllegalArgumentException("Duplicate plugin id: " + pluginContext + ", class " + plugin.getClass());
Expand Down
47 changes: 47 additions & 0 deletions jadx-core/src/main/java/jadx/core/plugins/JadxPluginsData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package jadx.core.plugins;

import jadx.api.JadxDecompiler;
import jadx.api.plugins.JadxPlugin;
import jadx.api.plugins.data.IJadxPlugins;
import jadx.api.plugins.data.JadxPluginRuntimeData;
import jadx.core.utils.exceptions.JadxRuntimeException;

public class JadxPluginsData implements IJadxPlugins {

private final JadxDecompiler decompiler;
private final JadxPluginManager pluginManager;

public JadxPluginsData(JadxDecompiler decompiler, JadxPluginManager pluginManager) {
this.decompiler = decompiler;
this.pluginManager = pluginManager;
}

@Override
public JadxPluginRuntimeData getById(String pluginId) {
return pluginManager.getResolvedPluginContexts()
.stream()
.filter(p -> p.getPluginId().equals(pluginId))
.findFirst()
.orElseThrow(() -> new JadxRuntimeException("Plugin with id '" + pluginId + "' not found"));
}

@Override
public JadxPluginRuntimeData getProviding(String provideId) {
return pluginManager.getResolvedPluginContexts()
.stream()
.filter(p -> p.getPluginInfo().getProvides().equals(provideId))
.findFirst()
.orElseThrow(() -> new JadxRuntimeException("Plugin providing '" + provideId + "' not found"));
}

@SuppressWarnings("unchecked")
@Override
public <P extends JadxPlugin> P getInstance(Class<P> pluginCls) {
return pluginManager.getResolvedPluginContexts()
.stream()
.filter(p -> p.getPluginInstance().getClass().equals(pluginCls))
.map(p -> ((P) p.getPluginInstance()))
.findFirst()
.orElseThrow(() -> new JadxRuntimeException("Plugin class '" + pluginCls + "' not found"));
}
}
36 changes: 32 additions & 4 deletions jadx-core/src/main/java/jadx/core/plugins/PluginContext.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package jadx.core.plugins;

import java.io.Closeable;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand All @@ -13,18 +15,24 @@
import jadx.api.plugins.JadxPlugin;
import jadx.api.plugins.JadxPluginContext;
import jadx.api.plugins.JadxPluginInfo;
import jadx.api.plugins.data.IJadxPlugins;
import jadx.api.plugins.data.JadxPluginRuntimeData;
import jadx.api.plugins.events.IJadxEvents;
import jadx.api.plugins.gui.JadxGuiContext;
import jadx.api.plugins.input.ICodeLoader;
import jadx.api.plugins.input.JadxCodeInput;
import jadx.api.plugins.input.data.impl.MergeCodeLoader;
import jadx.api.plugins.options.JadxPluginOptions;
import jadx.api.plugins.options.OptionDescription;
import jadx.api.plugins.options.OptionFlag;
import jadx.api.plugins.pass.JadxPass;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;

public class PluginContext implements JadxPluginContext, Comparable<PluginContext> {
public class PluginContext implements JadxPluginContext, JadxPluginRuntimeData, Comparable<PluginContext> {
private final JadxDecompiler decompiler;
private final JadxPluginsData pluginsData;
private final JadxPlugin plugin;
private final JadxPluginInfo pluginInfo;
private @Nullable JadxGuiContext guiContext;
Expand All @@ -35,8 +43,9 @@ public class PluginContext implements JadxPluginContext, Comparable<PluginContex

private boolean initialized;

PluginContext(JadxDecompiler decompiler, JadxPlugin plugin) {
PluginContext(JadxDecompiler decompiler, JadxPluginsData pluginsData, JadxPlugin plugin) {
this.decompiler = decompiler;
this.pluginsData = pluginsData;
this.plugin = plugin;
this.pluginInfo = plugin.getPluginInfo();
}
Expand All @@ -46,6 +55,7 @@ void init() {
initialized = true;
}

@Override
public boolean isInitialized() {
return initialized;
}
Expand All @@ -70,6 +80,7 @@ public void addCodeInput(JadxCodeInput codeInput) {
this.codeInputs.add(codeInput);
}

@Override
public List<JadxCodeInput> getCodeInputs() {
return codeInputs;
}
Expand All @@ -89,6 +100,7 @@ public void registerInputsHashSupplier(Supplier<String> supplier) {
this.inputsHashSupplier = supplier;
}

@Override
public String getInputsHash() {
if (inputsHashSupplier == null) {
return defaultOptionsHash();
Expand Down Expand Up @@ -128,22 +140,38 @@ public void setGuiContext(JadxGuiContext guiContext) {
this.guiContext = guiContext;
}

public JadxPlugin getPlugin() {
@Override
public JadxPlugin getPluginInstance() {
return plugin;
}

@Override
public JadxPluginInfo getPluginInfo() {
return pluginInfo;
}

@Override
public String getPluginId() {
return pluginInfo.getPluginId();
}

public JadxPluginOptions getOptions() {
@Override
public @Nullable JadxPluginOptions getOptions() {
return options;
}

@Override
public IJadxPlugins plugins() {
return pluginsData;
}

@Override
public ICodeLoader loadCodeFiles(List<Path> files, @Nullable Closeable closeable) {
return new MergeCodeLoader(
Utils.collectionMap(codeInputs, codeInput -> codeInput.loadFiles(files)),
closeable);
}

@Override
public boolean equals(Object other) {
if (this == other) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package jadx.api.plugins.input.data.impl;

import java.io.Closeable;
import java.io.IOException;
import java.util.List;
import java.util.function.Consumer;

import org.jetbrains.annotations.Nullable;

import jadx.api.plugins.input.ICodeLoader;
import jadx.api.plugins.input.data.IClassData;

public class MergeCodeLoader implements ICodeLoader {

private final List<ICodeLoader> codeLoaders;
private final @Nullable Closeable closeable;

public MergeCodeLoader(List<ICodeLoader> codeLoaders) {
this(codeLoaders, null);
}

public MergeCodeLoader(List<ICodeLoader> codeLoaders, @Nullable Closeable closeable) {
this.codeLoaders = codeLoaders;
this.closeable = closeable;
}

@Override
public void visitClasses(Consumer<IClassData> consumer) {
for (ICodeLoader codeLoader : codeLoaders) {
codeLoader.visitClasses(consumer);
}
}

@Override
public boolean isEmpty() {
for (ICodeLoader codeLoader : codeLoaders) {
if (!codeLoader.isEmpty()) {
return false;
}
}
return true;
}

@Override
public void close() throws IOException {
for (ICodeLoader codeLoader : codeLoaders) {
codeLoader.close();
}
if (closeable != null) {
closeable.close();
}
}
}
Loading

0 comments on commit 8ed4818

Please sign in to comment.