core: store line info, add fields and methods to api, refactoring

This commit is contained in:
Skylot 2013-07-23 22:59:00 +04:00
parent 36da79feb8
commit 25b2c8fe5b
19 changed files with 315 additions and 61 deletions

View File

@ -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<JavaPackage> 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 {

View File

@ -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<JavaClass> innerClasses;
private final List<JavaField> fields;
private final List<JavaMethod> 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<JavaClass> list = new ArrayList<JavaClass>(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<JavaField> flds = new ArrayList<JavaField>(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<JavaMethod> mths = new ArrayList<JavaMethod>(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<JavaClass> getInnerClasses() {
return innerClasses;
}
public List<JavaField> getFields() {
return fields;
}
public List<JavaMethod> getMethods() {
return methods;
}
@Override
public String toString() {
return getFullName();
}
public int getDecompiledLine() {
return cls.getDecompiledLine();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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<ArgType, List<ArgType>> 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;
}

View File

@ -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;
}

View File

@ -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<Object, Integer> 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<Object, Integer> 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<Object, Integer>();
}
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<Object, Integer> 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());

View File

@ -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);

View File

@ -108,6 +108,7 @@ public class MethodGen {
annotationGen.addThrows(mth, code);
}
code.attachAnnotation(mth);
}
public CodeWriter makeArguments(List<RegisterArg> 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("*/");

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -97,6 +97,10 @@ public class AccessInfo {
return accFlags;
}
public AFType getType() {
return type;
}
public String makeString() {
StringBuilder code = new StringBuilder();
if (isPublic())

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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<LoopAttr>(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

View File

@ -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<RegisterArg> 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;

View File

@ -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;