Skip to content

Commit

Permalink
feat(plugins): allow to set minimum required jadx version in plugin i…
Browse files Browse the repository at this point in the history
…nfo (#2314)
  • Loading branch information
skylot committed Nov 6, 2024
1 parent 5d064d3 commit be6cb57
Show file tree
Hide file tree
Showing 19 changed files with 453 additions and 96 deletions.
22 changes: 17 additions & 5 deletions jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
Expand All @@ -13,6 +13,7 @@
import org.jetbrains.annotations.Nullable;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterDescription;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.Parameterized;
Expand Down Expand Up @@ -133,14 +134,16 @@ private static int printOptions(JCommander jc, PrintStream out, boolean addDefau
out.println("options:");

List<ParameterDescription> params = jc.getParameters();
Map<String, ParameterDescription> paramsMap = new LinkedHashMap<>(params.size());
Map<String, ParameterDescription> paramsMap = new HashMap<>(params.size());
int maxNamesLen = 0;
for (ParameterDescription p : params) {
paramsMap.put(p.getParameterized().getName(), p);
int len = p.getNames().length();
if (len > maxNamesLen) {
maxNamesLen = len;
String valueDesc = getValueDesc(p);
if (valueDesc != null) {
len += 1 + valueDesc.length();
}
maxNamesLen = Math.max(maxNamesLen, len);
}
maxNamesLen += 3;

Expand All @@ -153,8 +156,12 @@ private static int printOptions(JCommander jc, PrintStream out, boolean addDefau
}
StringBuilder opt = new StringBuilder();
opt.append(" ").append(p.getNames());
String description = p.getDescription();
String valueDesc = getValueDesc(p);
if (valueDesc != null) {
opt.append(' ').append(valueDesc);
}
addSpaces(opt, maxNamesLen - opt.length());
String description = p.getDescription();
if (description.contains("\n")) {
String[] lines = description.split("\n");
opt.append("- ").append(lines[0]);
Expand All @@ -177,6 +184,11 @@ private static int printOptions(JCommander jc, PrintStream out, boolean addDefau
return maxNamesLen;
}

private static @Nullable String getValueDesc(ParameterDescription p) {
Parameter parameterAnnotation = p.getParameterAnnotation();
return parameterAnnotation == null ? null : parameterAnnotation.defaultValueDescription();
}

/**
* Get all declared fields of the specified class and all super classes
*/
Expand Down
61 changes: 51 additions & 10 deletions jadx-cli/src/main/java/jadx/cli/commands/CommandPlugins.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import jadx.api.plugins.JadxPluginInfo;
import jadx.cli.JCommanderWrapper;
import jadx.cli.LogHelper;
import jadx.core.utils.StringUtils;
import jadx.plugins.tools.JadxPluginsList;
import jadx.plugins.tools.JadxPluginsTools;
import jadx.plugins.tools.data.JadxPluginMetadata;
Expand All @@ -20,33 +21,40 @@
@Parameters(commandDescription = "manage jadx plugins")
public class CommandPlugins implements ICommand {

@Parameter(names = { "-i", "--install" }, description = "install plugin with locationId")
@Parameter(names = { "-i", "--install" }, description = "install plugin with locationId", defaultValueDescription = "<locationId>")
protected String install;

@Parameter(names = { "-j", "--install-jar" }, description = "install plugin from jar file")
@Parameter(names = { "-j", "--install-jar" }, description = "install plugin from jar file", defaultValueDescription = "<path-to.jar>")
protected String installJar;

@Parameter(names = { "-l", "--list" }, description = "list installed plugins")
protected boolean list;

@Parameter(names = { "--list-all" }, description = "list all plugins including bundled and dropins")
protected boolean listAll;

@Parameter(names = { "-a", "--available" }, description = "list available plugins")
@Parameter(names = { "-a", "--available" }, description = "list available plugins from jadx-plugins-list (aka marketplace)")
protected boolean available;

@Parameter(names = { "-u", "--update" }, description = "update installed plugins")
protected boolean update;

@Parameter(names = { "--uninstall" }, description = "uninstall plugin with pluginId")
@Parameter(names = { "--uninstall" }, description = "uninstall plugin with pluginId", defaultValueDescription = "<pluginId>")
protected String uninstall;

@Parameter(names = { "--disable" }, description = "disable plugin with pluginId")
@Parameter(names = { "--disable" }, description = "disable plugin with pluginId", defaultValueDescription = "<pluginId>")
protected String disable;

@Parameter(names = { "--enable" }, description = "enable plugin with pluginId")
@Parameter(names = { "--enable" }, description = "enable plugin with pluginId", defaultValueDescription = "<pluginId>")
protected String enable;

@Parameter(names = { "--list-all" }, description = "list all plugins including bundled and dropins")
protected boolean listAll;

@Parameter(
names = { "--list-versions" },
description = "fetch latest versions of plugin from locationId (will download all artefacts, limited to 10)",
defaultValueDescription = "<locationId>"
)
protected String listVersions;

@Parameter(names = { "-h", "--help" }, description = "print this help", help = true)
protected boolean printHelp = false;

Expand All @@ -55,6 +63,7 @@ public String name() {
return "plugins";
}

@SuppressWarnings("UnnecessaryReturnStatement")
@Override
public void process(JCommanderWrapper<?> jcw, JCommander subCommander) {
if (printHelp) {
Expand All @@ -71,13 +80,16 @@ public void process(JCommanderWrapper<?> jcw, JCommander subCommander) {

if (install != null) {
installPlugin(install);
return;
}
if (installJar != null) {
installPlugin("file:" + installJar);
return;
}
if (uninstall != null) {
boolean uninstalled = JadxPluginsTools.getInstance().uninstall(uninstall);
System.out.println(uninstalled ? "Uninstalled" : "Plugin not found");
return;
}
if (update) {
List<JadxPluginUpdate> updates = JadxPluginsTools.getInstance().updateAll();
Expand All @@ -89,21 +101,28 @@ public void process(JCommanderWrapper<?> jcw, JCommander subCommander) {
System.out.println(" " + update.getPluginId() + ": " + update.getOldVersion() + " -> " + update.getNewVersion());
}
}
return;
}
if (list) {
printPlugins(JadxPluginsTools.getInstance().getInstalled());
return;
}
if (listAll) {
printAllPlugins();
return;
}
if (listVersions != null) {
printVersions(listVersions, 10);
return;
}

if (available) {
List<JadxPluginMetadata> availableList = JadxPluginsList.getInstance().get();
System.out.println("Available plugins: " + availableList.size());
for (JadxPluginMetadata plugin : availableList) {
System.out.println(" - " + plugin.getName() + ": " + plugin.getDescription()
+ " (" + plugin.getLocationId() + ")");
}
return;
}

if (disable != null) {
Expand All @@ -112,13 +131,15 @@ public void process(JCommanderWrapper<?> jcw, JCommander subCommander) {
} else {
System.out.println("Plugin '" + disable + "' already disabled.");
}
return;
}
if (enable != null) {
if (JadxPluginsTools.getInstance().changeDisabledStatus(enable, false)) {
System.out.println("Plugin '" + enable + "' enabled.");
} else {
System.out.println("Plugin '" + enable + "' already enabled.");
}
return;
}
}

Expand All @@ -140,6 +161,26 @@ private static void printPlugins(List<JadxPluginMetadata> installed) {
}
}

private void printVersions(String locationId, int limit) {
System.out.println("Loading ...");
List<JadxPluginMetadata> versions = JadxPluginsTools.getInstance().getVersionsByLocation(locationId, 1, limit);
if (versions.isEmpty()) {
System.out.println("No versions found");
return;
}
JadxPluginMetadata plugin = versions.get(0);
System.out.println("Versions for plugin id: " + plugin.getPluginId());
for (JadxPluginMetadata version : versions) {
StringBuilder sb = new StringBuilder();
sb.append(" - ").append(version.getVersion());
String reqVer = version.getRequiredJadxVersion();
if (StringUtils.notBlank(reqVer)) {
sb.append(", require jadx: ").append(reqVer);
}
System.out.println(sb);
}
}

private static void printAllPlugins() {
List<JadxPluginMetadata> installed = JadxPluginsTools.getInstance().getInstalled();
printPlugins(installed);
Expand Down
34 changes: 32 additions & 2 deletions jadx-core/src/main/java/jadx/api/plugins/JadxPluginInfo.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
package jadx.api.plugins;

import org.jetbrains.annotations.Nullable;

public class JadxPluginInfo {
private final String pluginId;
private final String name;
private final String description;
private final String homepage;
private String homepage;

/**
* Conflicting plugins should have the same 'provides' property; only one will be loaded
*/
private final String provides;
private String provides;

/**
* Minimum required jadx version to run this plugin.
* <br>
* Format: "<stable version>, r<revision number of unstable version>".
* Example: "1.5.1, r2305"
*
* @see <a href="https://github.com/skylot/jadx/wiki/Jadx-plugins-guide#required-jadx-version">wiki
* page</a>
* for details.
*/
private @Nullable String requiredJadxVersion;

public JadxPluginInfo(String id, String name, String description) {
this(id, name, description, "", id);
Expand Down Expand Up @@ -43,10 +57,26 @@ public String getHomepage() {
return homepage;
}

public void setHomepage(String homepage) {
this.homepage = homepage;
}

public String getProvides() {
return provides;
}

public void setProvides(String provides) {
this.provides = provides;
}

public @Nullable String getRequiredJadxVersion() {
return requiredJadxVersion;
}

public void setRequiredJadxVersion(@Nullable String requiredJadxVersion) {
this.requiredJadxVersion = requiredJadxVersion;
}

@Override
public String toString() {
return pluginId + ": " + name + " - '" + description + '\'';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

import org.jetbrains.annotations.Nullable;

import jadx.core.plugins.versions.VerifyRequiredVersion;

public class JadxPluginInfoBuilder {
private String pluginId;
private String name;
private String description;
private String homepage = "";
private @Nullable String requiredJadxVersion;
private @Nullable String provides;

/**
Expand Down Expand Up @@ -43,13 +46,23 @@ public JadxPluginInfoBuilder provides(String provides) {
return this;
}

public JadxPluginInfoBuilder requiredJadxVersion(String versions) {
this.requiredJadxVersion = versions;
return this;
}

public JadxPluginInfo build() {
Objects.requireNonNull(pluginId, "PluginId is required");
Objects.requireNonNull(name, "Name is required");
Objects.requireNonNull(description, "Description is required");
if (provides == null) {
provides = pluginId;
}
return new JadxPluginInfo(pluginId, name, description, homepage, provides);
if (requiredJadxVersion != null) {
VerifyRequiredVersion.verify(requiredJadxVersion);
}
JadxPluginInfo pluginInfo = new JadxPluginInfo(pluginId, name, description, homepage, provides);
pluginInfo.setRequiredJadxVersion(requiredJadxVersion);
return pluginInfo;
}
}
14 changes: 11 additions & 3 deletions jadx-core/src/main/java/jadx/core/plugins/JadxPluginManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import jadx.api.plugins.loader.JadxPluginLoader;
import jadx.api.plugins.options.JadxPluginOptions;
import jadx.api.plugins.options.OptionDescription;
import jadx.core.plugins.versions.VerifyRequiredVersion;
import jadx.core.utils.exceptions.JadxRuntimeException;

public class JadxPluginManager {
Expand Down Expand Up @@ -50,15 +51,16 @@ public void providesSuggestion(String provides, String pluginId) {

public void load(JadxPluginLoader pluginLoader) {
allPlugins.clear();
VerifyRequiredVersion verifyRequiredVersion = new VerifyRequiredVersion();
for (JadxPlugin plugin : pluginLoader.load()) {
addPlugin(plugin);
addPlugin(plugin, verifyRequiredVersion);
}
resolve();
}

public void register(JadxPlugin plugin) {
Objects.requireNonNull(plugin);
PluginContext addedPlugin = addPlugin(plugin);
PluginContext addedPlugin = addPlugin(plugin, new VerifyRequiredVersion());
if (addedPlugin == null) {
LOG.debug("Can't register plugin, it was disabled: {}", plugin.getPluginInfo().getPluginId());
return;
Expand All @@ -67,11 +69,17 @@ public void register(JadxPlugin plugin) {
resolve();
}

private @Nullable PluginContext addPlugin(JadxPlugin plugin) {
private @Nullable PluginContext addPlugin(JadxPlugin plugin, VerifyRequiredVersion verifyRequiredVersion) {
PluginContext pluginContext = new PluginContext(decompiler, pluginsData, plugin);
if (disabledPlugins.contains(pluginContext.getPluginId())) {
return null;
}
String requiredJadxVersion = pluginContext.getPluginInfo().getRequiredJadxVersion();
if (!verifyRequiredVersion.isCompatible(requiredJadxVersion)) {
LOG.warn("Plugin '{}' not loaded: requires '{}' jadx version which it is not compatible with current: {}",
pluginContext, requiredJadxVersion, verifyRequiredVersion.getJadxVersion());
return null;
}
LOG.debug("Loading plugin: {}", pluginContext);
if (!allPlugins.add(pluginContext)) {
throw new IllegalArgumentException("Duplicate plugin id: " + pluginContext + ", class " + plugin.getClass());
Expand Down
Loading

0 comments on commit be6cb57

Please sign in to comment.