Commit 2a60ac47 authored by Skylot's avatar Skylot

core: annotate generated code with reference to used methods

parent 9cd72fe1
package jadx.api;
public final class CodePosition {
private final JavaClass cls;
private final int line;
private final int offset;
public CodePosition(JavaClass cls, int line, int offset) {
this.cls = cls;
this.line = line;
this.offset = offset;
}
public CodePosition(int line, int offset) {
this.cls = null;
this.line = line;
this.offset = offset;
}
public JavaClass getJavaClass() {
return cls;
}
public int getLine() {
return line;
}
public int getOffset() {
return offset;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CodePosition that = (CodePosition) o;
return line == that.line && offset == that.offset;
}
@Override
public int hashCode() {
return line + 31 * offset;
}
@Override
public String toString() {
return line + ":" + offset + (cls != null ? " " + cls : "");
}
}
...@@ -2,6 +2,7 @@ package jadx.api; ...@@ -2,6 +2,7 @@ package jadx.api;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.dex.attributes.AttributeFlag; import jadx.core.dex.attributes.AttributeFlag;
import jadx.core.dex.attributes.LineAttrNode;
import jadx.core.dex.info.AccessInfo; import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.FieldNode;
...@@ -12,6 +13,7 @@ import java.util.ArrayList; ...@@ -12,6 +13,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map;
public final class JavaClass { public final class JavaClass {
...@@ -27,13 +29,24 @@ public final class JavaClass { ...@@ -27,13 +29,24 @@ public final class JavaClass {
this.cls = classNode; this.cls = classNode;
} }
public String getCode() {
CodeWriter code = cls.getCode();
if (code == null) {
decompile();
code = cls.getCode();
}
return code != null ? code.toString() : "error processing class";
}
public void decompile() { public void decompile() {
if (decompiler == null) { if (decompiler == null) {
throw new JadxRuntimeException("Can't decompile inner class"); throw new JadxRuntimeException("Can't decompile inner class");
} }
if (cls.getCode() == null) {
decompiler.processClass(cls); decompiler.processClass(cls);
load(); load();
} }
}
private void load() { private void load() {
int inClsCount = cls.getInnerClasses().size(); int inClsCount = cls.getInnerClasses().size();
...@@ -78,13 +91,32 @@ public final class JavaClass { ...@@ -78,13 +91,32 @@ public final class JavaClass {
} }
} }
public String getCode() { private Map<CodePosition, Object> getCodeAnnotations() {
CodeWriter code = cls.getCode(); getCode();
if (code == null) { return cls.getCode().getAnnotations();
decompile();
code = cls.getCode();
} }
return code != null ? code.toString() : "error processing class";
public CodePosition getDefinitionPosition(int line, int offset) {
Map<CodePosition, Object> map = getCodeAnnotations();
Object obj = map.get(new CodePosition(line, offset));
if (obj instanceof LineAttrNode) {
ClassNode clsNode = null;
if (obj instanceof ClassNode) {
clsNode = (ClassNode) obj;
} else if (obj instanceof MethodNode) {
clsNode = ((MethodNode) obj).getParentClass();
} else if (obj instanceof FieldNode) {
clsNode = ((FieldNode) obj).getParentClass();
}
if (clsNode != null) {
clsNode = clsNode.getParentClass();
JavaClass jCls = new JavaClass(decompiler, clsNode);
jCls.decompile();
int defLine = ((LineAttrNode) obj).getDecompiledLine();
return new CodePosition(jCls, defLine, 0);
}
}
return null;
} }
public String getFullName() { public String getFullName() {
...@@ -115,12 +147,22 @@ public final class JavaClass { ...@@ -115,12 +147,22 @@ public final class JavaClass {
return methods; return methods;
} }
public int getDecompiledLine() {
return cls.getDecompiledLine();
}
@Override @Override
public String toString() { public boolean equals(Object o) {
return getFullName(); return this == o || o instanceof JavaClass && cls.equals(((JavaClass) o).cls);
} }
public int getDecompiledLine() { @Override
return cls.getDecompiledLine(); public int hashCode() {
return cls.hashCode();
}
@Override
public String toString() {
return getFullName();
} }
} }
...@@ -12,6 +12,7 @@ import jadx.core.dex.visitors.FallbackModeVisitor; ...@@ -12,6 +12,7 @@ import jadx.core.dex.visitors.FallbackModeVisitor;
import jadx.core.dex.visitors.IDexTreeVisitor; import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.dex.visitors.MethodInlineVisitor; import jadx.core.dex.visitors.MethodInlineVisitor;
import jadx.core.dex.visitors.ModVisitor; import jadx.core.dex.visitors.ModVisitor;
import jadx.core.dex.visitors.PrepareForCodeGen;
import jadx.core.dex.visitors.SimplifyVisitor; import jadx.core.dex.visitors.SimplifyVisitor;
import jadx.core.dex.visitors.regions.CheckRegions; import jadx.core.dex.visitors.regions.CheckRegions;
import jadx.core.dex.visitors.regions.ProcessVariables; import jadx.core.dex.visitors.regions.ProcessVariables;
...@@ -82,6 +83,7 @@ public class Jadx { ...@@ -82,6 +83,7 @@ public class Jadx {
passes.add(new MethodInlineVisitor()); passes.add(new MethodInlineVisitor());
passes.add(new ClassModifier()); passes.add(new ClassModifier());
passes.add(new PrepareForCodeGen());
} }
passes.add(new CodeGen(args)); passes.add(new CodeGen(args));
return passes; return passes;
......
...@@ -47,7 +47,7 @@ public class AnnotationGen { ...@@ -47,7 +47,7 @@ public class AnnotationGen {
return; return;
} }
for (Annotation a : aList.getAll()) { for (Annotation a : aList.getAll()) {
code.add(formatAnnotation(a)); formatAnnotation(code, a);
code.add(' '); code.add(' ');
} }
} }
...@@ -66,26 +66,25 @@ public class AnnotationGen { ...@@ -66,26 +66,25 @@ public class AnnotationGen {
} }
} else { } else {
code.startLine(); code.startLine();
code.add(formatAnnotation(a)); formatAnnotation(code, a);
} }
} }
} }
private CodeWriter formatAnnotation(Annotation a) { private void formatAnnotation(CodeWriter code, Annotation a) {
CodeWriter code = new CodeWriter();
code.add('@'); code.add('@');
code.add(classGen.useClass(a.getType())); code.add(classGen.useClass(a.getType()));
Map<String, Object> vl = a.getValues(); Map<String, Object> vl = a.getValues();
if (!vl.isEmpty()) { if (!vl.isEmpty()) {
code.add('('); code.add('(');
if (vl.size() == 1 && vl.containsKey("value")) { if (vl.size() == 1 && vl.containsKey("value")) {
code.add(encValueToString(vl.get("value"))); encodeValue(code, vl.get("value"));
} else { } else {
for (Iterator<Entry<String, Object>> it = vl.entrySet().iterator(); it.hasNext(); ) { for (Iterator<Entry<String, Object>> it = vl.entrySet().iterator(); it.hasNext(); ) {
Entry<String, Object> e = it.next(); Entry<String, Object> e = it.next();
code.add(e.getKey()); code.add(e.getKey());
code.add(" = "); code.add(" = ");
code.add(encValueToString(e.getValue())); encodeValue(code, e.getValue());
if (it.hasNext()) { if (it.hasNext()) {
code.add(", "); code.add(", ");
} }
...@@ -93,7 +92,6 @@ public class AnnotationGen { ...@@ -93,7 +92,6 @@ public class AnnotationGen {
} }
code.add(')'); code.add(')');
} }
return code;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
...@@ -122,70 +120,52 @@ public class AnnotationGen { ...@@ -122,70 +120,52 @@ public class AnnotationGen {
} }
// TODO: refactor this boilerplate code // TODO: refactor this boilerplate code
@SuppressWarnings("unchecked") public void encodeValue(CodeWriter code, Object val) {
public String encValueToString(Object val) {
if (val == null) { if (val == null) {
return "null"; code.add("null");
return;
} }
if (val instanceof String) { if (val instanceof String) {
return StringUtils.unescapeString((String) val); code.add(StringUtils.unescapeString((String) val));
} } else if (val instanceof Integer) {
if (val instanceof Integer) { code.add(TypeGen.formatInteger((Integer) val));
return TypeGen.formatInteger((Integer) val); } else if (val instanceof Character) {
} code.add(StringUtils.unescapeChar((Character) val));
if (val instanceof Character) { } else if (val instanceof Boolean) {
return StringUtils.unescapeChar((Character) val); code.add(Boolean.TRUE.equals(val) ? "true" : "false");
} } else if (val instanceof Float) {
if (val instanceof Boolean) { code.add(TypeGen.formatFloat((Float) val));
return Boolean.TRUE.equals(val) ? "true" : "false"; } else if (val instanceof Double) {
} code.add(TypeGen.formatDouble((Double) val));
if (val instanceof Float) { } else if (val instanceof Long) {
return TypeGen.formatFloat((Float) val); code.add(TypeGen.formatLong((Long) val));
} } else if (val instanceof Short) {
if (val instanceof Double) { code.add(TypeGen.formatShort((Short) val));
return TypeGen.formatDouble((Double) val); } else if (val instanceof Byte) {
} code.add(TypeGen.formatByte((Byte) val));
if (val instanceof Long) { } else if (val instanceof ArgType) {
return TypeGen.formatLong((Long) val); code.add(TypeGen.translate(classGen, (ArgType) val)).add(".class");
} } else if (val instanceof FieldInfo) {
if (val instanceof Short) {
return TypeGen.formatShort((Short) val);
}
if (val instanceof Byte) {
return TypeGen.formatByte((Byte) val);
}
if (val instanceof ArgType) {
return TypeGen.translate(classGen, (ArgType) val) + ".class";
}
if (val instanceof FieldInfo) {
// must be a static field // must be a static field
FieldInfo field = (FieldInfo) val; FieldInfo field = (FieldInfo) val;
// FIXME: !!code from InsnGen.sfield code.add(InsnGen.makeStaticFieldAccess(field, classGen));
String thisClass = cls.getFullName(); } else if (val instanceof List) {
if (field.getDeclClass().getFullName().equals(thisClass)) { code.add('{');
return field.getName(); List list = (List) val;
} else { Iterator it = list.iterator();
return classGen.useClass(field.getDeclClass()) + '.' + field.getName(); while (it.hasNext()) {
}
}
if (val instanceof List) {
StringBuilder str = new StringBuilder();
str.append('{');
List<Object> list = (List<Object>) val;
for (Iterator<Object> it = list.iterator(); it.hasNext(); ) {
Object obj = it.next(); Object obj = it.next();
str.append(encValueToString(obj)); encodeValue(code, obj);
if (it.hasNext()) { if (it.hasNext()) {
str.append(", "); code.add(", ");
}
}
str.append('}');
return str.toString();
} }
if (val instanceof Annotation) {
return formatAnnotation((Annotation) val).toString();
} }
code.add('}');
} else if (val instanceof Annotation) {
formatAnnotation(code, (Annotation) val);
} else {
// TODO: also can be method values // TODO: also can be method values
throw new JadxRuntimeException("Can't decode value: " + val + " (" + val.getClass() + ")"); throw new JadxRuntimeException("Can't decode value: " + val + " (" + val.getClass() + ")");
} }
}
} }
...@@ -151,7 +151,7 @@ public class ClassGen { ...@@ -151,7 +151,7 @@ public class ClassGen {
} }
} }
clsCode.attachAnnotation(cls); clsCode.attachDefinition(cls);
} }
public boolean makeGenericMap(CodeWriter code, Map<ArgType, List<ArgType>> gmap) { public boolean makeGenericMap(CodeWriter code, Map<ArgType, List<ArgType>> gmap) {
...@@ -228,8 +228,8 @@ public class ClassGen { ...@@ -228,8 +228,8 @@ public class ClassGen {
if (cls.getAccessFlags().isAnnotation()) { if (cls.getAccessFlags().isAnnotation()) {
Object def = annotationGen.getAnnotationDefaultValue(mth.getName()); Object def = annotationGen.getAnnotationDefaultValue(mth.getName());
if (def != null) { if (def != null) {
String v = annotationGen.encValueToString(def); code.add(" default ");
code.add(" default ").add(v); annotationGen.encodeValue(code, def);
} }
} }
code.add(';'); code.add(';');
...@@ -282,11 +282,11 @@ public class ClassGen { ...@@ -282,11 +282,11 @@ public class ClassGen {
if (fv.getValue() == null) { if (fv.getValue() == null) {
code.add(TypeGen.literalToString(0, f.getType())); code.add(TypeGen.literalToString(0, f.getType()));
} else { } else {
code.add(annotationGen.encValueToString(fv.getValue())); annotationGen.encodeValue(code, fv.getValue());
} }
} }
code.add(';'); code.add(';');
code.attachAnnotation(f); code.attachDefinition(f);
} }
return code; return code;
} }
......
package jadx.core.codegen; package jadx.core.codegen;
import jadx.api.CodePosition;
import jadx.core.dex.attributes.LineAttrNode; import jadx.core.dex.attributes.LineAttrNode;
import jadx.core.utils.Utils; import jadx.core.utils.Utils;
...@@ -7,6 +8,7 @@ import java.io.File; ...@@ -7,6 +8,7 @@ import java.io.File;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import org.slf4j.Logger; import org.slf4j.Logger;
...@@ -17,7 +19,7 @@ public class CodeWriter { ...@@ -17,7 +19,7 @@ public class CodeWriter {
private static final int MAX_FILENAME_LENGTH = 128; private static final int MAX_FILENAME_LENGTH = 128;
public static final String NL = System.getProperty("line.separator"); public static final String NL = System.getProperty("line.separator");
public static final String INDENT = "\t"; public static final String INDENT = " ";
private static final String[] INDENT_CACHE = { private static final String[] INDENT_CACHE = {
"", "",
...@@ -33,7 +35,8 @@ public class CodeWriter { ...@@ -33,7 +35,8 @@ public class CodeWriter {
private int indent; private int indent;
private int line = 1; private int line = 1;
private Map<Object, Integer> annotations = Collections.emptyMap(); private int offset = 0;
private Map<CodePosition, Object> annotations = Collections.emptyMap();
public CodeWriter() { public CodeWriter() {
this.indent = 0; this.indent = 0;
...@@ -47,56 +50,66 @@ public class CodeWriter { ...@@ -47,56 +50,66 @@ public class CodeWriter {
public CodeWriter startLine() { public CodeWriter startLine() {
addLine(); addLine();
buf.append(indentStr); addIndent();
return this; return this;
} }
public CodeWriter startLine(char c) { public CodeWriter startLine(char c) {
addLine(); addLine();
buf.append(indentStr); addIndent();
buf.append(c); add(c);
return this; return this;
} }
public CodeWriter startLine(String str) { public CodeWriter startLine(String str) {
addLine(); addLine();
buf.append(indentStr); addIndent();
buf.append(str); add(str);
return this; return this;
} }
public CodeWriter startLine(int ind, String str) { public CodeWriter startLine(int ind, String str) {
addLine(); addLine();
buf.append(indentStr); addIndent();
for (int i = 0; i < ind; i++) { for (int i = 0; i < ind; i++) {
buf.append(INDENT); addIndent();
} }
buf.append(str); add(str);
return this; return this;
} }
public CodeWriter add(Object obj) { public CodeWriter add(Object obj) {
buf.append(obj); add(obj.toString());
return this; return this;
} }
public CodeWriter add(String str) { public CodeWriter add(String str) {
buf.append(str); buf.append(str);
offset += str.length();
return this; return this;
} }
public CodeWriter add(char c) { public CodeWriter add(char c) {
buf.append(c); buf.append(c);
offset++;
return this; return this;
} }
@Deprecated
public CodeWriter add(CodeWriter code) { public CodeWriter add(CodeWriter code) {
line--; line--;
for (Map.Entry<Object, Integer> entry : code.annotations.entrySet()) { for (Map.Entry<CodePosition, Object> entry : code.annotations.entrySet()) {
attachAnnotation(entry.getKey(), line + entry.getValue()); CodePosition pos = entry.getKey();
attachAnnotation(entry.getValue(), new CodePosition(line + pos.getLine(), pos.getOffset()));
} }
line += code.line; line += code.line;
buf.append(code); String str = code.toString();
buf.append(str);
if (str.contains(NL)) {
offset = code.offset;
} else {
offset += code.offset;
}
return this; return this;
} }
...@@ -108,25 +121,12 @@ public class CodeWriter { ...@@ -108,25 +121,12 @@ public class CodeWriter {
private void addLine() { private void addLine() {
buf.append(NL); buf.append(NL);
line++; line++;
offset = 0;
} }
public int getLine() { public CodeWriter addIndent() {
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); buf.append(indentStr);
offset += indentStr.length();
return this; return this;
} }
...@@ -169,16 +169,49 @@ public class CodeWriter { ...@@ -169,16 +169,49 @@ public class CodeWriter {
updateIndent(); updateIndent();
} }
private static class DefinitionWrapper {
private final LineAttrNode node;
private DefinitionWrapper(LineAttrNode node) {
this.node = node;
}
public LineAttrNode getNode() {
return node;
}
}
public Object attachDefinition(LineAttrNode obj) {
return attachAnnotation(new DefinitionWrapper(obj), new CodePosition(line, offset));
}
public Object attachAnnotation(Object obj) {
return attachAnnotation(obj, new CodePosition(line, offset + 1));
}
private Object attachAnnotation(Object obj, CodePosition pos) {
if (annotations.isEmpty()) {
annotations = new HashMap<CodePosition, Object>();
}
return annotations.put(pos, obj);
}
public Map<CodePosition, Object> getAnnotations() {
return annotations;
}
public void finish() { public void finish() {
buf.trimToSize(); buf.trimToSize();
for (Map.Entry<Object, Integer> entry : annotations.entrySet()) { Iterator<Map.Entry<CodePosition, Object>> it = annotations.entrySet().iterator();
Object v = entry.getKey(); while (it.hasNext()) {
if (v instanceof LineAttrNode) { Map.Entry<CodePosition, Object> entry = it.next();
LineAttrNode l = (LineAttrNode) v; Object v = entry.getValue();
l.setDecompiledLine(entry.getValue()); if (v instanceof DefinitionWrapper) {
LineAttrNode l = ((DefinitionWrapper) v).getNode();
l.setDecompiledLine(entry.getKey().getLine());
it.remove();
} }
} }
annotations.clear();
} }
private static String removeFirstEmptyLine(String str) { private static String removeFirstEmptyLine(String str) {
......
...@@ -61,13 +61,13 @@ public class MethodGen { ...@@ -61,13 +61,13 @@ public class MethodGen {
public boolean addDefinition(CodeWriter code) { public boolean addDefinition(CodeWriter code) {
if (mth.getMethodInfo().isClassInit()) { if (mth.getMethodInfo().isClassInit()) {
code.startLine("static"); code.startLine("static");
code.attachAnnotation(mth); code.attachDefinition(mth);
return true; return true;
} }
if (mth.getAttributes().contains(AttributeFlag.ANONYMOUS_CONSTRUCTOR)) { if (mth.getAttributes().contains(AttributeFlag.ANONYMOUS_CONSTRUCTOR)) {
// don't add method name and arguments // don't add method name and arguments
code.startLine(); code.startLine();
code.attachAnnotation(mth); code.attachDefinition(mth);
return false; return false;
} }
annotationGen.addForMethod(code, mth); annotationGen.addForMethod(code, mth);
...@@ -110,17 +110,15 @@ public class MethodGen { ...@@ -110,17 +110,15 @@ public class MethodGen {
)); ));
} }
} }
code.add(makeArguments(args)); addMethodArguments(code, args);
code.add(")"); code.add(')');
annotationGen.addThrows(mth, code); annotationGen.addThrows(mth, code);
code.attachAnnotation(mth); code.attachDefinition(mth);
return true; return true;
} }
public CodeWriter makeArguments(List<RegisterArg> args) { private void addMethodArguments(CodeWriter argsCode, List<RegisterArg> args) {
CodeWriter argsCode = new CodeWriter();
MethodParameters paramsAnnotation = MethodParameters paramsAnnotation =
(MethodParameters) mth.getAttributes().get(AttributeType.ANNOTATION_MTH_PARAMETERS); (MethodParameters) mth.getAttributes().get(AttributeType.ANNOTATION_MTH_PARAMETERS);
...@@ -154,7 +152,6 @@ public class MethodGen { ...@@ -154,7 +152,6 @@ public class MethodGen {
argsCode.add(", "); argsCode.add(", ");
} }
} }
return argsCode;
} }
/** /**
......
...@@ -51,7 +51,7 @@ public class ArithNode extends InsnNode { ...@@ -51,7 +51,7 @@ public class ArithNode extends InsnNode {
} }
public ArithNode(ArithOp op, RegisterArg res, InsnArg a) { public ArithNode(ArithOp op, RegisterArg res, InsnArg a) {
super(InsnType.ARITH, 1); super(InsnType.ARITH_ONEARG, 1);
this.op = op; this.op = op;
setResult(res); setResult(res);
addArg(a); addArg(a);
......
...@@ -54,6 +54,7 @@ public enum InsnType { ...@@ -54,6 +54,7 @@ public enum InsnType {
CONTINUE, CONTINUE,
STR_CONCAT, // strings concatenation STR_CONCAT, // strings concatenation
ARITH_ONEARG,
TERNARY, TERNARY,
ARGS, // just generate arguments ARGS, // just generate arguments
......
...@@ -50,7 +50,10 @@ public class ClassNode extends LineAttrNode implements ILoadable { ...@@ -50,7 +50,10 @@ public class ClassNode extends LineAttrNode implements ILoadable {
private Map<Object, FieldNode> constFields = Collections.emptyMap(); private Map<Object, FieldNode> constFields = Collections.emptyMap();
private List<ClassNode> innerClasses = Collections.emptyList(); private List<ClassNode> innerClasses = Collections.emptyList();
// store decompiled code
private CodeWriter code; private CodeWriter code;
// store parent for inner classes or 'this' otherwise
private ClassNode parentClass;
public ClassNode(DexNode dex, ClassDef cls) throws DecodeException { public ClassNode(DexNode dex, ClassDef cls) throws DecodeException {
this.dex = dex; this.dex = dex;
...@@ -332,6 +335,19 @@ public class ClassNode extends LineAttrNode implements ILoadable { ...@@ -332,6 +335,19 @@ public class ClassNode extends LineAttrNode implements ILoadable {
return searchMethodByName(MethodInfo.fromDex(dex, id).getShortId()); return searchMethodByName(MethodInfo.fromDex(dex, id).getShortId());
} }
public ClassNode getParentClass() {
if (parentClass == null) {
if (clsInfo.isInner()) {
ClassNode parent = dex().resolveClass(clsInfo.getParentClass());
parent = parent == null ? this : parent;
parentClass = parent;
} else {
parentClass = this;
}
}
return parentClass;
}
public List<ClassNode> getInnerClasses() { public List<ClassNode> getInnerClasses() {
return innerClasses; return innerClasses;
} }
......
...@@ -10,12 +10,14 @@ import com.android.dx.io.ClassData.Field; ...@@ -10,12 +10,14 @@ import com.android.dx.io.ClassData.Field;
public class FieldNode extends LineAttrNode { public class FieldNode extends LineAttrNode {
private final ClassNode parent;
private final FieldInfo fieldInfo; private final FieldInfo fieldInfo;
private final AccessInfo accFlags; private final AccessInfo accFlags;
private ArgType type; // store signature private ArgType type; // store signature
public FieldNode(ClassNode cls, Field field) { public FieldNode(ClassNode cls, Field field) {
this.parent = cls;
this.fieldInfo = FieldInfo.fromDex(cls.dex(), field.getFieldIndex()); this.fieldInfo = FieldInfo.fromDex(cls.dex(), field.getFieldIndex());
this.type = fieldInfo.getType(); this.type = fieldInfo.getType();
this.accFlags = new AccessInfo(field.getAccessFlags(), AFType.FIELD); this.accFlags = new AccessInfo(field.getAccessFlags(), AFType.FIELD);
...@@ -41,6 +43,10 @@ public class FieldNode extends LineAttrNode { ...@@ -41,6 +43,10 @@ public class FieldNode extends LineAttrNode {
this.type = type; this.type = type;
} }
public ClassNode getParentClass() {
return parent;
}
@Override @Override
public int hashCode() { public int hashCode() {
return fieldInfo.hashCode(); return fieldInfo.hashCode();
......
...@@ -159,7 +159,7 @@ public class ClassModifier extends AbstractVisitor { ...@@ -159,7 +159,7 @@ public class ClassModifier extends AbstractVisitor {
&& af.isPublic() && af.isPublic()
&& mth.getArguments(false).isEmpty()) { && mth.getArguments(false).isEmpty()) {
List<BlockNode> bb = mth.getBasicBlocks(); List<BlockNode> bb = mth.getBasicBlocks();
if (bb.isEmpty() || allBlocksEmpty(bb)) { if (bb == null || bb.isEmpty() || allBlocksEmpty(bb)) {
mth.getAttributes().add(AttributeFlag.DONT_GENERATE); mth.getAttributes().add(AttributeFlag.DONT_GENERATE);
} }
} }
......
package jadx.core.dex.visitors;
import jadx.core.dex.instructions.ArithNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.mods.ConstructorInsn;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.exceptions.JadxException;
import java.util.Iterator;
import java.util.List;
public class PrepareForCodeGen extends AbstractVisitor {
@Override
public void visit(MethodNode mth) throws JadxException {
List<BlockNode> blocks = mth.getBasicBlocks();
if (blocks == null) {
return;
}
for (BlockNode block : blocks) {
removeInstructions(block);
modifyArith(block);
}
}
private static void removeInstructions(BlockNode block) {
Iterator<InsnNode> it = block.getInstructions().iterator();
while (it.hasNext()) {
InsnNode insn = it.next();
switch (insn.getType()) {
case NOP:
case MONITOR_ENTER:
case MONITOR_EXIT:
it.remove();
break;
case CONSTRUCTOR:
ConstructorInsn co = (ConstructorInsn) insn;
if (co.isSelf()) {
it.remove();
}
break;
}
}
}
private static void modifyArith(BlockNode block) {
List<InsnNode> list = block.getInstructions();
for (int i = 0; i < list.size(); i++) {
InsnNode insn = list.get(i);
if (insn.getType() == InsnType.ARITH) {
ArithNode arith = (ArithNode) insn;
RegisterArg res = arith.getResult();
InsnArg arg = arith.getArg(0);
if (res.equals(arg)) {
ArithNode newArith = new ArithNode(arith.getOp(), res, arith.getArg(1));
list.set(i, newArith);
}
}
}
}
}
package jadx.tests.internal; package jadx.tests.internal;
import jadx.api.InternalJadxTest; import jadx.api.InternalJadxTest;
import jadx.core.dex.instructions.ArithNode;
import jadx.core.dex.instructions.ArithOp;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import java.util.List;
import org.junit.Test; import org.junit.Test;
import static junit.framework.Assert.assertEquals;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
...@@ -39,15 +31,9 @@ public class TestFieldIncrement extends InternalJadxTest { ...@@ -39,15 +31,9 @@ public class TestFieldIncrement extends InternalJadxTest {
@Test @Test
public void test() { public void test() {
ClassNode cls = getClassNode(TestCls.class); ClassNode cls = getClassNode(TestCls.class);
MethodNode mth = getMethod(cls, "method");
List<InsnNode> insns = mth.getBasicBlocks().get(0).getInstructions();
assertEquals(insns.size(), 1);
InsnNode insnNode = insns.get(0);
assertEquals(InsnType.ARITH, insnNode.getType());
assertEquals(ArithOp.ADD, ((ArithNode) insnNode).getOp());
String code = cls.getCode().toString(); String code = cls.getCode().toString();
System.out.println(code);
assertThat(code, containsString("instanceField++;")); assertThat(code, containsString("instanceField++;"));
assertThat(code, containsString("staticField--;")); assertThat(code, containsString("staticField--;"));
assertThat(code, containsString("result += s + '_';")); assertThat(code, containsString("result += s + '_';"));
......
...@@ -37,5 +37,7 @@ public class TestSynchronized extends InternalJadxTest { ...@@ -37,5 +37,7 @@ public class TestSynchronized extends InternalJadxTest {
assertThat(code, containsString("public synchronized boolean test1() {")); assertThat(code, containsString("public synchronized boolean test1() {"));
assertThat(code, containsString("return this.f")); assertThat(code, containsString("return this.f"));
assertThat(code, containsString("synchronized (this.o) {")); assertThat(code, containsString("synchronized (this.o) {"));
assertThat(code, not(containsString(makeIndent(3) + ";")));
} }
} }
package jadx.tests.internal.arith;
import jadx.api.InternalJadxTest;
import jadx.core.dex.nodes.ClassNode;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThat;
public class TestArith extends InternalJadxTest {
public static class TestCls {
public void method(int a) {
a += 2;
}
public void method2(int a) {
a++;
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
assertThat(code, containsString("a += 2;"));
assertThat(code, containsString("a++;"));
}
}
package jadx.tests.internal.arith;
import jadx.api.InternalJadxTest;
import jadx.core.dex.nodes.ClassNode;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;
public class TestArith2 extends InternalJadxTest {
public static class TestCls {
public int test1(int a) {
return (a + 2) * 3;
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
assertThat(code, containsString("return (a + 2) * 3;"));
assertThat(code, not(containsString("a + 2 * 3")));
}
}
package jadx.tests.internal; package jadx.tests.internal.debuginfo;
import jadx.api.InternalJadxTest; import jadx.api.InternalJadxTest;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment