fix: improve plugins data handling
This commit is contained in:
+2
-4
@@ -1,6 +1,5 @@
|
||||
package jadx.plugins.input.dex;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -21,11 +20,10 @@ public class DexInputOptions extends BaseOptionsParser {
|
||||
|
||||
public List<OptionDescription> getOptionsDescriptions() {
|
||||
return Collections.singletonList(
|
||||
new JadxOptionDescription(
|
||||
JadxOptionDescription.booleanOption(
|
||||
VERIFY_CHECKSUM_OPT,
|
||||
"verify dex file checksum before load",
|
||||
"yes",
|
||||
Arrays.asList("yes", "no")));
|
||||
true));
|
||||
}
|
||||
|
||||
public boolean isVerifyChecksum() {
|
||||
|
||||
+7
-3
@@ -7,6 +7,7 @@ import java.util.Locale;
|
||||
import jadx.api.plugins.options.OptionDescription;
|
||||
import jadx.api.plugins.options.impl.BaseOptionsParser;
|
||||
import jadx.api.plugins.options.impl.JadxOptionDescription;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
|
||||
public class JavaConvertOptions extends BaseOptionsParser {
|
||||
|
||||
@@ -34,11 +35,10 @@ public class JavaConvertOptions extends BaseOptionsParser {
|
||||
"convert mode",
|
||||
"both",
|
||||
Arrays.asList("dx", "d8", "both")),
|
||||
new JadxOptionDescription(
|
||||
JadxOptionDescription.booleanOption(
|
||||
D8_DESUGAR_OPT,
|
||||
"use desugar in d8",
|
||||
"no",
|
||||
Arrays.asList("yes", "no")));
|
||||
false));
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
@@ -48,4 +48,8 @@ public class JavaConvertOptions extends BaseOptionsParser {
|
||||
public boolean isD8Desugar() {
|
||||
return d8Desugar;
|
||||
}
|
||||
|
||||
public String getOptionsHash() {
|
||||
return FileUtils.md5Sum(mode + ":" + d8Desugar);
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -32,6 +32,7 @@ public class JavaConvertPlugin implements JadxPlugin, JadxCodeInput {
|
||||
public void init(JadxPluginContext context) {
|
||||
context.registerOptions(options);
|
||||
context.addCodeInput(this);
|
||||
context.registerInputsHashSupplier(options::getOptionsHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package jadx.plugins.mappings;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.mappingio.format.MappingFormat;
|
||||
|
||||
import jadx.api.plugins.options.OptionDescription;
|
||||
import jadx.api.plugins.options.OptionDescription.OptionFlag;
|
||||
import jadx.api.plugins.options.impl.BaseOptionsParser;
|
||||
import jadx.api.plugins.options.impl.JadxOptionDescription;
|
||||
|
||||
import static jadx.plugins.mappings.RenameMappingsPlugin.PLUGIN_ID;
|
||||
|
||||
public class RenameMappingsOptions extends BaseOptionsParser {
|
||||
|
||||
public static final String INVERT_OPT = PLUGIN_ID + ".invert";
|
||||
public static final String FORMAT_OPT = PLUGIN_ID + ".format";
|
||||
|
||||
private boolean invert = false;
|
||||
|
||||
/**
|
||||
* null value - used for 'auto' option
|
||||
*/
|
||||
private @Nullable MappingFormat format = null;
|
||||
|
||||
@Override
|
||||
public void parseOptions() {
|
||||
format = getOption(FORMAT_OPT, RenameMappingsOptions::parseMappingFormat, null);
|
||||
invert = getBooleanOption(INVERT_OPT, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OptionDescription> getOptionsDescriptions() {
|
||||
return Arrays.asList(
|
||||
new JadxOptionDescription(FORMAT_OPT, "mapping format", "auto", getMappingFormats())
|
||||
.withFlag(OptionFlag.PER_PROJECT),
|
||||
JadxOptionDescription.booleanOption(INVERT_OPT, "invert mapping", false)
|
||||
.withFlag(OptionFlag.PER_PROJECT));
|
||||
}
|
||||
|
||||
private static MappingFormat parseMappingFormat(String name) {
|
||||
String upName = name.toUpperCase(Locale.ROOT);
|
||||
if (upName.equals("AUTO")) {
|
||||
return null;
|
||||
}
|
||||
return MappingFormat.valueOf(upName);
|
||||
}
|
||||
|
||||
private static List<String> getMappingFormats() {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add("auto");
|
||||
for (MappingFormat value : MappingFormat.values()) {
|
||||
list.add(value.name());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public MappingFormat getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public boolean isInvert() {
|
||||
return invert;
|
||||
}
|
||||
|
||||
public String getOptionsHashString() {
|
||||
return format + ":" + invert;
|
||||
}
|
||||
}
|
||||
+35
-35
@@ -1,58 +1,58 @@
|
||||
package jadx.plugins.mappings;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import net.fabricmc.mappingio.MappingReader;
|
||||
import net.fabricmc.mappingio.MappingUtil;
|
||||
import net.fabricmc.mappingio.tree.MappingTree;
|
||||
import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.args.UserRenamesMappingsMode;
|
||||
import jadx.api.plugins.JadxPlugin;
|
||||
import jadx.api.plugins.JadxPluginContext;
|
||||
import jadx.api.plugins.JadxPluginInfo;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.plugins.mappings.load.CodeMappingsVisitor;
|
||||
import jadx.plugins.mappings.load.MappingsVisitor;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.plugins.mappings.load.ApplyMappingsPass;
|
||||
import jadx.plugins.mappings.load.CodeMappingsPass;
|
||||
import jadx.plugins.mappings.load.LoadMappingsPass;
|
||||
|
||||
public class RenameMappingsPlugin implements JadxPlugin {
|
||||
public static final String PLUGIN_ID = "rename-mappings";
|
||||
|
||||
private final RenameMappingsOptions options = new RenameMappingsOptions();
|
||||
|
||||
@Override
|
||||
public JadxPluginInfo getPluginInfo() {
|
||||
return new JadxPluginInfo("jadx-rename-mappings", "Rename Mappings", "various mappings support");
|
||||
return new JadxPluginInfo(PLUGIN_ID, "Rename Mappings", "various mappings support");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(JadxPluginContext context) {
|
||||
MappingTree mappingTree = openMapping(context.getArgs());
|
||||
if (mappingTree != null) {
|
||||
context.addPass(new MappingsVisitor(mappingTree));
|
||||
context.addPass(new CodeMappingsVisitor(mappingTree));
|
||||
context.registerOptions(options);
|
||||
JadxArgs args = context.getArgs();
|
||||
if (args.getUserRenamesMappingsMode() == UserRenamesMappingsMode.IGNORE) {
|
||||
return;
|
||||
}
|
||||
Path mappingsPath = args.getUserRenamesMappingsPath();
|
||||
if (mappingsPath == null || !Files.isReadable(mappingsPath)) {
|
||||
return;
|
||||
}
|
||||
LoadMappingsPass loadPass = new LoadMappingsPass(options);
|
||||
context.addPass(loadPass);
|
||||
context.addPass(new ApplyMappingsPass(loadPass));
|
||||
context.addPass(new CodeMappingsPass(loadPass));
|
||||
|
||||
// use mapping file time modification to check for changes
|
||||
context.registerInputsHashSupplier(() -> FileUtils.md5Sum(getInputsHashString(mappingsPath)));
|
||||
}
|
||||
|
||||
public MappingTree openMapping(JadxArgs args) {
|
||||
if (args.getUserRenamesMappingsMode() != UserRenamesMappingsMode.IGNORE
|
||||
&& args.getUserRenamesMappingsPath() != null) {
|
||||
try {
|
||||
MemoryMappingTree mappingTree = new MemoryMappingTree();
|
||||
MappingReader.read(args.getUserRenamesMappingsPath(), mappingTree);
|
||||
if (mappingTree.getSrcNamespace() == null) {
|
||||
mappingTree.setSrcNamespace(MappingUtil.NS_SOURCE_FALLBACK);
|
||||
}
|
||||
if (mappingTree.getDstNamespaces() == null || mappingTree.getDstNamespaces().isEmpty()) {
|
||||
mappingTree.setDstNamespaces(Collections.singletonList(MappingUtil.NS_TARGET_FALLBACK));
|
||||
} else if (mappingTree.getDstNamespaces().size() > 1) {
|
||||
throw new JadxRuntimeException(
|
||||
String.format("JADX only supports mappings with just one destination namespace! The provided ones have %s.",
|
||||
mappingTree.getDstNamespaces().size()));
|
||||
}
|
||||
return mappingTree;
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Failed to load mappings", e);
|
||||
}
|
||||
private String getInputsHashString(Path mappingsPath) {
|
||||
return getFileHashString(mappingsPath) + ':' + options.getOptionsHashString();
|
||||
}
|
||||
|
||||
private static String getFileHashString(Path mappingsPath) {
|
||||
try {
|
||||
return mappingsPath.toAbsolutePath().normalize()
|
||||
+ ":" + Files.getLastModifiedTime(mappingsPath).toMillis();
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-10
@@ -16,31 +16,36 @@ import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
|
||||
public class MappingsVisitor implements JadxPreparePass {
|
||||
public class ApplyMappingsPass implements JadxPreparePass {
|
||||
|
||||
private final MappingTree mappingTree;
|
||||
private final LoadMappingsPass loadPass;
|
||||
|
||||
public MappingsVisitor(MappingTree mappingTree) {
|
||||
this.mappingTree = mappingTree;
|
||||
public ApplyMappingsPass(LoadMappingsPass loadPass) {
|
||||
this.loadPass = loadPass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JadxPassInfo getInfo() {
|
||||
return new OrderedJadxPassInfo(
|
||||
"MappingVisitor",
|
||||
"ApplyMappings",
|
||||
"Apply mappings to classes, fields and methods")
|
||||
.after("LoadMappings")
|
||||
.before("RenameVisitor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(RootNode root) {
|
||||
process(root);
|
||||
root.registerCodeDataUpdateListener(codeData -> process(root));
|
||||
MappingTree mappingTree = loadPass.getMappings();
|
||||
if (mappingTree == null) {
|
||||
return;
|
||||
}
|
||||
process(root, mappingTree);
|
||||
root.registerCodeDataUpdateListener(codeData -> process(root, mappingTree));
|
||||
}
|
||||
|
||||
private void process(RootNode root) {
|
||||
private void process(RootNode root, MappingTree mappingTree) {
|
||||
for (ClassNode cls : root.getClasses()) {
|
||||
String clsRawName = cls.getClassInfo().makeRawFullName().replace('.', '/');
|
||||
String clsRawName = cls.getClassInfo().getRawName().replace('.', '/');
|
||||
ClassMapping mapping = mappingTree.getClass(clsRawName);
|
||||
if (mapping != null) {
|
||||
processClass(cls, mapping);
|
||||
@@ -95,6 +100,6 @@ public class MappingsVisitor implements JadxPreparePass {
|
||||
if (comment != null) {
|
||||
method.addCodeComment(comment);
|
||||
}
|
||||
// Method args & vars are handled in CodeMappingsVisitor
|
||||
// Method args & vars are handled in CodeMappingsPass
|
||||
}
|
||||
}
|
||||
+13
-9
@@ -18,26 +18,30 @@ import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.plugins.mappings.utils.DalvikToJavaBytecodeUtils;
|
||||
|
||||
public class CodeMappingsVisitor implements JadxDecompilePass {
|
||||
private final MappingTree mappingTree;
|
||||
public class CodeMappingsPass implements JadxDecompilePass {
|
||||
private final LoadMappingsPass loadPass;
|
||||
private Map<String, ClassMapping> clsRenamesMap;
|
||||
|
||||
public CodeMappingsVisitor(MappingTree mappingTree) {
|
||||
this.mappingTree = mappingTree;
|
||||
public CodeMappingsPass(LoadMappingsPass loadPass) {
|
||||
this.loadPass = loadPass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JadxPassInfo getInfo() {
|
||||
return new OrderedJadxPassInfo(
|
||||
"ApplyCodeMappings",
|
||||
"CodeMappings",
|
||||
"Apply mappings to method args and vars")
|
||||
.before("CodeRenameVisitor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(RootNode root) {
|
||||
updateMappingsMap();
|
||||
root.registerCodeDataUpdateListener(codeData -> updateMappingsMap());
|
||||
MappingTree mappingTree = loadPass.getMappings();
|
||||
if (mappingTree == null) {
|
||||
return;
|
||||
}
|
||||
updateMappingsMap(mappingTree);
|
||||
root.registerCodeDataUpdateListener(codeData -> updateMappingsMap(mappingTree));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,9 +93,9 @@ public class CodeMappingsVisitor implements JadxDecompilePass {
|
||||
return clsRenamesMap.get(classPath);
|
||||
}
|
||||
|
||||
private void updateMappingsMap() {
|
||||
private void updateMappingsMap(MappingTree mappings) {
|
||||
clsRenamesMap = new HashMap<>();
|
||||
for (ClassMapping cls : mappingTree.getClasses()) {
|
||||
for (ClassMapping cls : mappings.getClasses()) {
|
||||
for (MethodMapping mth : cls.getMethods()) {
|
||||
if (!mth.getArgs().isEmpty() || !mth.getVars().isEmpty()) {
|
||||
clsRenamesMap.put(cls.getSrcName(), cls);
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package jadx.plugins.mappings.load;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.mappingio.MappingReader;
|
||||
import net.fabricmc.mappingio.MappingUtil;
|
||||
import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch;
|
||||
import net.fabricmc.mappingio.tree.MappingTree;
|
||||
import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.plugins.pass.JadxPassInfo;
|
||||
import jadx.api.plugins.pass.impl.SimpleJadxPassInfo;
|
||||
import jadx.api.plugins.pass.types.JadxPreparePass;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.plugins.mappings.RenameMappingsOptions;
|
||||
|
||||
public class LoadMappingsPass implements JadxPreparePass {
|
||||
|
||||
private final RenameMappingsOptions options;
|
||||
private MappingTree mappings;
|
||||
|
||||
public LoadMappingsPass(RenameMappingsOptions options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JadxPassInfo getInfo() {
|
||||
return new SimpleJadxPassInfo("LoadMappings", "Load mappings file");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(RootNode root) {
|
||||
mappings = loadMapping(root.getArgs());
|
||||
}
|
||||
|
||||
public @Nullable MappingTree getMappings() {
|
||||
return mappings;
|
||||
}
|
||||
|
||||
private MappingTree loadMapping(JadxArgs args) {
|
||||
try {
|
||||
Path mappingsPath = args.getUserRenamesMappingsPath();
|
||||
MemoryMappingTree mappingTree = new MemoryMappingTree();
|
||||
MappingReader.read(mappingsPath, options.getFormat(), mappingTree);
|
||||
if (mappingTree.getSrcNamespace() == null) {
|
||||
mappingTree.setSrcNamespace(MappingUtil.NS_SOURCE_FALLBACK);
|
||||
}
|
||||
if (mappingTree.getDstNamespaces() == null || mappingTree.getDstNamespaces().isEmpty()) {
|
||||
mappingTree.setDstNamespaces(Collections.singletonList(MappingUtil.NS_TARGET_FALLBACK));
|
||||
} else if (mappingTree.getDstNamespaces().size() > 1) {
|
||||
throw new JadxRuntimeException(
|
||||
String.format("JADX only supports mappings with just one destination namespace! The provided ones have %s.",
|
||||
mappingTree.getDstNamespaces().size()));
|
||||
}
|
||||
if (options.isInvert()) {
|
||||
MemoryMappingTree invertedMappingTree = new MemoryMappingTree();
|
||||
String dstNamespace = mappingTree.getDstNamespaces().get(0);
|
||||
mappingTree.accept(new MappingSourceNsSwitch(invertedMappingTree, dstNamespace));
|
||||
return invertedMappingTree;
|
||||
}
|
||||
return mappingTree;
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Failed to load mappings", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user