Commit 917cf20d authored by Skylot's avatar Skylot

core: fix decompiled lines info

parent dabaeed8
......@@ -17,7 +17,7 @@ public class CodeWriter {
private static final int MAX_FILENAME_LENGTH = 128;
public static final String NL = System.getProperty("line.separator");
private static final String INDENT = "\t";
public static final String INDENT = "\t";
private final StringBuilder buf = new StringBuilder();
private String indentStr;
......@@ -183,6 +183,10 @@ public class CodeWriter {
}
}
public boolean isEmpty() {
return buf.length() == 0;
}
public boolean notEmpty() {
return buf.length() != 0;
}
......@@ -227,4 +231,20 @@ public class CodeWriter {
}
}
@Override
public int hashCode() {
return buf.toString().hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof CodeWriter)) {
return false;
}
CodeWriter that = (CodeWriter) o;
return buf.toString().equals(that.buf.toString());
}
}
......@@ -77,38 +77,38 @@ public class InsnGen {
return fallback;
}
public String arg(InsnNode insn, int arg) throws CodegenException {
public CodeWriter arg(InsnNode insn, int arg) throws CodegenException {
return arg(insn.getArg(arg));
}
public String arg(InsnArg arg) throws CodegenException {
public CodeWriter arg(InsnArg arg) throws CodegenException {
return arg(arg, true);
}
public String arg(InsnArg arg, boolean wrap) throws CodegenException {
public CodeWriter arg(InsnArg arg, boolean wrap) throws CodegenException {
CodeWriter code = new CodeWriter();
if (arg.isRegister()) {
return mgen.makeArgName((RegisterArg) arg);
code.add(mgen.makeArgName((RegisterArg) arg));
} else if (arg.isLiteral()) {
return lit((LiteralArg) arg);
code.add(lit((LiteralArg) arg));
} else if (arg.isInsnWrap()) {
CodeWriter code = new CodeWriter();
IGState flag = wrap ? IGState.BODY_ONLY : IGState.BODY_ONLY_NOWRAP;
makeInsn(((InsnWrapArg) arg).getWrapInsn(), code, flag);
return code.toString();
} else if (arg.isNamed()) {
return ((NamedArg) arg).getName();
code.add(((NamedArg) arg).getName());
} else if (arg.isField()) {
FieldArg f = (FieldArg) arg;
if (f.isStatic()) {
return sfield(f.getField());
code.add(sfield(f.getField()));
} else {
RegisterArg regArg = new RegisterArg(f.getRegNum());
regArg.replaceTypedVar(f);
return ifield(f.getField(), regArg);
code.add(ifield(f.getField(), regArg));
}
} else {
throw new CodegenException("Unknown arg type " + arg);
}
return code;
}
public String assignVar(InsnNode insn) throws CodegenException {
......@@ -116,7 +116,7 @@ public class InsnGen {
if (insn.getAttributes().contains(AttributeType.DECLARE_VARIABLE)) {
return declareVar(arg);
} else {
return arg(arg);
return arg(arg).toString();
}
}
......@@ -130,7 +130,7 @@ public class InsnGen {
private String ifield(FieldInfo field, InsnArg arg) throws CodegenException {
FieldNode fieldNode = mth.getParentClass().searchField(field);
if(fieldNode != null && fieldNode.getAttributes().contains(AttributeFlag.DONT_GENERATE)) {
if (fieldNode != null && fieldNode.getAttributes().contains(AttributeFlag.DONT_GENERATE)) {
return "";
}
String name = field.getName();
......@@ -149,7 +149,7 @@ public class InsnGen {
return name;
}
}
String argStr = arg(arg);
CodeWriter argStr = arg(arg);
return argStr.isEmpty() ? name : argStr + "." + name;
}
......@@ -162,7 +162,7 @@ public class InsnGen {
// Android specific resources class handler
ClassInfo parentClass = declClass.getParentClass();
if (parentClass != null && parentClass.getShortName().equals("R")) {
return useClass(parentClass) + "." + declClass.getShortName() + "." + field.getName();
return useClass(parentClass) + "." + declClass.getShortName() + "." + field.getName();
}
return useClass(declClass) + '.' + field.getName();
}
......@@ -173,8 +173,9 @@ public class InsnGen {
if (field.getDeclClass().getFullName().equals(thisClass)) {
// if we generate this field - don't init if its final and used
FieldNode fn = mth.getParentClass().searchField(field);
if (fn != null && fn.getAccessFlags().isFinal())
if (fn != null && fn.getAccessFlags().isFinal()) {
fn.getAttributes().remove(AttributeType.FIELD_VALUE);
}
}
}
......@@ -246,14 +247,16 @@ public class InsnGen {
case CHECK_CAST:
case CAST: {
boolean wrap = state.contains(IGState.BODY_ONLY);
if (wrap)
if (wrap) {
code.add("(");
}
code.add("(");
code.add(useType(((ArgType) ((IndexInsnNode) insn).getIndex())));
code.add(") ");
code.add(arg(insn.getArg(0)));
if (wrap)
if (wrap) {
code.add(")");
}
break;
}
case ARITH:
......@@ -270,10 +273,11 @@ public class InsnGen {
break;
case RETURN:
if (insn.getArgsCount() != 0)
if (insn.getArgsCount() != 0) {
code.add("return ").add(arg(insn.getArg(0), false));
else
} else {
code.add("return");
}
break;
case BREAK:
......@@ -318,8 +322,9 @@ public class InsnGen {
ArgType arrayType = insn.getResult().getType();
int dim = arrayType.getArrayDimension();
code.add("new ").add(useType(arrayType.getArrayRootElement())).add('[').add(arg(insn, 0)).add(']');
for (int i = 0; i < dim - 1; i++)
for (int i = 0; i < dim - 1; i++) {
code.add("[]");
}
break;
}
......@@ -460,8 +465,9 @@ public class InsnGen {
code.add('{');
for (int i = 0; i < c; i++) {
code.add(arg(insn, i));
if (i + 1 < c)
if (i + 1 < c) {
code.add(", ");
}
}
code.add('}');
}
......@@ -581,8 +587,8 @@ public class InsnGen {
InsnArg arg = insn.getArg(0);
// FIXME: add 'this' for equals methods in scope
if (!arg.isThis()) {
String argStr = arg(arg);
if(!argStr.isEmpty()) {
CodeWriter argStr = arg(arg);
if (!argStr.isEmpty()) {
code.add(argStr).add('.');
}
}
......@@ -685,15 +691,15 @@ public class InsnGen {
private void makeArith(ArithNode insn, CodeWriter code, EnumSet<IGState> state) throws CodegenException {
ArithOp op = insn.getOp();
String v1 = arg(insn.getArg(0));
String v2 = arg(insn.getArg(1));
CodeWriter v1 = arg(insn.getArg(0));
CodeWriter v2 = arg(insn.getArg(1));
if (state.contains(IGState.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);
} else {
String res = arg(insn.getResult());
CodeWriter res = arg(insn.getResult());
if (res.equals(v1) && insn.getResult().equals(insn.getArg(0))) {
state.add(IGState.NO_RESULT);
// "++" or "--"
......
......@@ -60,8 +60,9 @@ public class RegionGen extends InsnGen {
if (tc != null) {
makeTryCatch(cont, tc.getTryBlock(), code);
} else {
for (IContainer c : r.getSubBlocks())
for (IContainer c : r.getSubBlocks()) {
makeRegion(code, c);
}
}
} else if (cont instanceof IfRegion) {
code.startLine();
......@@ -214,7 +215,7 @@ public class RegionGen extends InsnGen {
op = op.invert();
}
if (op == IfOp.EQ) {
return arg(firstArg, false); // == true
return arg(firstArg, false).toString(); // == true
} else if (op == IfOp.NE) {
return "!" + arg(firstArg); // != true
}
......@@ -257,8 +258,7 @@ public class RegionGen extends InsnGen {
code.startLine("case ");
if (k instanceof IndexInsnNode) {
code.add(sfield((FieldInfo) ((IndexInsnNode) k).getIndex()));
}
else {
} else {
code.add(TypeGen.literalToString((Integer) k, arg.getType()));
}
code.add(':');
......@@ -295,8 +295,9 @@ public class RegionGen extends InsnGen {
if (!handler.isCatchAll()) {
makeCatchBlock(code, handler);
} else {
if (allHandler != null)
if (allHandler != null) {
LOG.warn("Several 'all' handlers in try/catch block in " + mth);
}
allHandler = handler;
}
}
......
......@@ -295,7 +295,10 @@ public class ClassNode extends LineAttrNode implements ILoadable {
}
public FieldNode searchField(FieldInfo field) {
String name = field.getName();
return searchFieldByName(field.getName());
}
public FieldNode searchFieldByName(String name) {
for (FieldNode f : fields) {
if (f.getName().equals(name))
return f;
......
......@@ -25,6 +25,8 @@ import static junit.framework.Assert.fail;
public abstract class InternalJadxTest {
protected boolean outputCFG = false;
protected boolean deleteTmpJar = true;
protected String outDir = "test-out-tmp";
public ClassNode getClassNode(Class<?> clazz) {
......@@ -40,7 +42,7 @@ public abstract class InternalJadxTest {
String clsName = clazz.getName();
ClassNode cls = null;
for (ClassNode aClass : classes) {
if(aClass.getFullName().equals(clsName)) {
if (aClass.getFullName().equals(clsName)) {
cls = aClass;
}
}
......@@ -90,7 +92,11 @@ public abstract class InternalJadxTest {
add(file, path + "/" + file.getName(), jo);
}
jo.close();
temp.deleteOnExit();
if (deleteTmpJar) {
temp.deleteOnExit();
} else {
System.out.println("Temporary jar file path: " + temp.getAbsolutePath());
}
return temp;
}
......@@ -145,4 +151,10 @@ public abstract class InternalJadxTest {
protected void setOutputCFG() {
this.outputCFG = true;
}
// Use only for debug purpose
@Deprecated
protected void notDeleteTmpJar() {
this.deleteTmpJar = false;
}
}
package jadx.tests.internal;
import jadx.api.InternalJadxTest;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.attributes.LineAttrNode;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class TestLineNumbers extends InternalJadxTest {
public static class TestCls extends Exception {
int field;
public void func() {
}
public static class Inner {
int innerField;
public void innerFunc() {
}
public void innerFunc2() {
new Runnable() {
@Override
public void run() {
}
}.run();
}
public void innerFunc3() {
}
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
FieldNode field = cls.searchFieldByName("field");
MethodNode func = cls.searchMethodByName("func()V");
ClassNode inner = cls.getInnerClasses().get(0);
MethodNode innerFunc = inner.searchMethodByName("innerFunc()V");
MethodNode innerFunc2 = inner.searchMethodByName("innerFunc2()V");
MethodNode innerFunc3 = inner.searchMethodByName("innerFunc3()V");
FieldNode innerField = inner.searchFieldByName("innerField");
// check source lines (available only for instructions and methods)
int testClassLine = 18;
assertEquals(testClassLine + 3, func.getSourceLine());
assertEquals(testClassLine + 9, innerFunc.getSourceLine());
assertEquals(testClassLine + 12, innerFunc2.getSourceLine());
assertEquals(testClassLine + 20, innerFunc3.getSourceLine());
// check decompiled lines
String[] lines = code.split(CodeWriter.NL);
checkLine(lines, field, "int field;");
checkLine(lines, func, "public void func() {");
checkLine(lines, inner, "public static class Inner {");
checkLine(lines, innerField, "int innerField;");
checkLine(lines, innerFunc, "public void innerFunc() {");
checkLine(lines, innerFunc2, "public void innerFunc2() {");
checkLine(lines, innerFunc3, "public void innerFunc3() {");
}
private static void checkLine(String[] lines, LineAttrNode node, String str) {
int lineNumber = node.getDecompiledLine();
String line = lines[lineNumber - 1];
assertThat(line, containsString(str));
}
}
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