refactor: move mappings feature to separate plugin module
This commit is contained in:
+10
-4
@@ -12,10 +12,6 @@ public class OrderedJadxPassInfo implements JadxPassInfo {
|
||||
private final List<String> runAfter;
|
||||
private final List<String> runBefore;
|
||||
|
||||
public OrderedJadxPassInfo(String name) {
|
||||
this(name, name);
|
||||
}
|
||||
|
||||
public OrderedJadxPassInfo(String name, String desc) {
|
||||
this(name, desc, new ArrayList<>(), new ArrayList<>());
|
||||
}
|
||||
@@ -27,6 +23,16 @@ public class OrderedJadxPassInfo implements JadxPassInfo {
|
||||
this.runBefore = runBefore;
|
||||
}
|
||||
|
||||
public OrderedJadxPassInfo after(String pass) {
|
||||
runAfter.add(pass);
|
||||
return this;
|
||||
}
|
||||
|
||||
public OrderedJadxPassInfo before(String pass) {
|
||||
runBefore.add(pass);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
plugins {
|
||||
id 'jadx-library'
|
||||
|
||||
id 'com.github.johnrengelman.shadow' version '7.1.2'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":jadx-core"))
|
||||
|
||||
// TODO: Switch back to upstream once this PR gets merged:
|
||||
// https://github.com/FabricMC/mapping-io/pull/19
|
||||
// implementation 'net.fabricmc:mapping-io:0.3.0'
|
||||
api(files('libs/mapping-io-0.4.0-SNAPSHOT.jar'))
|
||||
|
||||
constraints {
|
||||
runtimeOnly 'org.ow2.asm:asm:9.3'
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
shadow(MavenPublication) { publication ->
|
||||
project.shadow.component(publication)
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
+62
@@ -0,0 +1,62 @@
|
||||
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 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;
|
||||
|
||||
public class RenameMappingsPlugin implements JadxPlugin {
|
||||
|
||||
@Override
|
||||
public JadxPluginInfo getPluginInfo() {
|
||||
return new JadxPluginInfo("jadx-rename-mappings", "Rename Mappings", "various mappings support");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(JadxPluginContext context) {
|
||||
JadxArgs args = ((JadxDecompiler) context.getDecompiler()).getArgs();
|
||||
MappingTree mappingTree = openMapping(args);
|
||||
if (mappingTree != null) {
|
||||
JadxPassContext passContext = context.getPassContext();
|
||||
passContext.addPass(new MappingsVisitor(mappingTree));
|
||||
passContext.addPass(new CodeMappingsVisitor(mappingTree));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package jadx.plugins.mappings.load;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.fabricmc.mappingio.tree.MappingTree;
|
||||
import net.fabricmc.mappingio.tree.MappingTree.ClassMapping;
|
||||
import net.fabricmc.mappingio.tree.MappingTree.MethodArgMapping;
|
||||
import net.fabricmc.mappingio.tree.MappingTree.MethodMapping;
|
||||
|
||||
import jadx.api.core.nodes.IClassNode;
|
||||
import jadx.api.core.nodes.IMethodNode;
|
||||
import jadx.api.core.nodes.IRootNode;
|
||||
import jadx.api.plugins.pass.JadxPassInfo;
|
||||
import jadx.api.plugins.pass.impl.OrderedJadxPassInfo;
|
||||
import jadx.api.plugins.pass.types.JadxDecompilePass;
|
||||
import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
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;
|
||||
private Map<String, ClassMapping> clsRenamesMap;
|
||||
|
||||
public CodeMappingsVisitor(MappingTree mappingTree) {
|
||||
this.mappingTree = mappingTree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JadxPassInfo getInfo() {
|
||||
return new OrderedJadxPassInfo(
|
||||
"ApplyCodeMappings",
|
||||
"Apply mappings to method args and vars")
|
||||
.before("CodeRenameVisitor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(IRootNode iroot) {
|
||||
RootNode root = (RootNode) iroot;
|
||||
updateMappingsMap();
|
||||
root.registerCodeDataUpdateListener(codeData -> updateMappingsMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(IClassNode icls) {
|
||||
ClassNode cls = (ClassNode) icls;
|
||||
ClassMapping classMapping = getMapping(cls);
|
||||
if (classMapping != null) {
|
||||
applyRenames(cls, classMapping);
|
||||
}
|
||||
cls.getInnerClasses().forEach(this::visit);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IMethodNode mth) {
|
||||
}
|
||||
|
||||
private static void applyRenames(ClassNode cls, ClassMapping classMapping) {
|
||||
for (MethodNode mth : cls.getMethods()) {
|
||||
String methodName = mth.getMethodInfo().getName();
|
||||
String methodDesc = mth.getMethodInfo().getShortId().substring(methodName.length());
|
||||
List<SSAVar> ssaVars = mth.getSVars();
|
||||
if (ssaVars.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
MethodMapping methodMapping = classMapping.getMethod(methodName, methodDesc);
|
||||
if (methodMapping == null) {
|
||||
continue;
|
||||
}
|
||||
// Method args
|
||||
for (MethodArgMapping argMapping : methodMapping.getArgs()) {
|
||||
Integer mappingLvIndex = argMapping.getLvIndex();
|
||||
for (SSAVar ssaVar : ssaVars) {
|
||||
Integer actualLvIndex = DalvikToJavaBytecodeUtils.getMethodArgLvIndex(ssaVar, mth);
|
||||
if (actualLvIndex.equals(mappingLvIndex)) {
|
||||
ssaVar.getCodeVar().setName(argMapping.getDstName(0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: Method vars (if ever feasible)
|
||||
}
|
||||
}
|
||||
|
||||
private ClassMapping getMapping(ClassNode cls) {
|
||||
if (clsRenamesMap == null || clsRenamesMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String classPath = cls.getClassInfo().makeRawFullName().replace('.', '/');
|
||||
return clsRenamesMap.get(classPath);
|
||||
}
|
||||
|
||||
private void updateMappingsMap() {
|
||||
clsRenamesMap = new HashMap<>();
|
||||
for (ClassMapping cls : mappingTree.getClasses()) {
|
||||
for (MethodMapping mth : cls.getMethods()) {
|
||||
if (!mth.getArgs().isEmpty() || !mth.getVars().isEmpty()) {
|
||||
clsRenamesMap.put(cls.getSrcName(), cls);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package jadx.plugins.mappings.load;
|
||||
|
||||
import net.fabricmc.mappingio.tree.MappingTree;
|
||||
import net.fabricmc.mappingio.tree.MappingTree.ClassMapping;
|
||||
import net.fabricmc.mappingio.tree.MappingTree.FieldMapping;
|
||||
import net.fabricmc.mappingio.tree.MappingTree.MethodMapping;
|
||||
|
||||
import jadx.api.core.nodes.IRootNode;
|
||||
import jadx.api.plugins.pass.JadxPassInfo;
|
||||
import jadx.api.plugins.pass.impl.OrderedJadxPassInfo;
|
||||
import jadx.api.plugins.pass.types.JadxPreparePass;
|
||||
import jadx.core.codegen.TypeGen;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
|
||||
public class MappingsVisitor implements JadxPreparePass {
|
||||
|
||||
private final MappingTree mappingTree;
|
||||
|
||||
public MappingsVisitor(MappingTree mappingTree) {
|
||||
this.mappingTree = mappingTree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JadxPassInfo getInfo() {
|
||||
return new OrderedJadxPassInfo(
|
||||
"MappingVisitor",
|
||||
"Apply mappings to classes, fields and methods")
|
||||
.before("RenameVisitor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(IRootNode iroot) {
|
||||
RootNode root = (RootNode) iroot;
|
||||
process(root);
|
||||
root.registerCodeDataUpdateListener(codeData -> process(root));
|
||||
}
|
||||
|
||||
private void process(RootNode root) {
|
||||
for (ClassNode cls : root.getClasses()) {
|
||||
ClassMapping mapping = mappingTree.getClass(cls.getClassInfo().makeRawFullName().replace('.', '/'));
|
||||
if (mapping == null) {
|
||||
continue;
|
||||
}
|
||||
processClass(cls, mapping);
|
||||
}
|
||||
}
|
||||
|
||||
private static void processClass(ClassNode cls, ClassMapping classMapping) {
|
||||
String alias = classMapping.getDstName(0);
|
||||
if (alias != null) {
|
||||
cls.rename(alias.replace('/', '.'));
|
||||
}
|
||||
if (classMapping.getComment() != null) {
|
||||
cls.addCodeComment(classMapping.getComment());
|
||||
}
|
||||
for (FieldNode field : cls.getFields()) {
|
||||
FieldInfo fieldInfo = field.getFieldInfo();
|
||||
String signature = TypeGen.signature(fieldInfo.getType());
|
||||
FieldMapping fieldMapping = classMapping.getField(fieldInfo.getName(), signature);
|
||||
if (fieldMapping != null) {
|
||||
processField(field, fieldMapping);
|
||||
}
|
||||
}
|
||||
for (MethodNode method : cls.getMethods()) {
|
||||
MethodInfo methodInfo = method.getMethodInfo();
|
||||
String methodName = methodInfo.getName();
|
||||
String methodDesc = methodInfo.getShortId().substring(methodName.length());
|
||||
MethodMapping methodMapping = classMapping.getMethod(methodName, methodDesc);
|
||||
if (methodMapping != null) {
|
||||
processMethod(method, methodMapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void processField(FieldNode field, FieldMapping fieldMapping) {
|
||||
String alias = fieldMapping.getDstName(0);
|
||||
if (alias != null) {
|
||||
field.rename(alias);
|
||||
}
|
||||
String comment = fieldMapping.getComment();
|
||||
if (comment != null) {
|
||||
field.addCodeComment(comment);
|
||||
}
|
||||
}
|
||||
|
||||
private static void processMethod(MethodNode method, MethodMapping methodMapping) {
|
||||
String alias = methodMapping.getDstName(0);
|
||||
if (alias != null) {
|
||||
method.rename(alias);
|
||||
}
|
||||
String comment = methodMapping.getComment();
|
||||
if (comment != null) {
|
||||
method.addCodeComment(comment);
|
||||
}
|
||||
// Method args & vars are handled in CodeMappingsVisitor
|
||||
}
|
||||
}
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
package jadx.plugins.mappings.save;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.fabricmc.mappingio.MappedElementKind;
|
||||
import net.fabricmc.mappingio.MappingUtil;
|
||||
import net.fabricmc.mappingio.MappingWriter;
|
||||
import net.fabricmc.mappingio.format.MappingFormat;
|
||||
import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.api.data.ICodeComment;
|
||||
import jadx.api.data.ICodeRename;
|
||||
import jadx.api.data.IJavaNodeRef.RefType;
|
||||
import jadx.api.data.impl.JadxCodeData;
|
||||
import jadx.api.data.impl.JadxCodeRef;
|
||||
import jadx.api.metadata.ICodeNodeRef;
|
||||
import jadx.api.metadata.annotations.InsnCodeOffset;
|
||||
import jadx.api.metadata.annotations.NodeDeclareRef;
|
||||
import jadx.api.metadata.annotations.VarNode;
|
||||
import jadx.api.utils.CodeUtils;
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.codegen.TypeGen;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.plugins.mappings.utils.DalvikToJavaBytecodeUtils;
|
||||
|
||||
public class MappingExporter {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MappingExporter.class);
|
||||
private final RootNode root;
|
||||
|
||||
public MappingExporter(RootNode rootNode) {
|
||||
this.root = rootNode;
|
||||
}
|
||||
|
||||
private List<SimpleEntry<VarNode, Integer>> collectMethodVars(MethodNode methodNode) {
|
||||
ICodeInfo codeInfo = methodNode.getTopParentClass().getCode();
|
||||
int mthDefPos = methodNode.getDefPosition();
|
||||
int mthLineEndPos = CodeUtils.getLineEndForPos(codeInfo.getCodeStr(), mthDefPos);
|
||||
|
||||
List<SimpleEntry<VarNode, Integer>> vars = new ArrayList<>();
|
||||
AtomicInteger lastOffset = new AtomicInteger(-1);
|
||||
codeInfo.getCodeMetadata().searchDown(mthLineEndPos, (pos, ann) -> {
|
||||
if (ann instanceof InsnCodeOffset) {
|
||||
lastOffset.set(((InsnCodeOffset) ann).getOffset());
|
||||
}
|
||||
if (ann instanceof NodeDeclareRef) {
|
||||
ICodeNodeRef declRef = ((NodeDeclareRef) ann).getNode();
|
||||
if (declRef instanceof VarNode) {
|
||||
VarNode varNode = (VarNode) declRef;
|
||||
if (!varNode.getMth().equals(methodNode)) {
|
||||
// Stop if we've gone too far and have entered a different method
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
if (lastOffset.get() != -1) {
|
||||
vars.add(new SimpleEntry<VarNode, Integer>(varNode, lastOffset.get()));
|
||||
} else {
|
||||
LOG.warn("Local variable not present in bytecode, skipping: "
|
||||
+ methodNode.getMethodInfo().getRawFullId() + "#" + varNode.getName());
|
||||
}
|
||||
lastOffset.set(-1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return vars;
|
||||
}
|
||||
|
||||
public void exportMappings(Path path, JadxCodeData codeData, MappingFormat mappingFormat) {
|
||||
MemoryMappingTree mappingTree = new MemoryMappingTree();
|
||||
// Map < SrcName >
|
||||
Set<String> mappedClasses = new HashSet<>();
|
||||
// Map < DeclClass + ShortId >
|
||||
Set<String> mappedFields = new HashSet<>();
|
||||
Set<String> mappedMethods = new HashSet<>();
|
||||
Set<String> methodsWithMappedElements = new HashSet<>();
|
||||
// Map < DeclClass + MethodShortId + CodeRef, NewName >
|
||||
Map<String, String> mappedMethodArgsAndVars = new HashMap<>();
|
||||
// Map < DeclClass + *ShortId + *CodeRef, Comment >
|
||||
Map<String, String> comments = new HashMap<>();
|
||||
|
||||
// We have to do this so we know for sure which elements are *manually* renamed
|
||||
for (ICodeRename codeRename : codeData.getRenames()) {
|
||||
if (codeRename.getNodeRef().getType().equals(RefType.CLASS)) {
|
||||
mappedClasses.add(codeRename.getNodeRef().getDeclaringClass());
|
||||
} else if (codeRename.getNodeRef().getType().equals(RefType.FIELD)) {
|
||||
mappedFields.add(codeRename.getNodeRef().getDeclaringClass() + codeRename.getNodeRef().getShortId());
|
||||
} else if (codeRename.getNodeRef().getType().equals(RefType.METHOD)) {
|
||||
if (codeRename.getCodeRef() == null) {
|
||||
mappedMethods.add(codeRename.getNodeRef().getDeclaringClass() + codeRename.getNodeRef().getShortId());
|
||||
} else {
|
||||
methodsWithMappedElements.add(codeRename.getNodeRef().getDeclaringClass() + codeRename.getNodeRef().getShortId());
|
||||
mappedMethodArgsAndVars.put(codeRename.getNodeRef().getDeclaringClass()
|
||||
+ codeRename.getNodeRef().getShortId()
|
||||
+ codeRename.getCodeRef(),
|
||||
codeRename.getNewName());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (ICodeComment codeComment : codeData.getComments()) {
|
||||
comments.put(codeComment.getNodeRef().getDeclaringClass()
|
||||
+ (codeComment.getNodeRef().getShortId() == null ? "" : codeComment.getNodeRef().getShortId())
|
||||
+ (codeComment.getCodeRef() == null ? "" : codeComment.getCodeRef()),
|
||||
codeComment.getComment());
|
||||
if (codeComment.getCodeRef() != null) {
|
||||
methodsWithMappedElements.add(codeComment.getNodeRef().getDeclaringClass() + codeComment.getNodeRef().getShortId());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (mappingFormat.hasSingleFile()) {
|
||||
FileUtils.deleteFileIfExists(path);
|
||||
FileUtils.makeDirsForFile(path);
|
||||
Files.createFile(path);
|
||||
} else {
|
||||
FileUtils.makeDirs(path);
|
||||
}
|
||||
|
||||
String srcNamespace = MappingUtil.NS_SOURCE_FALLBACK;
|
||||
String dstNamespace = MappingUtil.NS_TARGET_FALLBACK;
|
||||
|
||||
mappingTree.visitHeader();
|
||||
mappingTree.visitNamespaces(srcNamespace, Collections.singletonList(dstNamespace));
|
||||
mappingTree.visitContent();
|
||||
|
||||
for (ClassNode cls : root.getClasses()) {
|
||||
ClassInfo classInfo = cls.getClassInfo();
|
||||
String classPath = classInfo.makeRawFullName().replace('.', '/');
|
||||
String rawClassName = classInfo.getRawName();
|
||||
|
||||
if (classInfo.hasAlias()
|
||||
&& !classInfo.getAliasShortName().equals(classInfo.getShortName())
|
||||
&& mappedClasses.contains(rawClassName)) {
|
||||
mappingTree.visitClass(classPath);
|
||||
String alias = classInfo.makeAliasRawFullName().replace('.', '/');
|
||||
|
||||
if (alias.startsWith(Consts.DEFAULT_PACKAGE_NAME)) {
|
||||
alias = alias.substring(Consts.DEFAULT_PACKAGE_NAME.length() + 1);
|
||||
}
|
||||
mappingTree.visitDstName(MappedElementKind.CLASS, 0, alias);
|
||||
}
|
||||
if (comments.containsKey(rawClassName)) {
|
||||
mappingTree.visitClass(classPath);
|
||||
mappingTree.visitComment(MappedElementKind.CLASS, comments.get(rawClassName));
|
||||
}
|
||||
|
||||
for (FieldNode fld : cls.getFields()) {
|
||||
FieldInfo fieldInfo = fld.getFieldInfo();
|
||||
if (fieldInfo.hasAlias() && mappedFields.contains(rawClassName + fieldInfo.getShortId())) {
|
||||
visitField(mappingTree, classPath, fieldInfo.getName(), TypeGen.signature(fieldInfo.getType()));
|
||||
mappingTree.visitDstName(MappedElementKind.FIELD, 0, fieldInfo.getAlias());
|
||||
}
|
||||
if (comments.containsKey(rawClassName + fieldInfo.getShortId())) {
|
||||
visitField(mappingTree, classPath, fieldInfo.getName(), TypeGen.signature(fieldInfo.getType()));
|
||||
mappingTree.visitComment(MappedElementKind.FIELD, comments.get(rawClassName + fieldInfo.getShortId()));
|
||||
}
|
||||
}
|
||||
|
||||
for (MethodNode mth : cls.getMethods()) {
|
||||
MethodInfo methodInfo = mth.getMethodInfo();
|
||||
String methodName = methodInfo.getName();
|
||||
String methodDesc = methodInfo.getShortId().substring(methodName.length());
|
||||
if (methodInfo.hasAlias() && mappedMethods.contains(rawClassName + methodInfo.getShortId())) {
|
||||
visitMethod(mappingTree, classPath, methodName, methodDesc);
|
||||
mappingTree.visitDstName(MappedElementKind.METHOD, 0, methodInfo.getAlias());
|
||||
}
|
||||
if (comments.containsKey(rawClassName + methodInfo.getShortId())) {
|
||||
visitMethod(mappingTree, classPath, methodName, methodDesc);
|
||||
mappingTree.visitComment(MappedElementKind.METHOD, comments.get(rawClassName + methodInfo.getShortId()));
|
||||
}
|
||||
|
||||
if (!methodsWithMappedElements.contains(rawClassName + methodInfo.getShortId())) {
|
||||
continue;
|
||||
}
|
||||
// Method args
|
||||
int lvtIndex = mth.getAccessFlags().isStatic() ? 0 : 1;
|
||||
List<VarNode> args = mth.collectArgsWithoutLoading();
|
||||
for (VarNode arg : args) {
|
||||
Integer lvIndex = DalvikToJavaBytecodeUtils.getMethodArgLvIndex(arg);
|
||||
if (lvIndex == null) {
|
||||
lvIndex = -1;
|
||||
}
|
||||
String key = rawClassName + methodInfo.getShortId()
|
||||
+ JadxCodeRef.forVar(arg.getReg(), arg.getSsa());
|
||||
if (mappedMethodArgsAndVars.containsKey(key)) {
|
||||
visitMethodArg(mappingTree, classPath, methodName, methodDesc, args.indexOf(arg), lvIndex);
|
||||
mappingTree.visitDstName(MappedElementKind.METHOD_ARG, 0, mappedMethodArgsAndVars.get(key));
|
||||
mappedMethodArgsAndVars.remove(key);
|
||||
}
|
||||
lvtIndex++;
|
||||
// Not checking for comments since method args can't have any
|
||||
}
|
||||
// Method vars
|
||||
List<SimpleEntry<VarNode, Integer>> vars = collectMethodVars(mth);
|
||||
for (SimpleEntry<VarNode, Integer> entry : vars) {
|
||||
VarNode var = entry.getKey();
|
||||
int offset = entry.getValue();
|
||||
Integer lvIndex = DalvikToJavaBytecodeUtils.getMethodVarLvIndex(var);
|
||||
if (lvIndex == null) {
|
||||
lvIndex = -1;
|
||||
}
|
||||
String key = rawClassName + methodInfo.getShortId()
|
||||
+ JadxCodeRef.forVar(var.getReg(), var.getSsa());
|
||||
if (mappedMethodArgsAndVars.containsKey(key)) {
|
||||
visitMethodVar(mappingTree, classPath, methodName, methodDesc, lvtIndex, lvIndex, offset);
|
||||
mappingTree.visitDstName(MappedElementKind.METHOD_VAR, 0, mappedMethodArgsAndVars.get(key));
|
||||
}
|
||||
key = rawClassName + methodInfo.getShortId() + JadxCodeRef.forInsn(offset);
|
||||
if (comments.containsKey(key)) {
|
||||
visitMethodVar(mappingTree, classPath, methodName, methodDesc, lvtIndex, lvIndex, offset);
|
||||
mappingTree.visitComment(MappedElementKind.METHOD_VAR, comments.get(key));
|
||||
}
|
||||
lvtIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Write file
|
||||
MappingWriter writer = MappingWriter.create(path, mappingFormat);
|
||||
mappingTree.accept(writer);
|
||||
mappingTree.visitEnd();
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to save deobfuscation map file '{}'", path.toAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void visitField(MemoryMappingTree tree, String classPath, String srcName, String srcDesc) {
|
||||
tree.visitClass(classPath);
|
||||
tree.visitField(srcName, srcDesc);
|
||||
}
|
||||
|
||||
private void visitMethod(MemoryMappingTree tree, String classPath, String srcName, String srcDesc) {
|
||||
tree.visitClass(classPath);
|
||||
tree.visitMethod(srcName, srcDesc);
|
||||
}
|
||||
|
||||
private void visitMethodArg(MemoryMappingTree tree, String classPath, String methodSrcName, String methodSrcDesc, int argPosition,
|
||||
int lvIndex) {
|
||||
visitMethod(tree, classPath, methodSrcName, methodSrcDesc);
|
||||
tree.visitMethodArg(argPosition, lvIndex, null);
|
||||
}
|
||||
|
||||
private void visitMethodVar(MemoryMappingTree tree, String classPath, String methodSrcName, String methodSrcDesc, int lvtIndex,
|
||||
int lvIndex, int startOpIdx) {
|
||||
visitMethod(tree, classPath, methodSrcName, methodSrcDesc);
|
||||
tree.visitMethodVar(lvtIndex, lvIndex, startOpIdx, null);
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
package jadx.plugins.mappings.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.api.metadata.annotations.VarNode;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
public class DalvikToJavaBytecodeUtils {
|
||||
|
||||
// ****************************
|
||||
// Local variable index
|
||||
// ****************************
|
||||
|
||||
// Method args
|
||||
|
||||
public static Integer getMethodArgLvIndex(VarNode methodArg) {
|
||||
MethodNode mth = methodArg.getMth();
|
||||
Integer lvIndex = getMethodArgLvIndexViaSsaVars(methodArg.getReg(), mth);
|
||||
if (lvIndex != null) {
|
||||
return lvIndex;
|
||||
}
|
||||
List<VarNode> args = mth.collectArgsWithoutLoading();
|
||||
for (VarNode arg : args) {
|
||||
lvIndex = arg.getReg() - args.get(0).getReg() + (mth.getAccessFlags().isStatic() ? 0 : 1);
|
||||
if (arg.equals(methodArg)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return lvIndex;
|
||||
}
|
||||
|
||||
public static Integer getMethodArgLvIndex(SSAVar methodArgSsaVar, MethodNode mth) {
|
||||
return getMethodArgLvIndexViaSsaVars(methodArgSsaVar.getRegNum(), mth);
|
||||
}
|
||||
|
||||
private static Integer getMethodArgLvIndexViaSsaVars(int regNum, MethodNode mth) {
|
||||
List<SSAVar> ssaVars = mth.getSVars();
|
||||
if (!ssaVars.isEmpty()) {
|
||||
return regNum - ssaVars.get(0).getRegNum();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Method vars
|
||||
|
||||
public static Integer getMethodVarLvIndex(VarNode methodVar) {
|
||||
MethodNode mth = methodVar.getMth();
|
||||
Integer lvIndex = getMethodVarLvIndexViaSsaVars(methodVar.getReg(), mth);
|
||||
if (lvIndex != null) {
|
||||
return lvIndex;
|
||||
}
|
||||
Integer lastArgLvIndex = mth.getAccessFlags().isStatic() ? -1 : 0;
|
||||
List<VarNode> args = mth.collectArgsWithoutLoading();
|
||||
if (!args.isEmpty()) {
|
||||
lastArgLvIndex = getMethodArgLvIndex(args.get(args.size() - 1));
|
||||
}
|
||||
return lastArgLvIndex + methodVar.getReg() + (mth.getAccessFlags().isStatic() ? 0 : 1);
|
||||
}
|
||||
|
||||
public static Integer getMethodVarLvIndex(SSAVar methodVarSsaVar, MethodNode mth) {
|
||||
return getMethodVarLvIndexViaSsaVars(methodVarSsaVar.getRegNum(), mth);
|
||||
}
|
||||
|
||||
private static Integer getMethodVarLvIndexViaSsaVars(int regNum, MethodNode mth) {
|
||||
List<SSAVar> ssaVars = mth.getSVars();
|
||||
if (ssaVars.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Integer lastArgLvIndex = mth.getAccessFlags().isStatic() ? -1 : 0;
|
||||
List<RegisterArg> args = mth.getArgRegs();
|
||||
if (!args.isEmpty()) {
|
||||
lastArgLvIndex = getMethodArgLvIndexViaSsaVars(args.get(args.size() - 1).getSVar().getRegNum(), mth);
|
||||
}
|
||||
return lastArgLvIndex + regNum + (mth.getAccessFlags().isStatic() ? 0 : 1);
|
||||
}
|
||||
|
||||
// ****************************
|
||||
// Local variable table index
|
||||
// ****************************
|
||||
|
||||
// Method args
|
||||
|
||||
public static Integer getMethodArgLvtIndex(VarNode methodArg) {
|
||||
MethodNode mth = methodArg.getMth();
|
||||
int lvtIndex = mth.getAccessFlags().isStatic() ? 0 : 1;
|
||||
List<VarNode> args = mth.collectArgsWithoutLoading();
|
||||
for (VarNode arg : args) {
|
||||
if (arg.equals(methodArg)) {
|
||||
return lvtIndex;
|
||||
}
|
||||
lvtIndex++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Integer getMethodArgLvtIndex(SSAVar methodArgSsaVar, MethodNode mth) {
|
||||
List<SSAVar> ssaVars = mth.getSVars();
|
||||
if (ssaVars.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<RegisterArg> args = mth.getArgRegs();
|
||||
int lvtIndex = mth.getAccessFlags().isStatic() ? 0 : 1;
|
||||
for (RegisterArg arg : args) {
|
||||
if (arg.getSVar().equals(methodArgSsaVar)) {
|
||||
return lvtIndex;
|
||||
}
|
||||
lvtIndex++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Method vars
|
||||
|
||||
// TODO: public static Integer getMethodVarLvtIndex(VarNode methodVar) {}
|
||||
|
||||
public static Integer getMethodVarLvtIndex(SSAVar methodVarSsaVar, MethodNode mth) {
|
||||
List<SSAVar> ssaVars = new ArrayList<>(mth.getSVars());
|
||||
if (ssaVars.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Integer lvtIndex = getMethodArgLvtIndex(methodVarSsaVar, mth);
|
||||
if (lvtIndex != null) {
|
||||
return lvtIndex;
|
||||
}
|
||||
|
||||
lvtIndex = mth.getAccessFlags().isStatic() ? 0 : 1;
|
||||
lvtIndex += mth.getArgTypes().size();
|
||||
|
||||
lvtIndex = getMethodArgLvtIndex(methodVarSsaVar, mth) + 1;
|
||||
ssaVars.subList(0, ssaVars.indexOf(methodVarSsaVar) + 1).clear();
|
||||
|
||||
int lastRegNum = -1;
|
||||
for (SSAVar ssaVar : ssaVars) {
|
||||
if (ssaVar.getRegNum() == lastRegNum) {
|
||||
// Not present in bytecode
|
||||
// System.out.println("Duplicate RegNum: " + ssaVar.getRegNum());
|
||||
continue;
|
||||
}
|
||||
lvtIndex++;
|
||||
if (ssaVar.equals(methodVarSsaVar)) {
|
||||
return lvtIndex;
|
||||
}
|
||||
lastRegNum = ssaVar.getRegNum();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
jadx.plugins.mappings.RenameMappingsPlugin
|
||||
Reference in New Issue
Block a user