Commit b2f189b5 authored by Skylot's avatar Skylot

core: process complex condition in loop header

parent eec524ad
......@@ -30,9 +30,14 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.android.dx.rop.code.AccessFlags;
public class ClassGen {
private static final Logger LOG = LoggerFactory.getLogger(ClassGen.class);
private final ClassNode cls;
private final ClassGen parentGen;
private final AnnotationGen annotationGen;
......@@ -232,6 +237,11 @@ public class ClassGen {
continue;
MethodGen mthGen = new MethodGen(this, mth);
if (mth.getAttributes().contains(AttributeFlag.INCONSISTENT_CODE)) {
code.startLine("/* JADX WARNING: inconsistent code */");
LOG.error(ErrorsCounter.formatErrorMsg(mth, " Inconsistent code"));
mthGen.makeMethodDump(code);
}
mthGen.addDefinition(code);
code.add(" {");
insertSourceFileInfo(code, mth);
......
package jadx.core.codegen;
import jadx.core.Consts;
import jadx.core.dex.attributes.AttributeFlag;
import jadx.core.dex.attributes.AttributeType;
import jadx.core.dex.attributes.AttributesList;
import jadx.core.dex.attributes.JadxErrorAttr;
......@@ -61,11 +60,6 @@ public class MethodGen {
if (mth.getMethodInfo().isClassInit()) {
code.startLine("static");
} else {
if (mth.getAttributes().contains(AttributeFlag.INCONSISTENT_CODE)
&& !mth.getAttributes().contains(AttributeType.JADX_ERROR)) {
code.startLine("// jadx: inconsistent code");
}
annotationGen.addForMethod(code, mth);
AccessInfo clsAccFlags = mth.getParentClass().getAccessFlags();
......@@ -235,16 +229,11 @@ public class MethodGen {
code.startLine("Error: ").add(Utils.getStackTrace(cause));
code.add("*/");
}
makeMethodDump(code, mth);
makeMethodDump(code);
} else {
if (mth.getRegion() != null) {
CodeWriter insns = new CodeWriter(mthIndent + 1);
(new RegionGen(this, mth)).makeRegion(insns, mth.getRegion());
if (mth.getAttributes().contains(AttributeFlag.INCONSISTENT_CODE)) {
LOG.debug(ErrorsCounter.formatErrorMsg(mth, " Inconsistent code"));
// makeMethodDump(code, mth);
}
code.add(insns);
} else {
makeFallbackMethod(code, mth);
......@@ -253,7 +242,7 @@ public class MethodGen {
return code;
}
public void makeMethodDump(CodeWriter code, MethodNode mth) {
public void makeMethodDump(CodeWriter code) {
code.startLine("/*");
getFallbackMethodGen(mth).addDefinition(code);
code.add(" {");
......
......@@ -4,15 +4,22 @@ import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.utils.InsnUtils;
import com.android.dx.io.instructions.DecodedInstruction;
import static jadx.core.utils.BlockUtils.getBlockByOffset;
import static jadx.core.utils.BlockUtils.selectOther;
public class IfNode extends GotoNode {
protected boolean zeroCmp;
protected IfOp op;
private BlockNode thenBlock;
private BlockNode elseBlock;
public IfNode(int targ, InsnArg then, InsnArg els) {
super(InsnType.IF, targ);
addArg(then);
......@@ -49,9 +56,12 @@ public class IfNode extends GotoNode {
return zeroCmp;
}
public void invertOp(int targ) {
public void invertCondition() {
op = op.invert();
target = targ;
BlockNode tmp = thenBlock;
thenBlock = elseBlock;
elseBlock = tmp;
target = thenBlock.getStartOffset();
}
public void changeCondition(InsnArg arg1, InsnArg arg2, IfOp op) {
......@@ -59,11 +69,30 @@ public class IfNode extends GotoNode {
this.zeroCmp = arg2.isLiteral() && ((LiteralArg) arg2).getLiteral() == 0;
setArg(0, arg1);
if (!zeroCmp) {
if (getArgsCount() == 2)
if (getArgsCount() == 2) {
setArg(1, arg2);
else
} else {
addArg(arg2);
}
}
}
public void initBlocks(BlockNode curBlock) {
thenBlock = getBlockByOffset(target, curBlock.getSuccessors());
if (curBlock.getSuccessors().size() == 1) {
elseBlock = thenBlock;
} else {
elseBlock = selectOther(thenBlock, curBlock.getSuccessors());
}
target = thenBlock.getStartOffset();
}
public BlockNode getThenBlock() {
return thenBlock;
}
public BlockNode getElseBlock() {
return elseBlock;
}
@Override
......@@ -72,6 +101,6 @@ public class IfNode extends GotoNode {
+ InsnUtils.insnTypeToString(insnType)
+ getArg(0) + " " + op.getSymbol()
+ " " + (zeroCmp ? "0" : getArg(1))
+ " -> " + InsnUtils.formatOffset(target);
+ " -> " + (thenBlock != null ? thenBlock : InsnUtils.formatOffset(target));
}
}
......@@ -12,9 +12,9 @@ import java.util.List;
public final class IfCondition {
public static IfCondition fromIfBlock(BlockNode header) {
if (header == null)
if (header == null) {
return null;
}
return fromIfNode((IfNode) header.getInstructions().get(0));
}
......@@ -39,8 +39,8 @@ public final class IfCondition {
public static final class Compare {
private final IfNode insn;
public Compare(IfNode ifNode) {
this.insn = ifNode;
public Compare(IfNode insn) {
this.insn = insn;
}
public IfOp getOp() {
......@@ -57,6 +57,10 @@ public final class IfCondition {
else
return insn.getArg(1);
}
public Compare invert() {
insn.invertCondition();
return this;
}
@Override
public String toString() {
......@@ -113,6 +117,23 @@ public final class IfCondition {
return compare;
}
public IfCondition invert() {
switch (mode) {
case COMPARE:
return new IfCondition(compare.invert());
case NOT:
return new IfCondition(args.get(0));
case AND:
case OR:
List<IfCondition> newArgs = new ArrayList<IfCondition>(args.size());
for (IfCondition arg : args) {
newArgs.add(arg.invert());
}
return new IfCondition(mode == Mode.AND ? Mode.OR : Mode.AND, newArgs);
}
throw new RuntimeException("Unknown mode for invert: " + mode);
}
@Override
public String toString() {
switch (mode) {
......
package jadx.core.dex.regions;
import jadx.core.dex.nodes.BlockNode;
public final class IfInfo {
IfCondition condition;
BlockNode ifnode;
BlockNode thenBlock;
BlockNode elseBlock;
public IfCondition getCondition() {
return condition;
}
public void setCondition(IfCondition condition) {
this.condition = condition;
}
public BlockNode getIfnode() {
return ifnode;
}
public void setIfnode(BlockNode ifnode) {
this.ifnode = ifnode;
}
public BlockNode getThenBlock() {
return thenBlock;
}
public void setThenBlock(BlockNode thenBlock) {
this.thenBlock = thenBlock;
}
public BlockNode getElseBlock() {
return elseBlock;
}
public void setElseBlock(BlockNode elseBlock) {
this.elseBlock = elseBlock;
}
}
......@@ -14,8 +14,8 @@ import java.util.List;
public final class LoopRegion extends AbstractRegion {
// loop header contains one 'if' insn, equals null for infinite loop
private final IfCondition condition;
private final BlockNode conditionBlock;
private IfCondition condition;
private BlockNode conditionBlock;
// instruction which must be executed before condition in every loop
private BlockNode preCondition = null;
private IContainer body;
......@@ -32,10 +32,18 @@ public final class LoopRegion extends AbstractRegion {
return condition;
}
public void setCondition(IfCondition condition) {
this.condition = condition;
}
public BlockNode getHeader() {
return conditionBlock;
}
public void setHeader(BlockNode conditionBlock) {
this.conditionBlock = conditionBlock;
}
public IContainer getBody() {
return body;
}
......
package jadx.core.dex.visitors;
import jadx.core.dex.attributes.AttributeType;
import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.NamedArg;
......@@ -14,17 +15,21 @@ import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.utils.BlockUtils;
import java.util.List;
public class BlockProcessingHelper {
public static void visit(MethodNode mth) {
if (mth.isNoCode())
if (mth.isNoCode()) {
return;
}
for (BlockNode block : mth.getBasicBlocks()) {
markExceptionHandlers(block);
}
for (BlockNode block : mth.getBasicBlocks()) {
block.updateCleanSuccessors();
initBlocksInIfNodes(block);
}
for (BlockNode block : mth.getBasicBlocks()) {
processExceptionHandlers(mth, block);
......@@ -77,11 +82,13 @@ public class BlockProcessingHelper {
// remove 'monitor-exit' from exception handler blocks
InstructionRemover remover = new InstructionRemover(excBlock.getInstructions());
for (InsnNode insn : excBlock.getInstructions()) {
if (insn.getType() == InsnType.MONITOR_ENTER)
if (insn.getType() == InsnType.MONITOR_ENTER) {
break;
}
if (insn.getType() == InsnType.MONITOR_EXIT)
if (insn.getType() == InsnType.MONITOR_EXIT) {
remover.add(insn);
}
}
remover.perform();
......@@ -108,8 +115,9 @@ public class BlockProcessingHelper {
CatchAttr commonCatchAttr = null;
for (InsnNode insn : block.getInstructions()) {
CatchAttr catchAttr = (CatchAttr) insn.getAttributes().get(AttributeType.CATCH_BLOCK);
if (catchAttr == null)
if (catchAttr == null) {
continue;
}
if (commonCatchAttr == null) {
commonCatchAttr = catchAttr;
......@@ -137,4 +145,17 @@ public class BlockProcessingHelper {
}
}
}
/**
* Init 'then' and 'else' blocks for 'if' instruction.
*/
private static void initBlocksInIfNodes(BlockNode block) {
List<InsnNode> instructions = block.getInstructions();
if (instructions.size() == 1) {
InsnNode insn = instructions.get(0);
if (insn.getType() == InsnType.IF) {
((IfNode) insn).initBlocks(block);
}
}
}
}
......@@ -23,7 +23,11 @@ public class InsnUtils {
}
public static String formatOffset(int offset) {
return String.format("0x%04x", offset);
if (offset < 0) {
return "?";
} else {
return String.format("0x%04x", offset);
}
}
public static String insnTypeToString(InsnType type) {
......
......@@ -125,4 +125,8 @@ public abstract class InternalJadxTest {
}
}
}
protected void setOutputCFG() {
this.outputCFG = true;
}
}
package jadx.tests.internal;
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 TestLoopCondition extends InternalJadxTest {
@SuppressWarnings("serial")
public static class TestCls extends Exception {
public String f;
private void setEnabled(boolean r1z) {
}
private void testIfInLoop() {
int j = 0;
for (int i = 0; i < f.length(); i++) {
char ch = f.charAt(i);
if (ch == '/') {
j++;
if (j == 2) {
setEnabled(true);
return;
}
}
}
setEnabled(false);
}
public int testComplexIfInLoop(boolean a) {
int i = 0;
while (a && i < 10) {
i++;
}
return i;
}
}
@Test
public void test() {
setOutputCFG();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsString("i < f.length()"));
assertThat(code, containsString("while (a && i < 10) {"));
}
}
......@@ -60,15 +60,17 @@ public class TestCF3 extends AbstractTest {
while (it2.hasNext()) {
String s2 = it2.next();
if (s1.equals(s2)) {
if (s1.length() == 5)
if (s1.length() == 5) {
l2.add(s1);
else
} else {
l1.remove(s2);
}
}
}
}
if (l2.size() > 0)
if (l2.size() > 0) {
l1.clear();
}
return l1.size() == 0;
}
......@@ -111,8 +113,9 @@ public class TestCF3 extends AbstractTest {
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String ver = it.next();
if (ver != null)
if (ver != null) {
return ver;
}
}
return "error";
}
......@@ -123,8 +126,9 @@ public class TestCF3 extends AbstractTest {
while (it.hasNext()) {
String ver = it.next();
exc();
if (ver != null)
if (ver != null) {
return ver;
}
}
} catch (Exception e) {
setEnabled(false);
......@@ -132,6 +136,34 @@ public class TestCF3 extends AbstractTest {
return "error";
}
public int testComplexIfInLoop(boolean a) {
int i = 0;
while (a && i < 10) {
i++;
}
return i;
}
public int testComplexIfInLoop2(int k) {
int i = k;
while (i > 5 && i < 10) {
i++;
}
return i;
}
public int testComplexIfInLoop3(int k) {
int i = k;
while (i > 5 && i < k * 3) {
if (k == 8) {
i++;
} else {
break;
}
}
return i;
}
@Override
public boolean testRun() throws Exception {
setEnabled(false);
......@@ -158,6 +190,16 @@ public class TestCF3 extends AbstractTest {
// assertEquals(testReturnInLoop2(list2), "error");
// assertTrue(testLabeledBreakContinue());
assertEquals(testComplexIfInLoop(false), 0);
assertEquals(testComplexIfInLoop(true), 10);
assertEquals(testComplexIfInLoop2(2), 2);
assertEquals(testComplexIfInLoop2(6), 10);
assertEquals(testComplexIfInLoop3(2), 2);
assertEquals(testComplexIfInLoop3(6), 6);
assertEquals(testComplexIfInLoop3(8), 24);
return true;
}
......
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