fix: ignore debug lines hints if numbers was adjusted for method

This commit is contained in:
Skylot
2024-09-20 21:36:13 +01:00
parent 23696d3971
commit ef4f1d3ed4
3 changed files with 32 additions and 19 deletions
@@ -1,15 +1,14 @@
package jadx.core.dex.visitors.debuginfo;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import jadx.api.plugins.input.data.IDebugInfo;
import jadx.api.plugins.input.data.ILocalVar;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.nodes.LocalVarsDebugInfoAttr;
import jadx.core.dex.attributes.nodes.RegDebugInfoAttr;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg;
@@ -20,7 +19,6 @@ import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.dex.visitors.JadxVisitor;
import jadx.core.dex.visitors.blocks.BlockSplitter;
import jadx.core.dex.visitors.ssa.SSATransform;
import jadx.core.utils.ListUtils;
import jadx.core.utils.exceptions.InvalidDataException;
import jadx.core.utils.exceptions.JadxException;
@@ -59,32 +57,46 @@ public class DebugInfoAttachVisitor extends AbstractVisitor {
if (lineMapping.isEmpty()) {
return;
}
Map<Integer, Integer> linesStat = new HashMap<>(); // count repeating lines
for (Map.Entry<Integer, Integer> entry : lineMapping.entrySet()) {
try {
Integer offset = entry.getKey();
InsnNode insn = insnArr[offset];
InsnNode insn = insnArr[entry.getKey()];
if (insn != null) {
int line = entry.getValue();
insn.setSourceLine(line);
if (insn.getType() != InsnType.NOP) {
linesStat.merge(line, 1, (v, one) -> v + 1);
}
insn.setSourceLine(entry.getValue());
}
} catch (Exception e) {
mth.addWarnComment("Error attach source line", e);
return;
}
}
// 3 here is allowed maximum for lines repeat,
// can occur in indexed 'for' loops (3 instructions with same line)
List<Map.Entry<Integer, Integer>> repeatingLines = ListUtils.filter(linesStat.entrySet(), p -> p.getValue() > 3);
if (repeatingLines.isEmpty()) {
mth.add(AFlag.USE_LINES_HINTS);
String ignoreReason = verifyDebugLines(lineMapping);
if (ignoreReason != null) {
mth.addDebugComment("Don't trust debug lines info. " + ignoreReason);
} else {
mth.addDebugComment("Don't trust debug lines info. Repeating lines: " + repeatingLines);
mth.add(AFlag.USE_LINES_HINTS);
}
}
private String verifyDebugLines(Map<Integer, Integer> lineMapping) {
// search min line in method
int minLine = lineMapping.values().stream().mapToInt(v -> v).min().orElse(Integer.MAX_VALUE);
if (minLine < 3) {
return "Lines numbers was adjusted: min line is " + minLine;
}
// count repeating lines
// 3 here is allowed maximum for line repeat count
// can occur in indexed 'for' loops (3 instructions with the same line)
var repeatingLines = lineMapping.values().stream()
.collect(Collectors.toMap(l -> l, l -> 1, Integer::sum))
.entrySet().stream()
.filter(p -> p.getValue() > 3)
.collect(Collectors.toList());
if (!repeatingLines.isEmpty()) {
return "Repeating lines: " + repeatingLines;
}
return null;
}
private void attachDebugInfo(MethodNode mth, List<ILocalVar> localVars, InsnNode[] insnArr) {
if (localVars.isEmpty()) {
return;
@@ -15,6 +15,7 @@ public class TestConstructor2 extends SmaliTest {
public void test() {
assertThat(getClassNodeFromSmaliFiles())
.code()
.containsOne("A a = new A();");
.containsOne("A a = new A();")
.doesNotContain("return");
}
}
@@ -14,6 +14,6 @@ public class TestConstructorBranched3 extends SmaliTest {
disableCompilation();
assertThat(getClassNodeFromSmali())
.code()
.countString(4, "return new f(");
.countString(2, "return new f(");
}
}