diff --git a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/save/MappingExporter.java b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/save/MappingExporter.java index e10ba23f..2956a5c6 100644 --- a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/save/MappingExporter.java +++ b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/save/MappingExporter.java @@ -3,16 +3,12 @@ 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.Map.Entry; import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -27,17 +23,12 @@ import net.fabricmc.mappingio.tree.MemoryMappingTree; import net.fabricmc.mappingio.tree.VisitOrder; import net.fabricmc.mappingio.tree.VisitableMappingTree; -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; @@ -50,6 +41,7 @@ import jadx.core.dex.nodes.RootNode; import jadx.core.utils.files.FileUtils; import jadx.plugins.mappings.RenameMappingsData; import jadx.plugins.mappings.utils.DalvikToJavaBytecodeUtils; +import jadx.plugins.mappings.utils.VariablesUtils; public class MappingExporter { private static final Logger LOG = LoggerFactory.getLogger(MappingExporter.class); @@ -62,44 +54,6 @@ public class MappingExporter { this.loadedMappingTree = RenameMappingsData.getTree(this.root); } - private List>> collectMethodVars(MethodNode methodNode) { - ICodeInfo codeInfo = methodNode.getTopParentClass().getCode(); - int mthDefPos = methodNode.getDefPosition(); - int mthLineEndPos = CodeUtils.getLineEndForPos(codeInfo.getCodeStr(), mthDefPos); - - List>> 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 - if (!vars.isEmpty()) { - vars.get(vars.size() - 1).getValue().setValue(declRef.getDefPosition() - 1); - } - return Boolean.TRUE; - } - if (lastOffset.get() != -1) { - if (!vars.isEmpty()) { - vars.get(vars.size() - 1).getValue().setValue(lastOffset.get() - 1); - } - vars.add(new SimpleEntry>(varNode, new SimpleEntry<>(lastOffset.get(), null))); - } 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) { VisitableMappingTree mappingTree = new MemoryMappingTree(); // Map < SrcName > @@ -142,14 +96,6 @@ public class MappingExporter { } 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; @@ -230,16 +176,11 @@ public class MappingExporter { // Not checking for comments since method args can't have any } // Method vars - var vars = collectMethodVars(mth); - for (int i = 0; i < vars.size(); i++) { - var entry = vars.get(i); - VarNode var = entry.getKey(); - int startOpIdx = entry.getValue().getKey(); - int endOpIdx = entry.getValue().getValue(); - Integer lvIndex = DalvikToJavaBytecodeUtils.getMethodVarLvIndex(var); - if (lvIndex == null) { - lvIndex = -1; - } + for (VariablesUtils.VarInfo info : VariablesUtils.collect(mth)) { + VarNode var = info.getVar(); + int startOpIdx = info.getStartOpIdx(); + int endOpIdx = info.getEndOpIdx(); + int lvIndex = DalvikToJavaBytecodeUtils.getMethodVarLvIndex(var); String key = rawClassName + methodInfo.getShortId() + JadxCodeRef.forVar(var.getReg(), var.getSsa()); if (mappedMethodArgsAndVars.containsKey(key)) { @@ -255,10 +196,18 @@ public class MappingExporter { } } } + // write file as late as possible because a mapping collection can fail with exception + if (mappingFormat.hasSingleFile()) { + FileUtils.deleteFileIfExists(path); + FileUtils.makeDirsForFile(path); + Files.createFile(path); + } else { + FileUtils.makeDirs(path); + } // Write file mappingTree.accept(MappingWriter.create(path, mappingFormat), VisitOrder.createByName()); mappingTree.visitEnd(); - } catch (IOException e) { + } catch (Exception e) { LOG.error("Failed to save deobfuscation map file '{}'", path.toAbsolutePath(), e); } } diff --git a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/utils/DalvikToJavaBytecodeUtils.java b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/utils/DalvikToJavaBytecodeUtils.java index 98061eba..d7af0a8c 100644 --- a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/utils/DalvikToJavaBytecodeUtils.java +++ b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/utils/DalvikToJavaBytecodeUtils.java @@ -46,7 +46,7 @@ public class DalvikToJavaBytecodeUtils { // Method vars - public static Integer getMethodVarLvIndex(VarNode methodVar) { + public static int getMethodVarLvIndex(VarNode methodVar) { MethodNode mth = methodVar.getMth(); Integer lvIndex = getMethodVarLvIndexViaSsaVars(methodVar.getReg(), mth); if (lvIndex != null) { diff --git a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/utils/VariablesUtils.java b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/utils/VariablesUtils.java new file mode 100644 index 00000000..846fd32a --- /dev/null +++ b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/utils/VariablesUtils.java @@ -0,0 +1,101 @@ +package jadx.plugins.mappings.utils; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jadx.api.ICodeInfo; +import jadx.api.metadata.ICodeAnnotation; +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.dex.nodes.MethodNode; + +public class VariablesUtils { + private static final Logger LOG = LoggerFactory.getLogger(VariablesUtils.class); + + public static class VarInfo { + private final VarNode var; + private final int startOpIdx; + private int endOpIdx; + + public VarInfo(VarNode var, int startOpIdx) { + this.var = var; + this.startOpIdx = startOpIdx; + this.endOpIdx = startOpIdx; + } + + public VarNode getVar() { + return var; + } + + public int getStartOpIdx() { + return startOpIdx; + } + + public int getEndOpIdx() { + return endOpIdx; + } + + public void setEndOpIdx(int endOpIdx) { + this.endOpIdx = endOpIdx; + } + } + + public static List collect(MethodNode mth) { + ICodeInfo codeInfo = mth.getTopParentClass().getCode(); + int mthDefPos = mth.getDefPosition(); + int mthLineEndPos = CodeUtils.getLineEndForPos(codeInfo.getCodeStr(), mthDefPos); + CodeVisitor codeVisitor = new CodeVisitor(mth); + codeInfo.getCodeMetadata().searchDown(mthLineEndPos, codeVisitor::process); + return codeVisitor.getVars(); + } + + private static class CodeVisitor { + private final MethodNode mth; + private final List vars = new ArrayList<>(); + private int lastOffset = -1; + + public CodeVisitor(MethodNode mth) { + this.mth = mth; + } + + public @Nullable Boolean process(Integer pos, ICodeAnnotation ann) { + if (ann instanceof InsnCodeOffset) { + lastOffset = ((InsnCodeOffset) ann).getOffset(); + } + if (ann instanceof NodeDeclareRef) { + ICodeNodeRef declRef = ((NodeDeclareRef) ann).getNode(); + if (declRef instanceof VarNode) { + VarNode varNode = (VarNode) declRef; + if (!varNode.getMth().equals(mth)) { // Stop if we've gone too far and have entered a different method + if (!vars.isEmpty()) { + vars.get(vars.size() - 1).setEndOpIdx(declRef.getDefPosition() - 1); + } + return Boolean.TRUE; + } + if (lastOffset != -1) { + if (!vars.isEmpty()) { + vars.get(vars.size() - 1).setEndOpIdx(lastOffset - 1); + } + vars.add(new VarInfo(varNode, lastOffset)); + } else { + LOG.warn("Local variable not present in bytecode, skipping: {}#{}", + mth.getMethodInfo().getRawFullId(), varNode.getName()); + } + lastOffset = -1; + } + } + return null; + } + + public List getVars() { + return vars; + } + } +}