fix: improve plugins data handling

This commit is contained in:
Skylot
2023-04-01 21:06:05 +01:00
parent a992c93198
commit 7a309ca367
35 changed files with 786 additions and 562 deletions
@@ -2,7 +2,6 @@ package jadx.api;
import java.io.Closeable;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
@@ -636,8 +635,8 @@ public class JadxArgs implements Closeable {
+ insertDebugLines + extractFinally
+ debugInfo + useSourceNameAsClassAlias + escapeUnicode + replaceConsts
+ respectBytecodeAccModifiers + fsCaseSensitive + renameFlags
+ commentsLevel + useDxInput + pluginOptions;
return FileUtils.md5Sum(argStr.getBytes(StandardCharsets.US_ASCII));
+ commentsLevel + useDxInput;
return FileUtils.md5Sum(argStr);
}
@Override
@@ -23,14 +23,12 @@ import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.impl.plugins.PluginsContext;
import jadx.api.metadata.ICodeAnnotation;
import jadx.api.metadata.ICodeNodeRef;
import jadx.api.metadata.annotations.NodeDeclareRef;
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.input.ICodeLoader;
import jadx.api.plugins.input.JadxCodeInput;
import jadx.api.plugins.pass.JadxPass;
@@ -45,6 +43,7 @@ import jadx.core.dex.nodes.PackageNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.SaveCode;
import jadx.core.export.ExportGradleProject;
import jadx.core.plugins.JadxPluginManager;
import jadx.core.utils.DecompilerScheduler;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
@@ -85,7 +84,7 @@ public final class JadxDecompiler implements Closeable {
private static final Logger LOG = LoggerFactory.getLogger(JadxDecompiler.class);
private final JadxArgs args;
private final JadxPluginManager pluginManager = new JadxPluginManager();
private final JadxPluginManager pluginManager = new JadxPluginManager(this);
private final List<ICodeLoader> loadedInputs = new ArrayList<>();
private RootNode root;
@@ -97,7 +96,6 @@ public final class JadxDecompiler implements Closeable {
private final IDecompileScheduler decompileScheduler = new DecompilerScheduler();
private final PluginsContext pluginsContext = new PluginsContext(this);
private final List<ICodeLoader> customCodeLoaders = new ArrayList<>();
private final Map<JadxPassType, List<JadxPass>> customPasses = new HashMap<>();
@@ -144,7 +142,7 @@ 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 (JadxCodeInput codeLoader : pluginsContext.getCodeInputs()) {
for (JadxCodeInput codeLoader : pluginManager.getCodeInputs()) {
ICodeLoader loader = codeLoader.loadFiles(inputFiles);
if (loader != null && !loader.isEmpty()) {
loadedInputs.add(loader);
@@ -186,10 +184,9 @@ public final class JadxDecompiler implements Closeable {
pluginManager.providesSuggestion("java-input", args.isUseDxInput() ? "java-convert" : "java-input");
pluginManager.load();
if (LOG.isDebugEnabled()) {
LOG.debug("Resolved plugins: {}", Utils.collectionMap(pluginManager.getResolvedPlugins(),
p -> p.getPluginInfo().getPluginId()));
LOG.debug("Resolved plugins: {}", pluginManager.getResolvedPluginContexts());
}
pluginManager.initResolved(pluginsContext);
pluginManager.initResolved();
if (LOG.isDebugEnabled()) {
List<String> passes = customPasses.values().stream().flatMap(Collection::stream)
.map(p -> p.getInfo().getName()).collect(Collectors.toList());
@@ -673,10 +670,6 @@ public final class JadxDecompiler implements Closeable {
customPasses.computeIfAbsent(pass.getPassType(), l -> new ArrayList<>()).add(pass);
}
public PluginsContext getPluginsContext() {
return pluginsContext;
}
@Override
public String toString() {
return "jadx decompiler " + getVersion();
@@ -1,86 +0,0 @@
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(@Nullable 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;
}
}
@@ -1,5 +1,7 @@
package jadx.api.plugins;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import jadx.api.JadxArgs;
@@ -21,6 +23,13 @@ public interface JadxPluginContext {
void registerOptions(JadxPluginOptions options);
/**
* Function to calculate hash of all options which can change output code.
* Hash for input files ({@link JadxArgs#getInputFiles()}) already calculated,
* so this method can omit these files.
*/
void registerInputsHashSupplier(Supplier<String> supplier);
@Nullable
JadxGuiContext getGuiContext();
}
@@ -1,209 +0,0 @@
package jadx.api.plugins;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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);
private final Set<PluginData> allPlugins = new TreeSet<>();
private final Map<String, String> provideSuggestions = new TreeMap<>();
private List<JadxPlugin> resolvedPlugins = Collections.emptyList();
public JadxPluginManager() {
}
/**
* Add suggestion how to resolve conflicting plugins
*/
public void providesSuggestion(String provides, String pluginId) {
provideSuggestions.put(provides, pluginId);
}
public void load() {
allPlugins.clear();
ServiceLoader<JadxPlugin> jadxPlugins = ServiceLoader.load(JadxPlugin.class);
for (JadxPlugin plugin : jadxPlugins) {
addPlugin(plugin);
}
resolve();
}
public void register(JadxPlugin plugin) {
Objects.requireNonNull(plugin);
PluginData addedPlugin = addPlugin(plugin);
LOG.debug("Register plugin: {}", addedPlugin.getPluginId());
resolve();
}
private PluginData addPlugin(JadxPlugin plugin) {
PluginData pluginData = new PluginData(plugin, plugin.getPluginInfo());
LOG.debug("Loading plugin: {}", pluginData.getPluginId());
if (!allPlugins.add(pluginData)) {
throw new IllegalArgumentException("Duplicate plugin id: " + pluginData + ", class " + plugin.getClass());
}
return pluginData;
}
public boolean unload(String pluginId) {
boolean result = allPlugins.removeIf(pd -> {
String id = pd.getPluginId();
boolean match = id.equals(pluginId);
if (match) {
LOG.debug("Unload plugin: {}", id);
}
return match;
});
resolve();
return result;
}
public List<JadxPlugin> getAllPlugins() {
if (allPlugins.isEmpty()) {
load();
}
return allPlugins.stream().map(PluginData::getPlugin).collect(Collectors.toList());
}
public List<JadxPlugin> getResolvedPlugins() {
return Collections.unmodifiableList(resolvedPlugins);
}
private synchronized void resolve() {
Map<String, List<PluginData>> provides = allPlugins.stream()
.collect(Collectors.groupingBy(p -> p.getInfo().getProvides()));
List<PluginData> result = new ArrayList<>(provides.size());
provides.forEach((provide, list) -> {
if (list.size() == 1) {
result.add(list.get(0));
} else {
String suggestion = provideSuggestions.get(provide);
if (suggestion != null) {
list.stream().filter(p -> p.getPluginId().equals(suggestion))
.findFirst()
.ifPresent(result::add);
} else {
PluginData selected = list.get(0);
result.add(selected);
LOG.debug("Select providing '{}' plugin '{}', candidates: {}", provide, selected, list);
}
}
});
Collections.sort(result);
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);
}
public void init(PluginsContext context, List<JadxPlugin> plugins) {
for (JadxPlugin plugin : plugins) {
try {
context.setCurrentPlugin(plugin);
plugin.init(context);
context.setCurrentPlugin(null);
} catch (Exception e) {
String pluginId = plugin.getPluginInfo().getPluginId();
throw new JadxRuntimeException("Failed to init plugin: " + pluginId, e);
}
}
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;
private PluginData(JadxPlugin plugin, JadxPluginInfo info) {
this.plugin = plugin;
this.info = info;
}
public JadxPlugin getPlugin() {
return plugin;
}
public JadxPluginInfo getInfo() {
return info;
}
public String getPluginId() {
return info.getPluginId();
}
@Override
public int compareTo(@NotNull JadxPluginManager.PluginData o) {
return this.info.getPluginId().compareTo(o.info.getPluginId());
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof PluginData)) {
return false;
}
PluginData that = (PluginData) o;
return getInfo().getPluginId().equals(that.getInfo().getPluginId());
}
@Override
public int hashCode() {
return info.getPluginId().hashCode();
}
@Override
public String toString() {
return info.getPluginId();
}
}
}
@@ -1,6 +1,8 @@
package jadx.api.plugins.options;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
@@ -30,4 +32,13 @@ public interface OptionDescription {
default OptionType getType() {
return OptionType.STRING;
}
enum OptionFlag {
PER_PROJECT, // store in project settings instead global (for jadx-gui)
HIDE_IN_GUI, // do not show this option in jadx-gui (useful if option is configured with custom ui)
}
default Set<OptionFlag> getFlags() {
return Collections.emptySet();
}
}
@@ -1,6 +1,10 @@
package jadx.api.plugins.options.impl;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
@@ -8,11 +12,19 @@ import jadx.api.plugins.options.OptionDescription;
public class JadxOptionDescription implements OptionDescription {
public static JadxOptionDescription booleanOption(String name, String desc, boolean defaultValue) {
return new JadxOptionDescription(name, desc,
defaultValue ? "yes" : "no",
Arrays.asList("yes", "no"),
OptionType.BOOLEAN);
}
private final String name;
private final String desc;
private final String defaultValue;
private final List<String> values;
private final OptionType type;
private final Set<OptionFlag> flags = EnumSet.noneOf(OptionFlag.class);
public JadxOptionDescription(String name, String desc, @Nullable String defaultValue, List<String> values) {
this(name, desc, defaultValue, values, OptionType.STRING);
@@ -51,6 +63,21 @@ public class JadxOptionDescription implements OptionDescription {
return type;
}
@Override
public Set<OptionFlag> getFlags() {
return flags;
}
public JadxOptionDescription withFlag(OptionFlag flag) {
this.flags.add(flag);
return this;
}
public JadxOptionDescription withFlags(OptionFlag... flags) {
Collections.addAll(this.flags, flags);
return this;
}
@Override
public String toString() {
return "OptionDescription{" + desc + ", values=" + values + '}';
@@ -4,7 +4,7 @@ import jadx.api.JadxDecompiler;
import jadx.api.plugins.pass.JadxPass;
public interface JadxAfterLoadPass extends JadxPass {
JadxPassType TYPE = new JadxPassType(JadxAfterLoadPass.class);
JadxPassType TYPE = new JadxPassType("AfterLoadPass");
void init(JadxDecompiler decompiler);
@@ -6,7 +6,7 @@ import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
public interface JadxDecompilePass extends JadxPass {
JadxPassType TYPE = new JadxPassType(JadxDecompilePass.class);
JadxPassType TYPE = new JadxPassType("DecompilePass");
void init(RootNode root);
@@ -1,12 +1,10 @@
package jadx.api.plugins.pass.types;
import jadx.api.plugins.pass.JadxPass;
public class JadxPassType {
private final String cls;
public JadxPassType(Class<? extends JadxPass> cls) {
this.cls = cls.getSimpleName();
public JadxPassType(String clsName) {
this.cls = clsName;
}
@Override
@@ -27,6 +25,6 @@ public class JadxPassType {
@Override
public String toString() {
return "JadxPassType{" + cls + '}';
return cls;
}
}
@@ -4,7 +4,7 @@ import jadx.api.plugins.pass.JadxPass;
import jadx.core.dex.nodes.RootNode;
public interface JadxPreparePass extends JadxPass {
JadxPassType TYPE = new JadxPassType(JadxPreparePass.class);
JadxPassType TYPE = new JadxPassType("PreparePass");
void init(RootNode root);
@@ -0,0 +1,176 @@
package jadx.core.plugins;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.JadxDecompiler;
import jadx.api.plugins.JadxPlugin;
import jadx.api.plugins.gui.JadxGuiContext;
import jadx.api.plugins.input.JadxCodeInput;
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);
private final JadxDecompiler decompiler;
private final SortedSet<PluginContext> allPlugins = new TreeSet<>();
private final SortedSet<PluginContext> resolvedPlugins = new TreeSet<>();
private final Map<String, String> provideSuggestions = new TreeMap<>();
private @Nullable JadxGuiContext guiContext;
public JadxPluginManager(JadxDecompiler decompiler) {
this.decompiler = decompiler;
}
/**
* Add suggestion how to resolve conflicting plugins
*/
public void providesSuggestion(String provides, String pluginId) {
provideSuggestions.put(provides, pluginId);
}
public void load() {
allPlugins.clear();
ServiceLoader<JadxPlugin> jadxPlugins = ServiceLoader.load(JadxPlugin.class);
for (JadxPlugin plugin : jadxPlugins) {
addPlugin(plugin);
}
resolve();
}
public void register(JadxPlugin plugin) {
Objects.requireNonNull(plugin);
PluginContext addedPlugin = addPlugin(plugin);
LOG.debug("Register plugin: {}", addedPlugin.getPluginId());
resolve();
}
private PluginContext addPlugin(JadxPlugin plugin) {
PluginContext pluginContext = new PluginContext(decompiler, plugin);
LOG.debug("Loading plugin: {}", pluginContext);
if (!allPlugins.add(pluginContext)) {
throw new IllegalArgumentException("Duplicate plugin id: " + pluginContext + ", class " + plugin.getClass());
}
pluginContext.setGuiContext(guiContext);
return pluginContext;
}
public boolean unload(String pluginId) {
boolean result = allPlugins.removeIf(context -> {
if (context.getPluginId().equals(pluginId)) {
LOG.debug("Unload plugin: {}", pluginId);
return true;
}
return false;
});
resolve();
return result;
}
public SortedSet<PluginContext> getAllPluginContexts() {
return allPlugins;
}
public SortedSet<PluginContext> getResolvedPluginContexts() {
return resolvedPlugins;
}
private synchronized void resolve() {
Map<String, List<PluginContext>> provides = allPlugins.stream()
.collect(Collectors.groupingBy(p -> p.getPluginInfo().getProvides()));
List<PluginContext> resolved = new ArrayList<>(provides.size());
provides.forEach((provide, list) -> {
if (list.size() == 1) {
resolved.add(list.get(0));
} else {
String suggestion = provideSuggestions.get(provide);
if (suggestion != null) {
list.stream().filter(p -> p.getPluginId().equals(suggestion))
.findFirst()
.ifPresent(resolved::add);
} else {
PluginContext selected = list.get(0);
resolved.add(selected);
LOG.debug("Select providing '{}' plugin '{}', candidates: {}", provide, selected, list);
}
}
});
resolvedPlugins.clear();
resolvedPlugins.addAll(resolved);
}
public void initAll() {
init(allPlugins);
}
public void initResolved() {
init(resolvedPlugins);
}
public void init(SortedSet<PluginContext> pluginContexts) {
for (PluginContext context : pluginContexts) {
try {
context.init();
} catch (Exception e) {
throw new JadxRuntimeException("Failed to init plugin: " + context.getPluginId(), e);
}
}
for (PluginContext context : pluginContexts) {
JadxPluginOptions options = context.getOptions();
if (options != null) {
verifyOptions(context, options);
}
}
}
private void verifyOptions(PluginContext pluginContext, JadxPluginOptions options) {
String pluginId = pluginContext.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);
}
});
}
public List<JadxCodeInput> getCodeInputs() {
return getResolvedPluginContexts()
.stream()
.flatMap(p -> p.getCodeInputs().stream())
.collect(Collectors.toList());
}
public void setGuiContext(JadxGuiContext guiContext) {
this.guiContext = guiContext;
for (PluginContext context : getAllPluginContexts()) {
context.setGuiContext(guiContext);
}
}
}
@@ -0,0 +1,148 @@
package jadx.core.plugins;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
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.JadxPluginInfo;
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 PluginContext implements JadxPluginContext, Comparable<PluginContext> {
private final JadxDecompiler decompiler;
private final JadxPlugin plugin;
private final JadxPluginInfo pluginInfo;
private @Nullable JadxGuiContext guiContext;
private final List<JadxCodeInput> codeInputs = new ArrayList<>();
private @Nullable JadxPluginOptions options;
private @Nullable Supplier<String> inputsHashSupplier;
private boolean initialized;
PluginContext(JadxDecompiler decompiler, JadxPlugin plugin) {
this.decompiler = decompiler;
this.plugin = plugin;
this.pluginInfo = plugin.getPluginInfo();
}
void init() {
plugin.init(this);
initialized = true;
}
public boolean isInitialized() {
return initialized;
}
@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) {
this.codeInputs.add(codeInput);
}
public List<JadxCodeInput> getCodeInputs() {
return codeInputs;
}
@Override
public void registerOptions(JadxPluginOptions options) {
try {
this.options = Objects.requireNonNull(options);
options.setOptions(getArgs().getPluginOptions());
} catch (Exception e) {
throw new JadxRuntimeException("Failed to apply options for plugin: " + getPluginId(), e);
}
}
@Override
public void registerInputsHashSupplier(Supplier<String> supplier) {
this.inputsHashSupplier = supplier;
}
public String getInputsHash() {
if (inputsHashSupplier != null) {
try {
return inputsHashSupplier.get();
} catch (Exception e) {
throw new JadxRuntimeException("Failed to get inputs hash for plugin: " + getPluginId(), e);
}
}
return "";
}
@Override
public @Nullable JadxGuiContext getGuiContext() {
return guiContext;
}
public void setGuiContext(JadxGuiContext guiContext) {
this.guiContext = guiContext;
}
public JadxPlugin getPlugin() {
return plugin;
}
public JadxPluginInfo getPluginInfo() {
return pluginInfo;
}
public String getPluginId() {
return pluginInfo.getPluginId();
}
public JadxPluginOptions getOptions() {
return options;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof PluginContext)) {
return false;
}
return this.getPluginId().equals(((PluginContext) other).getPluginId());
}
@Override
public int hashCode() {
return getPluginId().hashCode();
}
@Override
public int compareTo(PluginContext other) {
return this.getPluginId().compareTo(other.getPluginId());
}
@Override
public String toString() {
return getPluginId();
}
}
@@ -44,6 +44,13 @@ public class Utils {
return obj;
}
public static String cutObject(String obj) {
if (obj.charAt(0) == 'L') {
return obj.substring(1, obj.length() - 1);
}
return obj;
}
public static String makeQualifiedObjectName(String obj) {
return 'L' + obj.replace('.', '/') + ';';
}
@@ -351,6 +351,10 @@ public class FileUtils {
return paths.stream().map(Path::toFile).collect(Collectors.toList());
}
public static String md5Sum(String str) {
return md5Sum(str.getBytes(StandardCharsets.UTF_8));
}
public static String md5Sum(byte[] data) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");