diff --git a/jadx-core/src/main/java/jadx/api/Decompiler.java b/jadx-core/src/main/java/jadx/api/Decompiler.java index 61fc29d1..f51db74b 100644 --- a/jadx-core/src/main/java/jadx/api/Decompiler.java +++ b/jadx-core/src/main/java/jadx/api/Decompiler.java @@ -65,7 +65,7 @@ public final class Decompiler { for (ClassNode classNode : root.getClasses()) { classes.add(new JavaClass(this, classNode)); } - return classes; + return Collections.unmodifiableList(classes); } public List getPackages() { @@ -85,7 +85,7 @@ public final class Decompiler { packages.add(new JavaPackage(entry.getKey(), entry.getValue())); } Collections.sort(packages); - return packages; + return Collections.unmodifiableList(packages); } public void saveAll() throws InterruptedException { diff --git a/jadx-core/src/main/java/jadx/api/JavaClass.java b/jadx-core/src/main/java/jadx/api/JavaClass.java index 4a986db8..5cacd467 100644 --- a/jadx-core/src/main/java/jadx/api/JavaClass.java +++ b/jadx-core/src/main/java/jadx/api/JavaClass.java @@ -1,21 +1,67 @@ package jadx.api; import jadx.core.codegen.CodeWriter; +import jadx.core.dex.attributes.AttributeFlag; +import jadx.core.dex.info.AccessInfo; import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.FieldNode; +import jadx.core.dex.nodes.MethodNode; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; public final class JavaClass { private final Decompiler decompiler; private final ClassNode cls; + private final List innerClasses; + private final List fields; + private final List methods; JavaClass(Decompiler decompiler, ClassNode classNode) { this.decompiler = decompiler; this.cls = classNode; + + int inClsCount = cls.getInnerClasses().size(); + if (inClsCount == 0) { + this.innerClasses = Collections.emptyList(); + } else { + List list = new ArrayList(inClsCount); + for (ClassNode inner : cls.getInnerClasses()) { + list.add(new JavaClass(decompiler, inner)); + } + this.innerClasses = Collections.unmodifiableList(list); + } + + int fieldsCount = cls.getFields().size(); + if (fieldsCount == 0) { + this.fields = Collections.emptyList(); + } else { + List flds = new ArrayList(fieldsCount); + for (FieldNode f : cls.getFields()) { + flds.add(new JavaField(f)); + } + this.fields = Collections.unmodifiableList(flds); + } + + int methodsCount = cls.getMethods().size(); + if (methodsCount == 0) { + this.methods = Collections.emptyList(); + } else { + List mths = new ArrayList(methodsCount); + for (MethodNode m : cls.getMethods()) { + if (!m.getAttributes().contains(AttributeFlag.DONT_GENERATE)) { + mths.add(new JavaMethod(m)); + } + } + this.methods = Collections.unmodifiableList(mths); + } } public String getCode() { CodeWriter code = cls.getCode(); - if(code == null) { + if (code == null) { decompiler.processClass(cls); code = cls.getCode(); } @@ -34,8 +80,28 @@ public final class JavaClass { return cls.getPackage(); } + public AccessInfo getAccessInfo() { + return cls.getAccessFlags(); + } + + public List getInnerClasses() { + return innerClasses; + } + + public List getFields() { + return fields; + } + + public List getMethods() { + return methods; + } + @Override public String toString() { return getFullName(); } + + public int getDecompiledLine() { + return cls.getDecompiledLine(); + } } diff --git a/jadx-core/src/main/java/jadx/api/JavaField.java b/jadx-core/src/main/java/jadx/api/JavaField.java new file mode 100644 index 00000000..d5f30ee3 --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/JavaField.java @@ -0,0 +1,25 @@ +package jadx.api; + +import jadx.core.dex.info.AccessInfo; +import jadx.core.dex.nodes.FieldNode; + +public class JavaField { + + private final FieldNode field; + + public JavaField(FieldNode f) { + this.field = f; + } + + public String getName() { + return field.getName(); + } + + public AccessInfo getAccessFlags() { + return field.getAccessFlags(); + } + + public int getDecompiledLine() { + return field.getDecompiledLine(); + } +} diff --git a/jadx-core/src/main/java/jadx/api/JavaMethod.java b/jadx-core/src/main/java/jadx/api/JavaMethod.java new file mode 100644 index 00000000..f6585224 --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/JavaMethod.java @@ -0,0 +1,31 @@ +package jadx.api; + +import jadx.core.dex.info.AccessInfo; +import jadx.core.dex.info.MethodInfo; +import jadx.core.dex.nodes.MethodNode; + +public class JavaMethod { + private final MethodNode mth; + + public JavaMethod(MethodNode m) { + this.mth = m; + } + + public String getName() { + MethodInfo mi = mth.getMethodInfo(); + if (mi.isConstructor()) { + return mth.getParentClass().getShortName(); + } else if (mi.isClassInit()) { + return "static"; + } + return mi.getName(); + } + + public AccessInfo getAccessFlags() { + return mth.getAccessFlags(); + } + + public int getDecompiledLine() { + return mth.getDecompiledLine(); + } +} diff --git a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java index 58754e6b..7951eff0 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java @@ -59,7 +59,7 @@ public class ClassGen { if (!"".equals(cls.getPackage())) { clsCode.add("package ").add(cls.getPackage()).add(';'); - clsCode.endl(); + clsCode.newLine(); } if (imports.size() != 0) { @@ -71,7 +71,7 @@ public class ClassGen { for (String imp : sortImports) { clsCode.startLine("import ").add(imp).add(';'); } - clsCode.endl(); + clsCode.newLine(); sortImports.clear(); imports.clear(); @@ -90,7 +90,7 @@ public class ClassGen { makeClassDeclaration(code); makeClassBody(code); - code.endl(); + code.newLine(); } public void makeClassDeclaration(CodeWriter clsCode) { @@ -139,6 +139,8 @@ public class ClassGen { if (!cls.getInterfaces().isEmpty()) clsCode.add(' '); } + + clsCode.attachAnnotation(cls); } public boolean makeGenericMap(CodeWriter code, Map> gmap) { @@ -178,13 +180,13 @@ public class ClassGen { CodeWriter fieldsCode = makeFields(clsCode, cls, cls.getFields()); clsCode.add(fieldsCode); if (fieldsCode.notEmpty() && mthsCode.notEmpty()) - clsCode.endl(); + clsCode.newLine(); // insert inner classes code if (cls.getInnerClasses().size() != 0) { clsCode.add(makeInnerClasses(cls, clsCode.getIndent())); if (mthsCode.notEmpty()) - clsCode.endl(); + clsCode.newLine(); } clsCode.add(mthsCode); clsCode.startLine('}'); @@ -239,7 +241,7 @@ public class ClassGen { } if (it.hasNext()) - code.endl(); + code.newLine(); } return code; } @@ -278,7 +280,7 @@ public class ClassGen { code.startLine(); code.add(';'); - code.endl(); + code.newLine(); } for (FieldNode f : fields) { @@ -297,6 +299,7 @@ public class ClassGen { } } code.add(';'); + code.attachAnnotation(f); } return code; } diff --git a/jadx-core/src/main/java/jadx/core/codegen/CodeGen.java b/jadx-core/src/main/java/jadx/core/codegen/CodeGen.java index aeb4fc81..5e496948 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/CodeGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/CodeGen.java @@ -21,14 +21,8 @@ public class CodeGen extends AbstractVisitor { public boolean visit(ClassNode cls) throws CodegenException { ClassGen clsGen = new ClassGen(cls, null, isFallbackMode()); CodeWriter clsCode = clsGen.makeClass(); - + clsCode.finish(); cls.setCode(clsCode); - -// String fileName = cls.getClassInfo().getFullPath() + ".java"; -// if (isFallbackMode()) -// fileName += ".jadx"; -// clsCode.save(dir, fileName); - return false; } diff --git a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java index cda2a444..c0ebb31b 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java +++ b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java @@ -1,9 +1,13 @@ package jadx.core.codegen; +import jadx.core.dex.attributes.LineAttrNode; import jadx.core.utils.exceptions.JadxRuntimeException; import java.io.File; import java.io.PrintWriter; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,6 +23,9 @@ public class CodeWriter { private String indentStr; private int indent; + private int line = 1; + private Map annotations = Collections.emptyMap(); + public CodeWriter() { this.indent = 0; this.indentStr = ""; @@ -29,32 +36,32 @@ public class CodeWriter { updateIndent(); } - public CodeWriter startLine(String str) { - buf.append(NL); + public CodeWriter startLine() { + addLine(); buf.append(indentStr); - buf.append(str); return this; } public CodeWriter startLine(char c) { - buf.append(NL); + addLine(); buf.append(indentStr); buf.append(c); return this; } - public CodeWriter startLine(int ind, String str) { - buf.append(NL); + public CodeWriter startLine(String str) { + addLine(); buf.append(indentStr); - for (int i = 0; i < ind; i++) - buf.append(INDENT); buf.append(str); return this; } - public CodeWriter startLine() { - buf.append(NL); + public CodeWriter startLine(int ind, String str) { + addLine(); buf.append(indentStr); + for (int i = 0; i < ind; i++) + buf.append(INDENT); + buf.append(str); return this; } @@ -68,17 +75,47 @@ public class CodeWriter { return this; } - public CodeWriter add(CodeWriter mthsCode) { - buf.append(mthsCode.toString()); + public CodeWriter add(CodeWriter code) { + line--; + for (Map.Entry entry : code.annotations.entrySet()) { + attachAnnotation(entry.getKey(), line + entry.getValue()); + } + line += code.line; + buf.append(code.toString()); return this; } - public CodeWriter endl() { + public CodeWriter newLine() { + addLine(); + return this; + } + + private void addLine() { buf.append(NL); + line++; + } + + public int getLine() { + return line; + } + + public Object attachAnnotation(Object obj) { + return attachAnnotation(obj, line); + } + + public Object attachAnnotation(Object obj, int line) { + if (annotations.isEmpty()) { + annotations = new HashMap(); + } + return annotations.put(obj, line); + } + + public CodeWriter indent() { + buf.append(indentStr); return this; } - private static final String[] INDENT_CACHE = new String[] { + private static final String[] INDENT_CACHE = new String[]{ "", INDENT, INDENT + INDENT, @@ -126,6 +163,18 @@ public class CodeWriter { updateIndent(); } + public void finish() { + buf.trimToSize(); + for (Map.Entry entry : annotations.entrySet()) { + Object v = entry.getKey(); + if(v instanceof LineAttrNode) { + LineAttrNode l = (LineAttrNode) v; + l.setDecompiledLine(entry.getValue()); + } + } + annotations.clear(); + } + private static String removeFirstEmptyLine(String str) { if (str.startsWith(NL)) { return str.substring(NL.length()); diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index 2d1d0973..e2c66cba 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -152,13 +152,16 @@ public class InsnGen { } else { CodeWriter body = new CodeWriter(code.getIndent()); makeInsnBody(body, insn, state); - if (state.contains(InsnGenState.SKIP)) + if (state.contains(InsnGenState.SKIP)) { return false; + } + code.startLine(); + if (insn.getSourceLine() != 0) { + code.attachAnnotation(insn.getSourceLine()); + } if (insn.getResult() != null && !state.contains(InsnGenState.NO_RESULT)) - code.startLine(assignVar(insn)).add(" = "); - else - code.startLine(); + code.add(assignVar(insn)).add(" = "); code.add(body); diff --git a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java index c1d7d831..f0d48f11 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -108,6 +108,7 @@ public class MethodGen { annotationGen.addThrows(mth, code); } + code.attachAnnotation(mth); } public CodeWriter makeArguments(List args) { @@ -225,7 +226,7 @@ public class MethodGen { code.startLine("// jadx: method processing error"); Throwable cause = err.getCause(); if (cause != null) { - code.endl(); + code.newLine(); code.add("/*"); code.startLine("Error: ").add(Utils.getStackTrace(cause)); code.add("*/"); diff --git a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java index 5b0a75e5..4b802aa9 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java @@ -165,14 +165,14 @@ public class RegionGen extends InsnGen { case AND: case OR: String mode = condition.getMode() == IfCondition.MODE.AND ? " && " : " || "; - CodeWriter cw = new CodeWriter(); + StringBuilder sb = new StringBuilder(); for (IfCondition arg : condition.getArgs()) { - if (cw.notEmpty()) { - cw.add(mode); + if (sb.length() != 0) { + sb.append(mode); } - cw.add('(').add(makeCondition(arg)).add(')'); + sb.append('(').append(makeCondition(arg)).append(')'); } - return cw.toString(); + return sb.toString(); default: return "??" + condition.toString(); } diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/LineAttrNode.java b/jadx-core/src/main/java/jadx/core/dex/attributes/LineAttrNode.java new file mode 100644 index 00000000..bb41e7d2 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/LineAttrNode.java @@ -0,0 +1,24 @@ +package jadx.core.dex.attributes; + +public abstract class LineAttrNode extends AttrNode { + + private int sourceLine; + + private int decompiledLine; + + public int getSourceLine() { + return sourceLine; + } + + public void setSourceLine(int sourceLine) { + this.sourceLine = sourceLine; + } + + public int getDecompiledLine() { + return decompiledLine; + } + + public void setDecompiledLine(int decompiledLine) { + this.decompiledLine = decompiledLine; + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/info/AccessInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/AccessInfo.java index 1935bd39..48de6052 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/AccessInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/AccessInfo.java @@ -97,6 +97,10 @@ public class AccessInfo { return accFlags; } + public AFType getType() { + return type; + } + public String makeString() { StringBuilder code = new StringBuilder(); if (isPublic()) diff --git a/jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java index 2bf4a9ac..f8c75237 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java @@ -39,6 +39,26 @@ public class FieldInfo { return declClass; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FieldInfo fieldInfo = (FieldInfo) o; + if (!name.equals(fieldInfo.name)) return false; + if (!type.equals(fieldInfo.type)) return false; + if (!declClass.equals(fieldInfo.declClass)) return false; + return true; + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + type.hashCode(); + result = 31 * result + declClass.hashCode(); + return result; + } + @Override public String toString() { return declClass + "." + name + " " + type; diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index b219be44..84efa914 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -2,8 +2,8 @@ package jadx.core.dex.nodes; import jadx.core.Consts; import jadx.core.codegen.CodeWriter; -import jadx.core.dex.attributes.AttrNode; import jadx.core.dex.attributes.AttributeType; +import jadx.core.dex.attributes.LineAttrNode; import jadx.core.dex.attributes.SourceFileAttr; import jadx.core.dex.attributes.annotations.Annotation; import jadx.core.dex.info.AccessInfo; @@ -32,7 +32,7 @@ import com.android.dx.io.ClassData.Field; import com.android.dx.io.ClassData.Method; import com.android.dx.io.ClassDef; -public class ClassNode extends AttrNode implements ILoadable { +public class ClassNode extends LineAttrNode implements ILoadable { private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class); private final DexNode dex; diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/FieldNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/FieldNode.java index daba071c..c013e0b1 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/FieldNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/FieldNode.java @@ -1,6 +1,6 @@ package jadx.core.dex.nodes; -import jadx.core.dex.attributes.AttrNode; +import jadx.core.dex.attributes.LineAttrNode; import jadx.core.dex.info.AccessInfo; import jadx.core.dex.info.AccessInfo.AFType; import jadx.core.dex.info.FieldInfo; @@ -8,7 +8,7 @@ import jadx.core.dex.instructions.args.ArgType; import com.android.dx.io.ClassData.Field; -public class FieldNode extends AttrNode { +public class FieldNode extends LineAttrNode { private final FieldInfo fieldInfo; private final AccessInfo accFlags; @@ -41,6 +41,19 @@ public class FieldNode extends AttrNode { this.type = type; } + @Override + public int hashCode() { + return fieldInfo.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + FieldNode other = (FieldNode) obj; + return fieldInfo.equals(other.fieldInfo); + } + @Override public String toString() { return fieldInfo.getDeclClass() + "." + fieldInfo.getName() + " " + type; diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java index e6bd0f06..f1168f21 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java @@ -1,6 +1,6 @@ package jadx.core.dex.nodes; -import jadx.core.dex.attributes.AttrNode; +import jadx.core.dex.attributes.LineAttrNode; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; @@ -15,7 +15,7 @@ import java.util.List; import com.android.dx.io.instructions.DecodedInstruction; -public class InsnNode extends AttrNode { +public class InsnNode extends LineAttrNode { protected final InsnType insnType; diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java index 8dc6b19a..b5122b76 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java @@ -1,9 +1,9 @@ package jadx.core.dex.nodes; import jadx.core.Consts; -import jadx.core.dex.attributes.AttrNode; import jadx.core.dex.attributes.AttributeFlag; import jadx.core.dex.attributes.JumpAttribute; +import jadx.core.dex.attributes.LineAttrNode; import jadx.core.dex.attributes.LoopAttr; import jadx.core.dex.attributes.annotations.Annotation; import jadx.core.dex.info.AccessInfo; @@ -40,7 +40,7 @@ import com.android.dx.io.Code; import com.android.dx.io.Code.CatchHandler; import com.android.dx.io.Code.Try; -public class MethodNode extends AttrNode implements ILoadable { +public class MethodNode extends LineAttrNode implements ILoadable { private static final Logger LOG = LoggerFactory.getLogger(MethodNode.class); private final MethodInfo mthInfo; @@ -101,8 +101,17 @@ public class MethodNode extends AttrNode implements ILoadable { initTryCatches(mthCode, insnByOffset); initJumps(insnByOffset); - if (mthCode.getDebugInfoOffset() > 0) { - (new DebugInfoParser(this, mthCode.getDebugInfoOffset(), insnByOffset)).process(); + int debugInfoOffset = mthCode.getDebugInfoOffset(); + if (debugInfoOffset > 0) { + DebugInfoParser debugInfoParser = new DebugInfoParser(this, debugInfoOffset, insnByOffset); + debugInfoParser.process(); + + if (instructions.size() != 0) { + int line = instructions.get(0).getSourceLine(); + if (line != 0) { + this.setSourceLine(line - 1); + } + } } } catch (Exception e) { throw new DecodeException(this, "Load method exception", e); @@ -414,7 +423,7 @@ public class MethodNode extends AttrNode implements ILoadable { } public void registerLoop(LoopAttr loop) { - if(loops.isEmpty()) { + if (loops.isEmpty()) { loops = new ArrayList(5); } loops.add(loop); @@ -422,7 +431,7 @@ public class MethodNode extends AttrNode implements ILoadable { public LoopAttr getLoopForBlock(BlockNode block) { for (LoopAttr loop : loops) { - if(loop.getLoopBlocks().contains(block)) + if (loop.getLoopBlocks().contains(block)) return loop; } return null; @@ -477,6 +486,19 @@ public class MethodNode extends AttrNode implements ILoadable { return mthInfo; } + @Override + public int hashCode() { + return mthInfo.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + MethodNode other = (MethodNode) obj; + return mthInfo.equals(other.mthInfo); + } + @Override public String toString() { return retType diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java index fa0a7694..8524a7ad 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java @@ -49,9 +49,8 @@ public class DebugInfoParser { public void process() throws DecodeException { int addr = 0; - int line; + int line = section.readUleb128(); - line = section.readUleb128(); int param_size = section.readUleb128(); // exclude 'this' List mthArgs = mth.getArguments(false); assert param_size == mthArgs.size(); @@ -70,14 +69,14 @@ public class DebugInfoParser { activeRegisters[rn] = arg; } - addrChange(-1, 1); // process '0' instruction + addrChange(-1, 1, line); // process '0' instruction int c = section.readByte() & 0xFF; while (c != DBG_END_SEQUENCE) { switch (c) { case DBG_ADVANCE_PC: { int addrInc = section.readUleb128(); - addr = addrChange(addr, addrInc); + addr = addrChange(addr, addrInc, line); break; } case DBG_ADVANCE_LINE: { @@ -141,14 +140,13 @@ public class DebugInfoParser { int adjusted_opcode = c - DBG_FIRST_SPECIAL; line += DBG_LINE_BASE + (adjusted_opcode % DBG_LINE_RANGE); int addrInc = (adjusted_opcode / DBG_LINE_RANGE); - addr = addrChange(addr, addrInc); + addr = addrChange(addr, addrInc, line); } else { throw new DecodeException("Unknown debug insn code: " + c); } break; } } - c = section.readByte() & 0xFF; } @@ -160,13 +158,14 @@ public class DebugInfoParser { } } - private int addrChange(int addr, int addrInc) { + private int addrChange(int addr, int addrInc, int line) { int newAddr = addr + addrInc; for (int i = addr + 1; i <= newAddr; i++) { InsnNode insn = insnByOffset[i]; if (insn == null) continue; + insn.setSourceLine(line); for (InsnArg arg : insn.getArguments()) if (arg.isRegister()) { activeRegisters[arg.getRegNum()] = arg; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java index d4e1f86b..7bc3cf05 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java @@ -168,7 +168,7 @@ public class DotGraphVisitor extends AbstractVisitor { } else { CodeWriter code = new CodeWriter(0); MethodGen.makeFallbackInsns(code, mth, block.getInstructions(), false); - String str = escape(code.endl().toString()); + String str = escape(code.newLine().toString()); if (str.startsWith(NL)) str = str.substring(NL.length()); return str;