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; ...@@ -30,9 +30,14 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.android.dx.rop.code.AccessFlags; import com.android.dx.rop.code.AccessFlags;
public class ClassGen { public class ClassGen {
private static final Logger LOG = LoggerFactory.getLogger(ClassGen.class);
private final ClassNode cls; private final ClassNode cls;
private final ClassGen parentGen; private final ClassGen parentGen;
private final AnnotationGen annotationGen; private final AnnotationGen annotationGen;
...@@ -232,6 +237,11 @@ public class ClassGen { ...@@ -232,6 +237,11 @@ public class ClassGen {
continue; continue;
MethodGen mthGen = new MethodGen(this, mth); 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); mthGen.addDefinition(code);
code.add(" {"); code.add(" {");
insertSourceFileInfo(code, mth); insertSourceFileInfo(code, mth);
......
package jadx.core.codegen; package jadx.core.codegen;
import jadx.core.Consts; import jadx.core.Consts;
import jadx.core.dex.attributes.AttributeFlag;
import jadx.core.dex.attributes.AttributeType; import jadx.core.dex.attributes.AttributeType;
import jadx.core.dex.attributes.AttributesList; import jadx.core.dex.attributes.AttributesList;
import jadx.core.dex.attributes.JadxErrorAttr; import jadx.core.dex.attributes.JadxErrorAttr;
...@@ -61,11 +60,6 @@ public class MethodGen { ...@@ -61,11 +60,6 @@ public class MethodGen {
if (mth.getMethodInfo().isClassInit()) { if (mth.getMethodInfo().isClassInit()) {
code.startLine("static"); code.startLine("static");
} else { } else {
if (mth.getAttributes().contains(AttributeFlag.INCONSISTENT_CODE)
&& !mth.getAttributes().contains(AttributeType.JADX_ERROR)) {
code.startLine("// jadx: inconsistent code");
}
annotationGen.addForMethod(code, mth); annotationGen.addForMethod(code, mth);
AccessInfo clsAccFlags = mth.getParentClass().getAccessFlags(); AccessInfo clsAccFlags = mth.getParentClass().getAccessFlags();
...@@ -235,16 +229,11 @@ public class MethodGen { ...@@ -235,16 +229,11 @@ public class MethodGen {
code.startLine("Error: ").add(Utils.getStackTrace(cause)); code.startLine("Error: ").add(Utils.getStackTrace(cause));
code.add("*/"); code.add("*/");
} }
makeMethodDump(code, mth); makeMethodDump(code);
} else { } else {
if (mth.getRegion() != null) { if (mth.getRegion() != null) {
CodeWriter insns = new CodeWriter(mthIndent + 1); CodeWriter insns = new CodeWriter(mthIndent + 1);
(new RegionGen(this, mth)).makeRegion(insns, mth.getRegion()); (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); code.add(insns);
} else { } else {
makeFallbackMethod(code, mth); makeFallbackMethod(code, mth);
...@@ -253,7 +242,7 @@ public class MethodGen { ...@@ -253,7 +242,7 @@ public class MethodGen {
return code; return code;
} }
public void makeMethodDump(CodeWriter code, MethodNode mth) { public void makeMethodDump(CodeWriter code) {
code.startLine("/*"); code.startLine("/*");
getFallbackMethodGen(mth).addDefinition(code); getFallbackMethodGen(mth).addDefinition(code);
code.add(" {"); code.add(" {");
......
...@@ -4,15 +4,22 @@ import jadx.core.dex.instructions.args.ArgType; ...@@ -4,15 +4,22 @@ import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.instructions.args.PrimitiveType; import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.utils.InsnUtils; import jadx.core.utils.InsnUtils;
import com.android.dx.io.instructions.DecodedInstruction; 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 { public class IfNode extends GotoNode {
protected boolean zeroCmp; protected boolean zeroCmp;
protected IfOp op; protected IfOp op;
private BlockNode thenBlock;
private BlockNode elseBlock;
public IfNode(int targ, InsnArg then, InsnArg els) { public IfNode(int targ, InsnArg then, InsnArg els) {
super(InsnType.IF, targ); super(InsnType.IF, targ);
addArg(then); addArg(then);
...@@ -49,9 +56,12 @@ public class IfNode extends GotoNode { ...@@ -49,9 +56,12 @@ public class IfNode extends GotoNode {
return zeroCmp; return zeroCmp;
} }
public void invertOp(int targ) { public void invertCondition() {
op = op.invert(); op = op.invert();
target = targ; BlockNode tmp = thenBlock;
thenBlock = elseBlock;
elseBlock = tmp;
target = thenBlock.getStartOffset();
} }
public void changeCondition(InsnArg arg1, InsnArg arg2, IfOp op) { public void changeCondition(InsnArg arg1, InsnArg arg2, IfOp op) {
...@@ -59,12 +69,31 @@ public class IfNode extends GotoNode { ...@@ -59,12 +69,31 @@ public class IfNode extends GotoNode {
this.zeroCmp = arg2.isLiteral() && ((LiteralArg) arg2).getLiteral() == 0; this.zeroCmp = arg2.isLiteral() && ((LiteralArg) arg2).getLiteral() == 0;
setArg(0, arg1); setArg(0, arg1);
if (!zeroCmp) { if (!zeroCmp) {
if (getArgsCount() == 2) if (getArgsCount() == 2) {
setArg(1, arg2); setArg(1, arg2);
else } else {
addArg(arg2); 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 @Override
public String toString() { public String toString() {
...@@ -72,6 +101,6 @@ public class IfNode extends GotoNode { ...@@ -72,6 +101,6 @@ public class IfNode extends GotoNode {
+ InsnUtils.insnTypeToString(insnType) + InsnUtils.insnTypeToString(insnType)
+ getArg(0) + " " + op.getSymbol() + getArg(0) + " " + op.getSymbol()
+ " " + (zeroCmp ? "0" : getArg(1)) + " " + (zeroCmp ? "0" : getArg(1))
+ " -> " + InsnUtils.formatOffset(target); + " -> " + (thenBlock != null ? thenBlock : InsnUtils.formatOffset(target));
} }
} }
...@@ -12,9 +12,9 @@ import java.util.List; ...@@ -12,9 +12,9 @@ import java.util.List;
public final class IfCondition { public final class IfCondition {
public static IfCondition fromIfBlock(BlockNode header) { public static IfCondition fromIfBlock(BlockNode header) {
if (header == null) if (header == null) {
return null; return null;
}
return fromIfNode((IfNode) header.getInstructions().get(0)); return fromIfNode((IfNode) header.getInstructions().get(0));
} }
...@@ -39,8 +39,8 @@ public final class IfCondition { ...@@ -39,8 +39,8 @@ public final class IfCondition {
public static final class Compare { public static final class Compare {
private final IfNode insn; private final IfNode insn;
public Compare(IfNode ifNode) { public Compare(IfNode insn) {
this.insn = ifNode; this.insn = insn;
} }
public IfOp getOp() { public IfOp getOp() {
...@@ -57,6 +57,10 @@ public final class IfCondition { ...@@ -57,6 +57,10 @@ public final class IfCondition {
else else
return insn.getArg(1); return insn.getArg(1);
} }
public Compare invert() {
insn.invertCondition();
return this;
}
@Override @Override
public String toString() { public String toString() {
...@@ -113,6 +117,23 @@ public final class IfCondition { ...@@ -113,6 +117,23 @@ public final class IfCondition {
return compare; 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 @Override
public String toString() { public String toString() {
switch (mode) { 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; ...@@ -14,8 +14,8 @@ import java.util.List;
public final class LoopRegion extends AbstractRegion { public final class LoopRegion extends AbstractRegion {
// loop header contains one 'if' insn, equals null for infinite loop // loop header contains one 'if' insn, equals null for infinite loop
private final IfCondition condition; private IfCondition condition;
private final BlockNode conditionBlock; private BlockNode conditionBlock;
// instruction which must be executed before condition in every loop // instruction which must be executed before condition in every loop
private BlockNode preCondition = null; private BlockNode preCondition = null;
private IContainer body; private IContainer body;
...@@ -32,10 +32,18 @@ public final class LoopRegion extends AbstractRegion { ...@@ -32,10 +32,18 @@ public final class LoopRegion extends AbstractRegion {
return condition; return condition;
} }
public void setCondition(IfCondition condition) {
this.condition = condition;
}
public BlockNode getHeader() { public BlockNode getHeader() {
return conditionBlock; return conditionBlock;
} }
public void setHeader(BlockNode conditionBlock) {
this.conditionBlock = conditionBlock;
}
public IContainer getBody() { public IContainer getBody() {
return body; return body;
} }
......
package jadx.core.dex.visitors; package jadx.core.dex.visitors;
import jadx.core.dex.attributes.AttributeType; import jadx.core.dex.attributes.AttributeType;
import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.NamedArg; import jadx.core.dex.instructions.args.NamedArg;
...@@ -14,17 +15,21 @@ import jadx.core.dex.trycatch.ExceptionHandler; ...@@ -14,17 +15,21 @@ import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.TryCatchBlock; import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.utils.BlockUtils; import jadx.core.utils.BlockUtils;
import java.util.List;
public class BlockProcessingHelper { public class BlockProcessingHelper {
public static void visit(MethodNode mth) { public static void visit(MethodNode mth) {
if (mth.isNoCode()) if (mth.isNoCode()) {
return; return;
}
for (BlockNode block : mth.getBasicBlocks()) { for (BlockNode block : mth.getBasicBlocks()) {
markExceptionHandlers(block); markExceptionHandlers(block);
} }
for (BlockNode block : mth.getBasicBlocks()) { for (BlockNode block : mth.getBasicBlocks()) {
block.updateCleanSuccessors(); block.updateCleanSuccessors();
initBlocksInIfNodes(block);
} }
for (BlockNode block : mth.getBasicBlocks()) { for (BlockNode block : mth.getBasicBlocks()) {
processExceptionHandlers(mth, block); processExceptionHandlers(mth, block);
...@@ -77,12 +82,14 @@ public class BlockProcessingHelper { ...@@ -77,12 +82,14 @@ public class BlockProcessingHelper {
// remove 'monitor-exit' from exception handler blocks // remove 'monitor-exit' from exception handler blocks
InstructionRemover remover = new InstructionRemover(excBlock.getInstructions()); InstructionRemover remover = new InstructionRemover(excBlock.getInstructions());
for (InsnNode insn : excBlock.getInstructions()) { for (InsnNode insn : excBlock.getInstructions()) {
if (insn.getType() == InsnType.MONITOR_ENTER) if (insn.getType() == InsnType.MONITOR_ENTER) {
break; break;
}
if (insn.getType() == InsnType.MONITOR_EXIT) if (insn.getType() == InsnType.MONITOR_EXIT) {
remover.add(insn); remover.add(insn);
} }
}
remover.perform(); remover.perform();
// if 'throw' in exception handler block have 'catch' - merge these catch blocks // if 'throw' in exception handler block have 'catch' - merge these catch blocks
...@@ -108,8 +115,9 @@ public class BlockProcessingHelper { ...@@ -108,8 +115,9 @@ public class BlockProcessingHelper {
CatchAttr commonCatchAttr = null; CatchAttr commonCatchAttr = null;
for (InsnNode insn : block.getInstructions()) { for (InsnNode insn : block.getInstructions()) {
CatchAttr catchAttr = (CatchAttr) insn.getAttributes().get(AttributeType.CATCH_BLOCK); CatchAttr catchAttr = (CatchAttr) insn.getAttributes().get(AttributeType.CATCH_BLOCK);
if (catchAttr == null) if (catchAttr == null) {
continue; continue;
}
if (commonCatchAttr == null) { if (commonCatchAttr == null) {
commonCatchAttr = catchAttr; commonCatchAttr = catchAttr;
...@@ -137,4 +145,17 @@ public class BlockProcessingHelper { ...@@ -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);
}
}
}
} }
...@@ -17,6 +17,7 @@ import jadx.core.dex.nodes.IRegion; ...@@ -17,6 +17,7 @@ import jadx.core.dex.nodes.IRegion;
import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.regions.IfCondition; import jadx.core.dex.regions.IfCondition;
import jadx.core.dex.regions.IfInfo;
import jadx.core.dex.regions.IfRegion; import jadx.core.dex.regions.IfRegion;
import jadx.core.dex.regions.LoopRegion; import jadx.core.dex.regions.LoopRegion;
import jadx.core.dex.regions.Region; import jadx.core.dex.regions.Region;
...@@ -61,11 +62,12 @@ public class RegionMaker { ...@@ -61,11 +62,12 @@ public class RegionMaker {
public Region makeRegion(BlockNode startBlock, RegionStack stack) { public Region makeRegion(BlockNode startBlock, RegionStack stack) {
if (Consts.DEBUG) { if (Consts.DEBUG) {
int id = startBlock.getId(); int id = startBlock.getId();
if (processedBlocks.get(id)) if (processedBlocks.get(id)) {
LOG.debug(" Block already processed: " + startBlock + ", mth: " + mth); LOG.debug(" Block already processed: " + startBlock + ", mth: " + mth);
else } else {
processedBlocks.set(id); processedBlocks.set(id);
} }
}
Region r = new Region(stack.peekRegion()); Region r = new Region(stack.peekRegion());
BlockNode next = startBlock; BlockNode next = startBlock;
...@@ -133,11 +135,12 @@ public class RegionMaker { ...@@ -133,11 +135,12 @@ public class RegionMaker {
next = BlockUtils.getNextBlock(block); next = BlockUtils.getNextBlock(block);
} }
if (next != null && !stack.containsExit(block) && !stack.containsExit(next)) if (next != null && !stack.containsExit(block) && !stack.containsExit(next)) {
return next; return next;
else } else {
return null; return null;
} }
}
private BlockNode processLoop(IRegion curRegion, LoopAttr loop, RegionStack stack) { private BlockNode processLoop(IRegion curRegion, LoopAttr loop, RegionStack stack) {
BlockNode loopStart = loop.getStart(); BlockNode loopStart = loop.getStart();
...@@ -149,21 +152,29 @@ public class RegionMaker { ...@@ -149,21 +152,29 @@ public class RegionMaker {
// this can help if loop have several exits (after using 'break' or 'return' in loop) // this can help if loop have several exits (after using 'break' or 'return' in loop)
List<BlockNode> exitBlocks = new ArrayList<BlockNode>(exitBlocksSet.size()); List<BlockNode> exitBlocks = new ArrayList<BlockNode>(exitBlocksSet.size());
BlockNode nextStart = BlockUtils.getNextBlock(loopStart); BlockNode nextStart = BlockUtils.getNextBlock(loopStart);
if (nextStart != null && exitBlocksSet.remove(nextStart)) if (nextStart != null && exitBlocksSet.remove(nextStart)) {
exitBlocks.add(nextStart); exitBlocks.add(nextStart);
if (exitBlocksSet.remove(loop.getEnd())) }
if (exitBlocksSet.remove(loop.getEnd())) {
exitBlocks.add(loop.getEnd()); exitBlocks.add(loop.getEnd());
if (exitBlocksSet.remove(loopStart)) }
if (exitBlocksSet.remove(loopStart)) {
exitBlocks.add(loopStart); exitBlocks.add(loopStart);
}
exitBlocks.addAll(exitBlocksSet); exitBlocks.addAll(exitBlocksSet);
exitBlocksSet = null; exitBlocksSet = null;
BlockNode condBlock = null; // exit block with loop condition // exit block with loop condition
BlockNode condBlock = null;
// first block in loop
BlockNode bThen = null;
for (BlockNode exit : exitBlocks) { for (BlockNode exit : exitBlocks) {
if (exit.getAttributes().contains(AttributeType.EXC_HANDLER) if (exit.getAttributes().contains(AttributeType.EXC_HANDLER)
|| exit.getInstructions().size() != 1) || exit.getInstructions().size() != 1) {
continue; continue;
}
InsnNode insn = exit.getInstructions().get(0); InsnNode insn = exit.getInstructions().get(0);
if (insn.getType() == InsnType.IF) { if (insn.getType() == InsnType.IF) {
...@@ -190,10 +201,26 @@ public class RegionMaker { ...@@ -190,10 +201,26 @@ public class RegionMaker {
loopRegion = null; loopRegion = null;
condBlock = null; condBlock = null;
// try another exit // try another exit
} else } else {
List<BlockNode> merged = new ArrayList<BlockNode>(2);
IfInfo mergedIf = mergeNestedIfNodes(condBlock,
ifnode.getThenBlock(), ifnode.getElseBlock(), merged);
if (mergedIf != null) {
condBlock = mergedIf.getIfnode();
if (!loop.getLoopBlocks().contains(mergedIf.getThenBlock())) {
// invert loop condition if it points to exit
loopRegion.setCondition(mergedIf.getCondition().invert());
bThen = mergedIf.getElseBlock();
} else {
loopRegion.setCondition(mergedIf.getCondition());
bThen = mergedIf.getThenBlock();
}
exitBlocks.removeAll(merged);
}
break; break;
} }
} }
}
// endless loop // endless loop
if (loopRegion == null) { if (loopRegion == null) {
...@@ -203,18 +230,20 @@ public class RegionMaker { ...@@ -203,18 +230,20 @@ public class RegionMaker {
loopStart.getAttributes().remove(AttributeType.LOOP); loopStart.getAttributes().remove(AttributeType.LOOP);
stack.push(loopRegion); stack.push(loopRegion);
Region body = makeRegion(loopStart, stack); Region body = makeRegion(loopStart, stack);
if (!RegionUtils.isRegionContainsBlock(body, loop.getEnd())) if (!RegionUtils.isRegionContainsBlock(body, loop.getEnd())) {
body.getSubBlocks().add(loop.getEnd()); body.getSubBlocks().add(loop.getEnd());
}
loopRegion.setBody(body); loopRegion.setBody(body);
stack.pop(); stack.pop();
loopStart.getAttributes().add(loop); loopStart.getAttributes().add(loop);
BlockNode next = BlockUtils.getNextBlock(loop.getEnd()); BlockNode next = BlockUtils.getNextBlock(loop.getEnd());
if (!RegionUtils.isRegionContainsBlock(body, next)) if (!RegionUtils.isRegionContainsBlock(body, next)) {
return next; return next;
else } else {
return null; return null;
} }
}
stack.push(loopRegion); stack.push(loopRegion);
curRegion.getSubBlocks().add(loopRegion); curRegion.getSubBlocks().add(loopRegion);
...@@ -250,10 +279,13 @@ public class RegionMaker { ...@@ -250,10 +279,13 @@ public class RegionMaker {
} }
} }
BlockNode bThen = getBlockByOffset(ifnode.getTarget(), condBlock.getSuccessors()); if (bThen == null) {
bThen = ifnode.getThenBlock();
}
BlockNode out; BlockNode out;
if (loopRegion.isConditionAtEnd()) { if (loopRegion.isConditionAtEnd()) {
BlockNode bElse = selectOther(bThen, condBlock.getSuccessors()); BlockNode bElse = ifnode.getElseBlock();
out = (bThen == loopStart ? bElse : bThen); out = (bThen == loopStart ? bElse : bThen);
loopStart.getAttributes().remove(AttributeType.LOOP); loopStart.getAttributes().remove(AttributeType.LOOP);
...@@ -269,9 +301,9 @@ public class RegionMaker { ...@@ -269,9 +301,9 @@ public class RegionMaker {
break; break;
} }
} }
if (bThen != loopBody) if (bThen != loopBody) {
ifnode.invertOp(bThen.getStartOffset()); loopRegion.setCondition(loopRegion.getCondition().invert());
}
out = selectOther(loopBody, condBlock.getSuccessors()); out = selectOther(loopBody, condBlock.getSuccessors());
AttributesList outAttrs = out.getAttributes(); AttributesList outAttrs = out.getAttributes();
if (outAttrs.contains(AttributeFlag.LOOP_START) if (outAttrs.contains(AttributeFlag.LOOP_START)
...@@ -286,7 +318,7 @@ public class RegionMaker { ...@@ -286,7 +318,7 @@ public class RegionMaker {
return out; return out;
} }
private static final Set<BlockNode> cacheSet = new HashSet<BlockNode>(); private final Set<BlockNode> cacheSet = new HashSet<BlockNode>();
private BlockNode processMonitorEnter(IRegion curRegion, BlockNode block, InsnNode insn, RegionStack stack) { private BlockNode processMonitorEnter(IRegion curRegion, BlockNode block, InsnNode insn, RegionStack stack) {
SynchronizedRegion synchRegion = new SynchronizedRegion(curRegion, insn); SynchronizedRegion synchRegion = new SynchronizedRegion(curRegion, insn);
...@@ -328,10 +360,11 @@ public class RegionMaker { ...@@ -328,10 +360,11 @@ public class RegionMaker {
} }
} }
for (BlockNode node : block.getCleanSuccessors()) { for (BlockNode node : block.getCleanSuccessors()) {
if (!visited.contains(node)) if (!visited.contains(node)) {
traverseMonitorExits(arg, node, exits, visited); traverseMonitorExits(arg, node, exits, visited);
} }
} }
}
/** /**
* Traverse from monitor-enter thru successors and search for exit paths cross * Traverse from monitor-enter thru successors and search for exit paths cross
...@@ -347,33 +380,28 @@ public class RegionMaker { ...@@ -347,33 +380,28 @@ public class RegionMaker {
break; break;
} }
} }
if (cross) if (cross) {
return node; return node;
}
if (!visited.contains(node)) { if (!visited.contains(node)) {
BlockNode res = traverseMonitorExitsCross(node, exits, visited); BlockNode res = traverseMonitorExitsCross(node, exits, visited);
if (res != null) if (res != null) {
return res; return res;
} }
} }
}
return null; return null;
} }
private BlockNode processIf(IRegion currentRegion, BlockNode block, IfNode ifnode, RegionStack stack) { private BlockNode processIf(IRegion currentRegion, BlockNode block, IfNode ifnode, RegionStack stack) {
BlockNode bThen = getBlockByOffset(ifnode.getTarget(), block.getSuccessors()); BlockNode bThen = ifnode.getThenBlock();
BlockNode bElse = ifnode.getElseBlock();
if (block.getAttributes().contains(AttributeFlag.SKIP)) { if (block.getAttributes().contains(AttributeFlag.SKIP)) {
// block already included in other if region // block already included in other if region
return bThen; return bThen;
} }
BlockNode bElse;
if (block.getSuccessors().size() == 1) {
// TODO eliminate useless 'if' instruction
bElse = bThen;
} else {
bElse = selectOther(bThen, block.getSuccessors());
}
BlockNode out = null; BlockNode out = null;
BlockNode thenBlock = null; BlockNode thenBlock = null;
BlockNode elseBlock = null; BlockNode elseBlock = null;
...@@ -388,63 +416,19 @@ public class RegionMaker { ...@@ -388,63 +416,19 @@ public class RegionMaker {
IfRegion ifRegion = new IfRegion(currentRegion, block); IfRegion ifRegion = new IfRegion(currentRegion, block);
currentRegion.getSubBlocks().add(ifRegion); currentRegion.getSubBlocks().add(ifRegion);
// merge nested if nodes IfInfo mergedIf = mergeNestedIfNodes(block, bThen, bElse, null);
boolean found; if (mergedIf != null) {
do { block = mergedIf.getIfnode();
found = false; ifRegion.setCondition(mergedIf.getCondition());
for (BlockNode succ : block.getSuccessors()) { thenBlock = mergedIf.getThenBlock();
BlockNode nestedIfBlock = getIfNode(succ); elseBlock = mergedIf.getElseBlock();
if (nestedIfBlock != null && nestedIfBlock != block) {
IfNode nestedIfInsn = (IfNode) nestedIfBlock.getInstructions().get(0);
BlockNode nbThen = getBlockByOffset(nestedIfInsn.getTarget(), nestedIfBlock.getSuccessors());
BlockNode nbElse = selectOther(nbThen, nestedIfBlock.getSuccessors());
IfCondition condition;
boolean inverted = false;
IfCondition nestedCondition = IfCondition.fromIfNode(nestedIfInsn);
if (isPathExists(bElse, nestedIfBlock)) {
// else branch
if (bThen != nbThen) {
if (bThen != nbElse) {
break; // not connected conditions
}
nestedIfInsn.invertOp(nbElse.getStartOffset());
inverted = true;
}
condition = IfCondition.merge(Mode.OR, ifRegion.getCondition(), nestedCondition);
} else {
// then branch
if (bElse != nbElse) {
if (bElse != nbThen) {
break; // not connected conditions
}
nestedIfInsn.invertOp(nbElse.getStartOffset());
inverted = true;
}
condition = IfCondition.merge(Mode.AND, ifRegion.getCondition(), nestedCondition);
}
ifRegion.setCondition(condition);
nestedIfBlock.getAttributes().add(AttributeFlag.SKIP);
// set new blocks
if (inverted) {
thenBlock = nbElse;
elseBlock = nbThen;
} else {
thenBlock = nbThen;
elseBlock = nbElse;
}
found = true;
block = nestedIfBlock;
bThen = thenBlock; bThen = thenBlock;
bElse = elseBlock; bElse = elseBlock;
break;
} }
}
} while (found);
if (thenBlock == null) { if (thenBlock == null) {
// invert condition (compiler often do it) // invert condition (compiler often do it)
ifnode.invertOp(bElse.getStartOffset()); ifnode.invertCondition();
BlockNode tmp = bThen; BlockNode tmp = bThen;
bThen = bElse; bThen = bElse;
bElse = tmp; bElse = tmp;
...@@ -466,16 +450,16 @@ public class RegionMaker { ...@@ -466,16 +450,16 @@ public class RegionMaker {
} }
} }
} }
if (BlockUtils.isBackEdge(block, out)) { if (BlockUtils.isBackEdge(block, out)) {
out = null; out = null;
} }
} }
if (elseBlock != null) { if (elseBlock != null) {
if (stack.containsExit(elseBlock)) if (stack.containsExit(elseBlock)) {
elseBlock = null; elseBlock = null;
} }
}
stack.push(ifRegion); stack.push(ifRegion);
stack.addExit(out); stack.addExit(out);
...@@ -487,6 +471,72 @@ public class RegionMaker { ...@@ -487,6 +471,72 @@ public class RegionMaker {
return out; return out;
} }
private IfInfo mergeNestedIfNodes(BlockNode block, BlockNode bThen, BlockNode bElse, List<BlockNode> merged) {
if (bThen == bElse) {
return null;
}
boolean found;
IfCondition condition = IfCondition.fromIfBlock(block);
IfInfo result = null;
do {
found = false;
for (BlockNode succ : block.getSuccessors()) {
BlockNode nestedIfBlock = getIfNode(succ);
if (nestedIfBlock != null && nestedIfBlock != block) {
IfNode nestedIfInsn = (IfNode) nestedIfBlock.getInstructions().get(0);
BlockNode nbThen = nestedIfInsn.getThenBlock();
BlockNode nbElse = nestedIfInsn.getElseBlock();
boolean inverted = false;
IfCondition nestedCondition = IfCondition.fromIfNode(nestedIfInsn);
if (isPathExists(bElse, nestedIfBlock)) {
// else branch
if (bThen != nbThen) {
if (bThen != nbElse) {
// not connected conditions
break;
}
nestedIfInsn.invertCondition();
inverted = true;
}
condition = IfCondition.merge(Mode.OR, condition, nestedCondition);
} else {
// then branch
if (bElse != nbElse) {
if (bElse != nbThen) {
// not connected conditions
break;
}
nestedIfInsn.invertCondition();
inverted = true;
}
condition = IfCondition.merge(Mode.AND, condition, nestedCondition);
}
result = new IfInfo();
result.setCondition(condition);
nestedIfBlock.getAttributes().add(AttributeFlag.SKIP);
if (merged != null) {
merged.add(nestedIfBlock);
}
// set new blocks
result.setIfnode(nestedIfBlock);
result.setThenBlock(inverted ? nbElse : nbThen);
result.setElseBlock(inverted ? nbThen : nbElse);
found = true;
block = nestedIfBlock;
bThen = result.getThenBlock();
bElse = result.getElseBlock();
break;
}
}
} while (found);
return result;
}
private BlockNode getIfNode(BlockNode block) { private BlockNode getIfNode(BlockNode block) {
if (block != null && !block.getAttributes().contains(AttributeType.LOOP)) { if (block != null && !block.getAttributes().contains(AttributeType.LOOP)) {
List<InsnNode> insns = block.getInstructions(); List<InsnNode> insns = block.getInstructions();
...@@ -571,10 +621,12 @@ public class RegionMaker { ...@@ -571,10 +621,12 @@ public class RegionMaker {
// filter successors of other blocks // filter successors of other blocks
for (int i = domsOn.nextSetBit(0); i >= 0; i = domsOn.nextSetBit(i + 1)) { for (int i = domsOn.nextSetBit(0); i >= 0; i = domsOn.nextSetBit(i + 1)) {
BlockNode b = mth.getBasicBlocks().get(i); BlockNode b = mth.getBasicBlocks().get(i);
for (BlockNode s : b.getCleanSuccessors()) for (BlockNode s : b.getCleanSuccessors()) {
if (domsOn.get(s.getId())) if (domsOn.get(s.getId())) {
domsOn.clear(s.getId()); domsOn.clear(s.getId());
} }
}
}
outCount = domsOn.cardinality(); outCount = domsOn.cardinality();
} }
...@@ -590,9 +642,10 @@ public class RegionMaker { ...@@ -590,9 +642,10 @@ public class RegionMaker {
if (out != null) { if (out != null) {
stack.addExit(out); stack.addExit(out);
} else { } else {
for (BlockNode e : BlockUtils.bitsetToBlocks(mth, domsOn)) for (BlockNode e : BlockUtils.bitsetToBlocks(mth, domsOn)) {
stack.addExit(e); stack.addExit(e);
} }
}
if (!stack.containsExit(defCase)) { if (!stack.containsExit(defCase)) {
sw.setDefaultCase(makeRegion(defCase, stack)); sw.setDefaultCase(makeRegion(defCase, stack));
...@@ -619,8 +672,9 @@ public class RegionMaker { ...@@ -619,8 +672,9 @@ public class RegionMaker {
} }
BlockNode out = BlockUtils.traverseWhileDominates(start, start); BlockNode out = BlockUtils.traverseWhileDominates(start, start);
if (out != null) if (out != null) {
stack.addExit(out); stack.addExit(out);
}
// TODO extract finally part which exists in all handlers from same try block // TODO extract finally part which exists in all handlers from same try block
// TODO add blocks common for several handlers to some region // TODO add blocks common for several handlers to some region
handler.setHandlerRegion(makeRegion(start, stack)); handler.setHandlerRegion(makeRegion(start, stack));
......
...@@ -23,8 +23,12 @@ public class InsnUtils { ...@@ -23,8 +23,12 @@ public class InsnUtils {
} }
public static String formatOffset(int offset) { public static String formatOffset(int offset) {
if (offset < 0) {
return "?";
} else {
return String.format("0x%04x", offset); return String.format("0x%04x", offset);
} }
}
public static String insnTypeToString(InsnType type) { public static String insnTypeToString(InsnType type) {
return insnTypeToString(type.toString()); return insnTypeToString(type.toString());
......
...@@ -125,4 +125,8 @@ public abstract class InternalJadxTest { ...@@ -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 { ...@@ -60,15 +60,17 @@ public class TestCF3 extends AbstractTest {
while (it2.hasNext()) { while (it2.hasNext()) {
String s2 = it2.next(); String s2 = it2.next();
if (s1.equals(s2)) { if (s1.equals(s2)) {
if (s1.length() == 5) if (s1.length() == 5) {
l2.add(s1); l2.add(s1);
else } else {
l1.remove(s2); l1.remove(s2);
} }
} }
} }
if (l2.size() > 0) }
if (l2.size() > 0) {
l1.clear(); l1.clear();
}
return l1.size() == 0; return l1.size() == 0;
} }
...@@ -111,9 +113,10 @@ public class TestCF3 extends AbstractTest { ...@@ -111,9 +113,10 @@ public class TestCF3 extends AbstractTest {
Iterator<String> it = list.iterator(); Iterator<String> it = list.iterator();
while (it.hasNext()) { while (it.hasNext()) {
String ver = it.next(); String ver = it.next();
if (ver != null) if (ver != null) {
return ver; return ver;
} }
}
return "error"; return "error";
} }
...@@ -123,15 +126,44 @@ public class TestCF3 extends AbstractTest { ...@@ -123,15 +126,44 @@ public class TestCF3 extends AbstractTest {
while (it.hasNext()) { while (it.hasNext()) {
String ver = it.next(); String ver = it.next();
exc(); exc();
if (ver != null) if (ver != null) {
return ver; return ver;
} }
}
} catch (Exception e) { } catch (Exception e) {
setEnabled(false); setEnabled(false);
} }
return "error"; 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 @Override
public boolean testRun() throws Exception { public boolean testRun() throws Exception {
setEnabled(false); setEnabled(false);
...@@ -158,6 +190,16 @@ public class TestCF3 extends AbstractTest { ...@@ -158,6 +190,16 @@ public class TestCF3 extends AbstractTest {
// assertEquals(testReturnInLoop2(list2), "error"); // assertEquals(testReturnInLoop2(list2), "error");
// assertTrue(testLabeledBreakContinue()); // 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; 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