提交 2a60ac47 编写于 作者: S Skylot

core: annotate generated code with reference to used methods

上级 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;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.attributes.AttributeFlag;
import jadx.core.dex.attributes.LineAttrNode;
import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
......@@ -12,6 +13,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
public final class JavaClass {
......@@ -27,12 +29,23 @@ public final class JavaClass {
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() {
if (decompiler == null) {
throw new JadxRuntimeException("Can't decompile inner class");
}
decompiler.processClass(cls);
load();
if (cls.getCode() == null) {
decompiler.processClass(cls);
load();
}
}
private void load() {
......@@ -78,13 +91,32 @@ public final class JavaClass {
}
}
public String getCode() {
CodeWriter code = cls.getCode();
if (code == null) {
decompile();
code = cls.getCode();
private Map<CodePosition, Object> getCodeAnnotations() {
getCode();
return cls.getCode().getAnnotations();
}
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 code != null ? code.toString() : "error processing class";
return null;
}
public String getFullName() {
......@@ -115,12 +147,22 @@ public final class JavaClass {
return methods;
}
public int getDecompiledLine() {
return cls.getDecompiledLine();
}
@Override
public String toString() {
return getFullName();
public boolean equals(Object o) {
return this == o || o instanceof JavaClass && cls.equals(((JavaClass) o).cls);
}
public int getDecompiledLine() {
return cls.getDecompiledLine();
@Override
public int hashCode() {
return cls.hashCode();
}
@Override
public String toString() {
return getFullName();
}
}
......@@ -12,6 +12,7 @@ import jadx.core.dex.visitors.FallbackModeVisitor;
import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.dex.visitors.MethodInlineVisitor;
import jadx.core.dex.visitors.ModVisitor;
import jadx.core.dex.visitors.PrepareForCodeGen;
import jadx.core.dex.visitors.SimplifyVisitor;
import jadx.core.dex.visitors.regions.CheckRegions;
import jadx.core.dex.visitors.regions.ProcessVariables;
......@@ -82,6 +83,7 @@ public class Jadx {
passes.add(new MethodInlineVisitor());
passes.add(new ClassModifier());
passes.add(new PrepareForCodeGen());
}
passes.add(new CodeGen(args));
return passes;
......
......@@ -47,7 +47,7 @@ public class AnnotationGen {
return;
}
for (Annotation a : aList.getAll()) {
code.add(formatAnnotation(a));
formatAnnotation(code, a);
code.add(' ');
}
}
......@@ -66,26 +66,25 @@ public class AnnotationGen {
}
} else {
code.startLine();
code.add(formatAnnotation(a));
formatAnnotation(code, a);
}
}
}
private CodeWriter formatAnnotation(Annotation a) {
CodeWriter code = new CodeWriter();
private void formatAnnotation(CodeWriter code, Annotation a) {
code.add('@');
code.add(classGen.useClass(a.getType()));
Map<String, Object> vl = a.getValues();
if (!vl.isEmpty()) {
code.add('(');
if (vl.size() == 1 && vl.containsKey("value")) {
code.add(encValueToString(vl.get("value")));
encodeValue(code, vl.get("value"));
} else {
for (Iterator<Entry<String, Object>> it = vl.entrySet().iterator(); it.hasNext(); ) {
Entry<String, Object> e = it.next();
code.add(e.getKey());
code.add(" = ");
code.add(encValueToString(e.getValue()));
encodeValue(code, e.getValue());
if (it.hasNext()) {
code.add(", ");
}
......@@ -93,7 +92,6 @@ public class AnnotationGen {
}
code.add(')');
}
return code;
}
@SuppressWarnings("unchecked")
......@@ -122,70 +120,52 @@ public class AnnotationGen {
}
// TODO: refactor this boilerplate code
@SuppressWarnings("unchecked")
public String encValueToString(Object val) {
public void encodeValue(CodeWriter code, Object val) {
if (val == null) {
return "null";
code.add("null");
return;
}
if (val instanceof String) {
return StringUtils.unescapeString((String) val);
}
if (val instanceof Integer) {
return TypeGen.formatInteger((Integer) val);
}
if (val instanceof Character) {
return StringUtils.unescapeChar((Character) val);
}
if (val instanceof Boolean) {
return Boolean.TRUE.equals(val) ? "true" : "false";
}
if (val instanceof Float) {
return TypeGen.formatFloat((Float) val);
}
if (val instanceof Double) {
return TypeGen.formatDouble((Double) val);
}
if (val instanceof Long) {
return TypeGen.formatLong((Long) val);
}
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) {
code.add(StringUtils.unescapeString((String) val));
} else if (val instanceof Integer) {
code.add(TypeGen.formatInteger((Integer) val));
} else if (val instanceof Character) {
code.add(StringUtils.unescapeChar((Character) val));
} else if (val instanceof Boolean) {
code.add(Boolean.TRUE.equals(val) ? "true" : "false");
} else if (val instanceof Float) {
code.add(TypeGen.formatFloat((Float) val));
} else if (val instanceof Double) {
code.add(TypeGen.formatDouble((Double) val));
} else if (val instanceof Long) {
code.add(TypeGen.formatLong((Long) val));
} else if (val instanceof Short) {
code.add(TypeGen.formatShort((Short) val));
} else if (val instanceof Byte) {
code.add(TypeGen.formatByte((Byte) val));
} else if (val instanceof ArgType) {
code.add(TypeGen.translate(classGen, (ArgType) val)).add(".class");
} else if (val instanceof FieldInfo) {
// must be a static field
FieldInfo field = (FieldInfo) val;
// FIXME: !!code from InsnGen.sfield
String thisClass = cls.getFullName();
if (field.getDeclClass().getFullName().equals(thisClass)) {
return field.getName();
} else {
return classGen.useClass(field.getDeclClass()) + '.' + field.getName();
}
}
if (val instanceof List) {
StringBuilder str = new StringBuilder();
str.append('{');
List<Object> list = (List<Object>) val;
for (Iterator<Object> it = list.iterator(); it.hasNext(); ) {
code.add(InsnGen.makeStaticFieldAccess(field, classGen));
} else if (val instanceof List) {
code.add('{');
List list = (List) val;
Iterator it = list.iterator();
while (it.hasNext()) {
Object obj = it.next();
str.append(encValueToString(obj));
encodeValue(code, obj);
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
throw new JadxRuntimeException("Can't decode value: " + val + " (" + val.getClass() + ")");
}
// TODO: also can be method values
throw new JadxRuntimeException("Can't decode value: " + val + " (" + val.getClass() + ")");
}
}
......@@ -151,7 +151,7 @@ public class ClassGen {
}
}
clsCode.attachAnnotation(cls);
clsCode.attachDefinition(cls);
}
public boolean makeGenericMap(CodeWriter code, Map<ArgType, List<ArgType>> gmap) {
......@@ -228,8 +228,8 @@ public class ClassGen {
if (cls.getAccessFlags().isAnnotation()) {
Object def = annotationGen.getAnnotationDefaultValue(mth.getName());
if (def != null) {
String v = annotationGen.encValueToString(def);
code.add(" default ").add(v);
code.add(" default ");
annotationGen.encodeValue(code, def);
}
}
code.add(';');
......@@ -282,11 +282,11 @@ public class ClassGen {
if (fv.getValue() == null) {
code.add(TypeGen.literalToString(0, f.getType()));
} else {
code.add(annotationGen.encValueToString(fv.getValue()));
annotationGen.encodeValue(code, fv.getValue());
}
}
code.add(';');
code.attachAnnotation(f);
code.attachDefinition(f);
}
return code;
}
......
package jadx.core.codegen;
import jadx.api.CodePosition;
import jadx.core.dex.attributes.LineAttrNode;
import jadx.core.utils.Utils;
......@@ -7,6 +8,7 @@ import java.io.File;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.slf4j.Logger;
......@@ -17,7 +19,7 @@ public class CodeWriter {
private static final int MAX_FILENAME_LENGTH = 128;
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 = {
"",
......@@ -33,7 +35,8 @@ public class CodeWriter {
private int indent;
private int line = 1;
private Map<Object, Integer> annotations = Collections.emptyMap();
private int offset = 0;
private Map<CodePosition, Object> annotations = Collections.emptyMap();
public CodeWriter() {
this.indent = 0;
......@@ -47,56 +50,66 @@ public class CodeWriter {
public CodeWriter startLine() {
addLine();
buf.append(indentStr);
addIndent();
return this;
}
public CodeWriter startLine(char c) {
addLine();
buf.append(indentStr);
buf.append(c);
addIndent();
add(c);
return this;
}
public CodeWriter startLine(String str) {
addLine();
buf.append(indentStr);
buf.append(str);
addIndent();
add(str);
return this;
}
public CodeWriter startLine(int ind, String str) {
addLine();
buf.append(indentStr);
addIndent();
for (int i = 0; i < ind; i++) {
buf.append(INDENT);
addIndent();
}
buf.append(str);
add(str);
return this;
}
public CodeWriter add(Object obj) {
buf.append(obj);
add(obj.toString());
return this;
}
public CodeWriter add(String str) {
buf.append(str);
offset += str.length();
return this;
}
public CodeWriter add(char c) {
buf.append(c);
offset++;
return this;
}
@Deprecated
public CodeWriter add(CodeWriter code) {
line--;
for (Map.Entry<Object, Integer> entry : code.annotations.entrySet()) {
attachAnnotation(entry.getKey(), line + entry.getValue());
for (Map.Entry<CodePosition, Object> entry : code.annotations.entrySet()) {
CodePosition pos = entry.getKey();
attachAnnotation(entry.getValue(), new CodePosition(line + pos.getLine(), pos.getOffset()));
}
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;
}
......@@ -108,25 +121,12 @@ public class CodeWriter {
private void addLine() {
buf.append(NL);
line++;
offset = 0;
}
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() {
public CodeWriter addIndent() {
buf.append(indentStr);
offset += indentStr.length();
return this;
}
......@@ -169,16 +169,49 @@ public class CodeWriter {
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() {
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());
Iterator<Map.Entry<CodePosition, Object>> it = annotations.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<CodePosition, Object> entry = it.next();
Object v = 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) {
......
......@@ -16,6 +16,7 @@ import jadx.core.dex.instructions.FillArrayNode;
import jadx.core.dex.instructions.GotoNode;
import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.IndexInsnNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.InvokeNode;
import jadx.core.dex.instructions.InvokeType;
import jadx.core.dex.instructions.SwitchNode;
......@@ -38,6 +39,7 @@ import jadx.core.utils.InsnUtils;
import jadx.core.utils.RegionUtils;
import jadx.core.utils.StringUtils;
import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import java.util.ArrayList;
import java.util.EnumSet;
......@@ -57,12 +59,7 @@ public class InsnGen {
protected final RootNode root;
private final boolean fallback;
private static enum IGState {
SKIP,
NO_SEMICOLON,
NO_RESULT,
private static enum Flags {
BODY_ONLY,
BODY_ONLY_NOWRAP,
}
......@@ -113,7 +110,7 @@ public class InsnGen {
} else if (arg.isLiteral()) {
code.add(lit((LiteralArg) arg));
} else if (arg.isInsnWrap()) {
IGState flag = wrap ? IGState.BODY_ONLY : IGState.BODY_ONLY_NOWRAP;
Flags flag = wrap ? Flags.BODY_ONLY : Flags.BODY_ONLY_NOWRAP;
makeInsn(((InsnWrapArg) arg).getWrapInsn(), code, flag);
} else if (arg.isNamed()) {
code.add(((NamedArg) arg).getName());
......@@ -164,18 +161,17 @@ public class InsnGen {
code.add(field.getName());
}
protected String staticField(FieldInfo field) {
String thisClass = mth.getParentClass().getFullName();
public static String makeStaticFieldAccess(FieldInfo field, ClassGen clsGen) {
ClassInfo declClass = field.getDeclClass();
if (thisClass.startsWith(declClass.getFullName())) {
if (clsGen.getClassNode().getFullName().startsWith(declClass.getFullName())) {
return field.getName();
}
// Android specific resources class handler
ClassInfo parentClass = declClass.getParentClass();
if (parentClass != null && parentClass.getShortName().equals("R")) {
return useClass(parentClass) + "." + declClass.getShortName() + "." + field.getName();
return clsGen.useClass(parentClass) + "." + declClass.getShortName() + "." + field.getName();
}
return useClass(declClass) + '.' + field.getName();
return clsGen.useClass(declClass) + '.' + field.getName();
}
private void fieldPut(IndexInsnNode insn) {
......@@ -190,6 +186,10 @@ public class InsnGen {
}
}
protected String staticField(FieldInfo field) {
return makeStaticFieldAccess(field, mgen.getClassGen());
}
public String useClass(ClassInfo cls) {
return mgen.getClassGen().useClass(cls);
}
......@@ -202,31 +202,25 @@ public class InsnGen {
return makeInsn(insn, code, null);
}
private boolean makeInsn(InsnNode insn, CodeWriter code, IGState flag) throws CodegenException {
private boolean makeInsn(InsnNode insn, CodeWriter code, Flags flag) throws CodegenException {
try {
EnumSet<IGState> state = EnumSet.noneOf(IGState.class);
if (flag == IGState.BODY_ONLY || flag == IGState.BODY_ONLY_NOWRAP) {
if (insn.getType() == InsnType.NOP) {
return false;
}
EnumSet<Flags> state = EnumSet.noneOf(Flags.class);
if (flag == Flags.BODY_ONLY || flag == Flags.BODY_ONLY_NOWRAP) {
state.add(flag);
makeInsnBody(code, insn, state);
} else {
CodeWriter body = new CodeWriter(code.getIndent());
makeInsnBody(body, insn, state);
if (state.contains(IGState.SKIP)) {
return false;
}
code.startLine();
if (insn.getSourceLine() != 0) {
code.attachAnnotation(insn.getSourceLine());
}
if (insn.getResult() != null && !state.contains(IGState.NO_RESULT)) {
if (insn.getResult() != null && insn.getType() != InsnType.ARITH_ONEARG) {
code.add(assignVar(insn)).add(" = ");
}
code.add(body);
if (!state.contains(IGState.NO_SEMICOLON)) {
code.add(';');
}
makeInsnBody(code, insn, state);
code.add(';');
}
} catch (Throwable th) {
throw new CodegenException(mth, "Error generate insn: " + insn, th);
......@@ -234,7 +228,7 @@ public class InsnGen {
return true;
}
private void makeInsnBody(CodeWriter code, InsnNode insn, EnumSet<IGState> state) throws CodegenException {
private void makeInsnBody(CodeWriter code, InsnNode insn, EnumSet<Flags> state) throws CodegenException {
switch (insn.getType()) {
case CONST_STR:
String str = ((ConstStringNode) insn).getString();
......@@ -257,7 +251,7 @@ public class InsnGen {
case CHECK_CAST:
case CAST: {
boolean wrap = state.contains(IGState.BODY_ONLY);
boolean wrap = state.contains(Flags.BODY_ONLY);
if (wrap) {
code.add('(');
}
......@@ -270,13 +264,18 @@ public class InsnGen {
}
break;
}
case ARITH:
makeArith((ArithNode) insn, code, state);
break;
case ARITH_ONEARG:
makeArithOneArg((ArithNode) insn, code, state);
break;
case NEG:
String base = "-" + arg(insn.getArg(0));
if (state.contains(IGState.BODY_ONLY)) {
if (state.contains(Flags.BODY_ONLY)) {
code.add('(').add(base).add(')');
} else {
code.add(base);
......@@ -311,7 +310,7 @@ public class InsnGen {
break;
case INSTANCE_OF: {
boolean wrap = state.contains(IGState.BODY_ONLY);
boolean wrap = state.contains(Flags.BODY_ONLY);
if (wrap) {
code.add('(');
}
......@@ -400,7 +399,7 @@ public class InsnGen {
}
}
// TODO: wrap in braces only if necessary
if (state.contains(IGState.BODY_ONLY)) {
if (state.contains(Flags.BODY_ONLY)) {
code.add('(').add(sb.toString()).add(')');
} else {
code.add(sb.toString());
......@@ -410,16 +409,12 @@ public class InsnGen {
case MONITOR_ENTER:
if (isFallback()) {
code.add("monitor-enter(").add(arg(insn.getArg(0))).add(')');
} else {
state.add(IGState.SKIP);
}
break;
case MONITOR_EXIT:
if (isFallback()) {
code.add("monitor-exit(").add(arg(insn, 0)).add(')');
} else {
state.add(IGState.SKIP);
}
break;
......@@ -439,10 +434,6 @@ public class InsnGen {
code.add(arg(insn, 0));
break;
case NOP:
state.add(IGState.SKIP);
break;
/* fallback mode instructions */
case IF:
assert isFallback() : "if insn in not fallback mode";
......@@ -472,7 +463,6 @@ public class InsnGen {
code.add(MethodGen.getLabelName(sw.getDefaultCaseOffset())).add(';');
code.decIndent();
code.startLine('}');
state.add(IGState.NO_SEMICOLON);
break;
case NEW_INSTANCE:
......@@ -556,7 +546,7 @@ public class InsnGen {
code.add("new ").add(useType(elType)).add("[]{").add(str.toString()).add('}');
}
private void makeConstructor(ConstructorInsn insn, CodeWriter code, EnumSet<IGState> state)
private void makeConstructor(ConstructorInsn insn, CodeWriter code, EnumSet<Flags> state)
throws CodegenException {
ClassNode cls = mth.dex().resolveClass(insn.getClassType());
if (cls != null && cls.isAnonymous()) {
......@@ -579,9 +569,7 @@ public class InsnGen {
return;
}
if (insn.isSelf()) {
// skip
state.add(IGState.SKIP);
return;
throw new JadxRuntimeException("Constructor 'self' invoke must be removed!");
}
if (insn.isSuper()) {
code.add("super");
......@@ -632,6 +620,9 @@ public class InsnGen {
}
break;
}
if (callMthNode != null) {
code.attachAnnotation(callMthNode);
}
code.add(callMth.getName());
generateArguments(code, insn, k, callMthNode);
}
......@@ -678,7 +669,7 @@ public class InsnGen {
IAttribute mia = callMthNode.getAttributes().get(AttributeType.METHOD_INLINE);
InsnNode inl = ((MethodInlineAttr) mia).getInsn();
if (callMthNode.getMethodInfo().getArgumentsTypes().isEmpty()) {
makeInsn(inl, code, IGState.BODY_ONLY);
makeInsn(inl, code, Flags.BODY_ONLY);
} else {
// remap args
InsnArg[] regs = new InsnArg[callMthNode.getRegsCount()];
......@@ -705,7 +696,7 @@ public class InsnGen {
}
}
}
makeInsn(inl, code, IGState.BODY_ONLY);
makeInsn(inl, code, Flags.BODY_ONLY);
// revert changes
for (Map.Entry<RegisterArg, InsnArg> e : toRevert.entrySet()) {
inl.replaceArg(e.getValue(), e.getKey());
......@@ -713,14 +704,14 @@ public class InsnGen {
}
}
private void makeTernary(TernaryInsn insn, CodeWriter code, EnumSet<IGState> state) throws CodegenException {
private void makeTernary(TernaryInsn insn, CodeWriter code, EnumSet<Flags> state) throws CodegenException {
String cond = ConditionGen.make(this, insn.getCondition());
CodeWriter th = arg(insn.getArg(0), false);
CodeWriter els = arg(insn.getArg(1), false);
if (th.toString().equals("true") && els.toString().equals("false")) {
code.add(cond);
} else {
if (state.contains(IGState.BODY_ONLY)) {
if (state.contains(Flags.BODY_ONLY)) {
code.add("((").add(cond).add(')').add(" ? ").add(th).add(" : ").add(els).add(')');
} else {
code.add('(').add(cond).add(')').add(" ? ").add(th).add(" : ").add(els);
......@@ -728,33 +719,40 @@ public class InsnGen {
}
}
private void makeArith(ArithNode insn, CodeWriter code, EnumSet<IGState> state) throws CodegenException {
private void makeArith(ArithNode insn, CodeWriter code, EnumSet<Flags> state) throws CodegenException {
ArithOp op = insn.getOp();
CodeWriter v1 = arg(insn.getArg(0));
CodeWriter v2 = arg(insn.getArg(1));
if (state.contains(IGState.BODY_ONLY)) {
if (state.contains(Flags.BODY_ONLY)) {
// wrap insn in brackets for save correct operation order
code.add('(').add(v1).add(' ').add(op.getSymbol()).add(' ').add(v2).add(')');
} else if (state.contains(IGState.BODY_ONLY_NOWRAP)) {
code.add(v1).add(' ').add(op.getSymbol()).add(' ').add(v2);
code.add('(');
addArg(code, insn.getArg(0));
code.add(' ');
code.add(op.getSymbol());
code.add(' ');
addArg(code, insn.getArg(1));
code.add(')');
} else {
CodeWriter res = arg(insn.getResult());
if (res.equals(v1) && insn.getResult().equals(insn.getArg(0))) {
state.add(IGState.NO_RESULT);
// "++" or "--"
if (insn.getArg(1).isLiteral() && (op == ArithOp.ADD || op == ArithOp.SUB)) {
LiteralArg lit = (LiteralArg) insn.getArg(1);
if (lit.isInteger() && lit.getLiteral() == 1) {
code.add(assignVar(insn)).add(op.getSymbol()).add(op.getSymbol());
return;
}
}
// +=, -= ...
v2 = arg(insn.getArg(1), false);
code.add(assignVar(insn)).add(' ').add(op.getSymbol()).add("= ").add(v2);
} else {
code.add(v1).add(' ').add(op.getSymbol()).add(' ').add(v2);
addArg(code, insn.getArg(0));
code.add(' ');
code.add(op.getSymbol());
code.add(' ');
addArg(code, insn.getArg(1));
}
}
private void makeArithOneArg(ArithNode insn, CodeWriter code, EnumSet<Flags> state) throws CodegenException {
ArithOp op = insn.getOp();
InsnArg arg = insn.getArg(0);
// "++" or "--"
if (arg.isLiteral() && (op == ArithOp.ADD || op == ArithOp.SUB)) {
LiteralArg lit = (LiteralArg) arg;
if (lit.isInteger() && lit.getLiteral() == 1) {
String opSymbol = op.getSymbol();
code.add(assignVar(insn)).add(opSymbol).add(opSymbol);
return;
}
}
// +=, -= ...
code.add(assignVar(insn)).add(' ').add(op.getSymbol()).add("= ");
addArg(code, arg, false);
}
}
......@@ -61,13 +61,13 @@ public class MethodGen {
public boolean addDefinition(CodeWriter code) {
if (mth.getMethodInfo().isClassInit()) {
code.startLine("static");
code.attachAnnotation(mth);
code.attachDefinition(mth);
return true;
}
if (mth.getAttributes().contains(AttributeFlag.ANONYMOUS_CONSTRUCTOR)) {
// don't add method name and arguments
code.startLine();
code.attachAnnotation(mth);
code.attachDefinition(mth);
return false;
}
annotationGen.addForMethod(code, mth);
......@@ -110,17 +110,15 @@ public class MethodGen {
));
}
}
code.add(makeArguments(args));
code.add(")");
addMethodArguments(code, args);
code.add(')');
annotationGen.addThrows(mth, code);
code.attachAnnotation(mth);
code.attachDefinition(mth);
return true;
}
public CodeWriter makeArguments(List<RegisterArg> args) {
CodeWriter argsCode = new CodeWriter();
private void addMethodArguments(CodeWriter argsCode, List<RegisterArg> args) {
MethodParameters paramsAnnotation =
(MethodParameters) mth.getAttributes().get(AttributeType.ANNOTATION_MTH_PARAMETERS);
......@@ -154,7 +152,6 @@ public class MethodGen {
argsCode.add(", ");
}
}
return argsCode;
}
/**
......
......@@ -51,7 +51,7 @@ public class ArithNode extends InsnNode {
}
public ArithNode(ArithOp op, RegisterArg res, InsnArg a) {
super(InsnType.ARITH, 1);
super(InsnType.ARITH_ONEARG, 1);
this.op = op;
setResult(res);
addArg(a);
......
......@@ -54,6 +54,7 @@ public enum InsnType {
CONTINUE,
STR_CONCAT, // strings concatenation
ARITH_ONEARG,
TERNARY,
ARGS, // just generate arguments
......
......@@ -50,7 +50,10 @@ public class ClassNode extends LineAttrNode implements ILoadable {
private Map<Object, FieldNode> constFields = Collections.emptyMap();
private List<ClassNode> innerClasses = Collections.emptyList();
// store decompiled code
private CodeWriter code;
// store parent for inner classes or 'this' otherwise
private ClassNode parentClass;
public ClassNode(DexNode dex, ClassDef cls) throws DecodeException {
this.dex = dex;
......@@ -332,6 +335,19 @@ public class ClassNode extends LineAttrNode implements ILoadable {
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() {
return innerClasses;
}
......
......@@ -10,12 +10,14 @@ import com.android.dx.io.ClassData.Field;
public class FieldNode extends LineAttrNode {
private final ClassNode parent;
private final FieldInfo fieldInfo;
private final AccessInfo accFlags;
private ArgType type; // store signature
public FieldNode(ClassNode cls, Field field) {
this.parent = cls;
this.fieldInfo = FieldInfo.fromDex(cls.dex(), field.getFieldIndex());
this.type = fieldInfo.getType();
this.accFlags = new AccessInfo(field.getAccessFlags(), AFType.FIELD);
......@@ -41,6 +43,10 @@ public class FieldNode extends LineAttrNode {
this.type = type;
}
public ClassNode getParentClass() {
return parent;
}
@Override
public int hashCode() {
return fieldInfo.hashCode();
......
......@@ -159,7 +159,7 @@ public class ClassModifier extends AbstractVisitor {
&& af.isPublic()
&& mth.getArguments(false).isEmpty()) {
List<BlockNode> bb = mth.getBasicBlocks();
if (bb.isEmpty() || allBlocksEmpty(bb)) {
if (bb == null || bb.isEmpty() || allBlocksEmpty(bb)) {
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;
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.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import java.util.List;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThat;
......@@ -39,15 +31,9 @@ public class TestFieldIncrement extends InternalJadxTest {
@Test
public void test() {
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();
System.out.println(code);
assertThat(code, containsString("instanceField++;"));
assertThat(code, containsString("staticField--;"));
assertThat(code, containsString("result += s + '_';"));
......
......@@ -37,5 +37,7 @@ public class TestSynchronized extends InternalJadxTest {
assertThat(code, containsString("public synchronized boolean test1() {"));
assertThat(code, containsString("return this.f"));
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.core.codegen.CodeWriter;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册