Commit a0d8d9fc authored by Skylot's avatar Skylot

core: fix 'break' detection in loops

parent a2142b2f
...@@ -93,16 +93,12 @@ public class RegionGen extends InsnGen { ...@@ -93,16 +93,12 @@ public class RegionGen extends InsnGen {
for (InsnNode insn : block.getInstructions()) { for (InsnNode insn : block.getInstructions()) {
makeInsn(insn, code); makeInsn(insn, code);
} }
if (block.getAttributes().contains(AttributeFlag.BREAK)) {
code.startLine("break;");
} else {
IAttribute attr; IAttribute attr;
if ((attr = block.getAttributes().get(AttributeType.FORCE_RETURN)) != null) { if ((attr = block.getAttributes().get(AttributeType.FORCE_RETURN)) != null) {
ForceReturnAttr retAttr = (ForceReturnAttr) attr; ForceReturnAttr retAttr = (ForceReturnAttr) attr;
makeInsn(retAttr.getReturnInsn(), code); makeInsn(retAttr.getReturnInsn(), code);
} }
} }
}
private void makeIf(IfRegion region, CodeWriter code, boolean newLine) throws CodegenException { private void makeIf(IfRegion region, CodeWriter code, boolean newLine) throws CodegenException {
if (region.getTernRegion() != null) { if (region.getTernRegion() != null) {
......
...@@ -9,7 +9,6 @@ public enum AttributeFlag { ...@@ -9,7 +9,6 @@ public enum AttributeFlag {
SYNTHETIC, SYNTHETIC,
BREAK,
RETURN, // block contains only return instruction RETURN, // block contains only return instruction
DECLARE_VAR, DECLARE_VAR,
......
package jadx.core.dex.attributes; package jadx.core.dex.attributes;
import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.Edge;
import jadx.core.utils.BlockUtils; import jadx.core.utils.BlockUtils;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set; import java.util.Set;
public class LoopAttr implements IAttribute { public class LoopAttr implements IAttribute {
...@@ -37,16 +40,16 @@ public class LoopAttr implements IAttribute { ...@@ -37,16 +40,16 @@ public class LoopAttr implements IAttribute {
} }
/** /**
* Return block nodes with exit edges from loop <br> * Return source blocks of exit edges. <br>
* Exit nodes belongs to loop (contains in {@code loopBlocks}) * Exit nodes belongs to loop (contains in {@code loopBlocks})
*/ */
public Set<BlockNode> getExitNodes() { public Set<BlockNode> getExitNodes() {
Set<BlockNode> nodes = new HashSet<BlockNode>(); Set<BlockNode> nodes = new HashSet<BlockNode>();
Set<BlockNode> inloop = getLoopBlocks(); Set<BlockNode> blocks = getLoopBlocks();
for (BlockNode block : inloop) { for (BlockNode block : blocks) {
// exit: successor node not from this loop, (don't change to getCleanSuccessors) // exit: successor node not from this loop, (don't change to getCleanSuccessors)
for (BlockNode s : block.getSuccessors()) { for (BlockNode s : block.getSuccessors()) {
if (!inloop.contains(s) && !s.getAttributes().contains(AttributeType.EXC_HANDLER)) { if (!blocks.contains(s) && !s.getAttributes().contains(AttributeType.EXC_HANDLER)) {
nodes.add(block); nodes.add(block);
} }
} }
...@@ -54,6 +57,22 @@ public class LoopAttr implements IAttribute { ...@@ -54,6 +57,22 @@ public class LoopAttr implements IAttribute {
return nodes; return nodes;
} }
/**
* Return loop exit edges.
*/
public List<Edge> getExitEdges() {
List<Edge> edges = new LinkedList<Edge>();
Set<BlockNode> blocks = getLoopBlocks();
for (BlockNode block : blocks) {
for (BlockNode s : block.getSuccessors()) {
if (!blocks.contains(s) && !s.getAttributes().contains(AttributeType.EXC_HANDLER)) {
edges.add(new Edge(block, s));
}
}
}
return edges;
}
@Override @Override
public String toString() { public String toString() {
return "LOOP: " + start + "->" + end; return "LOOP: " + start + "->" + end;
......
package jadx.core.dex.nodes; package jadx.core.dex.nodes;
import jadx.core.dex.attributes.AttrNode; import jadx.core.dex.attributes.AttrNode;
import jadx.core.dex.attributes.AttributeFlag;
import jadx.core.dex.attributes.AttributeType; import jadx.core.dex.attributes.AttributeType;
import jadx.core.dex.attributes.BlockRegState; import jadx.core.dex.attributes.BlockRegState;
import jadx.core.dex.attributes.LoopAttr; import jadx.core.dex.attributes.LoopAttr;
...@@ -148,6 +149,14 @@ public class BlockNode extends AttrNode implements IBlock { ...@@ -148,6 +149,14 @@ public class BlockNode extends AttrNode implements IBlock {
this.endState = endState; this.endState = endState;
} }
public boolean isSynthetic() {
return getAttributes().contains(AttributeFlag.SYNTHETIC);
}
public boolean isReturnBlock() {
return getAttributes().contains(AttributeFlag.RETURN);
}
@Override @Override
public int hashCode() { public int hashCode() {
return id; // TODO id can change during reindex return id; // TODO id can change during reindex
......
package jadx.core.dex.nodes;
public class Edge {
private final BlockNode source;
private final BlockNode target;
public Edge(BlockNode source, BlockNode target) {
this.source = source;
this.target = target;
}
public BlockNode getSource() {
return source;
}
public BlockNode getTarget() {
return target;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Edge edge = (Edge) o;
return source.equals(edge.source) && target.equals(edge.target);
}
@Override
public int hashCode() {
return source.hashCode() + 31 * target.hashCode();
}
@Override
public String toString() {
return "Edge: " + source + " -> " + target;
}
}
...@@ -12,6 +12,7 @@ import jadx.core.dex.instructions.args.ArgType; ...@@ -12,6 +12,7 @@ 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.RegisterArg; import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.Edge;
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.trycatch.CatchAttr; import jadx.core.dex.trycatch.CatchAttr;
...@@ -299,6 +300,16 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -299,6 +300,16 @@ public class BlockMakerVisitor extends AbstractVisitor {
} }
} }
private static void markReturnBlocks(MethodNode mth) {
mth.getExitBlocks().clear();
for (BlockNode block : mth.getBasicBlocks()) {
if (BlockUtils.lastInsnType(block, InsnType.RETURN)) {
block.getAttributes().add(AttributeFlag.RETURN);
mth.getExitBlocks().add(block);
}
}
}
private static void markLoops(MethodNode mth) { private static void markLoops(MethodNode mth) {
for (BlockNode block : mth.getBasicBlocks()) { for (BlockNode block : mth.getBasicBlocks()) {
for (BlockNode succ : block.getSuccessors()) { for (BlockNode succ : block.getSuccessors()) {
...@@ -316,16 +327,6 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -316,16 +327,6 @@ public class BlockMakerVisitor extends AbstractVisitor {
} }
} }
private static void markReturnBlocks(MethodNode mth) {
mth.getExitBlocks().clear();
for (BlockNode block : mth.getBasicBlocks()) {
if (BlockUtils.lastInsnType(block, InsnType.RETURN)) {
block.getAttributes().add(AttributeFlag.RETURN);
mth.getExitBlocks().add(block);
}
}
}
private static void registerLoops(MethodNode mth) { private static void registerLoops(MethodNode mth) {
for (BlockNode block : mth.getBasicBlocks()) { for (BlockNode block : mth.getBasicBlocks()) {
AttributesList attributes = block.getAttributes(); AttributesList attributes = block.getAttributes();
...@@ -356,6 +357,7 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -356,6 +357,7 @@ public class BlockMakerVisitor extends AbstractVisitor {
if (oneHeader) { if (oneHeader) {
// several back edges connected to one loop header => make additional block // several back edges connected to one loop header => make additional block
BlockNode newLoopHeader = startNewBlock(mth, block.getStartOffset()); BlockNode newLoopHeader = startNewBlock(mth, block.getStartOffset());
newLoopHeader.getAttributes().add(AttributeFlag.SYNTHETIC);
connect(newLoopHeader, block); connect(newLoopHeader, block);
for (IAttribute a : loops) { for (IAttribute a : loops) {
LoopAttr la = (LoopAttr) a; LoopAttr la = (LoopAttr) a;
...@@ -366,6 +368,24 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -366,6 +368,24 @@ public class BlockMakerVisitor extends AbstractVisitor {
return true; return true;
} }
} }
// insert additional blocks if loop has several exits
if (loops.size() == 1) {
LoopAttr loop = (LoopAttr) loops.get(0);
List<Edge> edges = loop.getExitEdges();
if (edges.size() > 1) {
boolean change = false;
for (Edge edge : edges) {
BlockNode target = edge.getTarget();
if (!target.getAttributes().contains(AttributeFlag.SYNTHETIC)) {
insertBlockBetween(mth, edge.getSource(), target);
change = true;
}
}
if (change) {
return true;
}
}
}
} }
if (splitReturn(mth)) { if (splitReturn(mth)) {
return true; return true;
...@@ -376,6 +396,15 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -376,6 +396,15 @@ public class BlockMakerVisitor extends AbstractVisitor {
return false; return false;
} }
private static BlockNode insertBlockBetween(MethodNode mth, BlockNode source, BlockNode target) {
BlockNode newBlock = startNewBlock(mth, target.getStartOffset());
newBlock.getAttributes().add(AttributeFlag.SYNTHETIC);
removeConnection(source, target);
connect(source, newBlock);
connect(newBlock, target);
return newBlock;
}
/** /**
* Merge return blocks for void methods * Merge return blocks for void methods
*/ */
...@@ -414,7 +443,6 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -414,7 +443,6 @@ public class BlockMakerVisitor extends AbstractVisitor {
if (mth.getExitBlocks().size() != 1) { if (mth.getExitBlocks().size() != 1) {
return false; return false;
} }
boolean split = false;
BlockNode exitBlock = mth.getExitBlocks().get(0); BlockNode exitBlock = mth.getExitBlocks().get(0);
if (exitBlock.getPredecessors().size() > 1 if (exitBlock.getPredecessors().size() > 1
&& exitBlock.getInstructions().size() == 1 && exitBlock.getInstructions().size() == 1
...@@ -422,10 +450,9 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -422,10 +450,9 @@ public class BlockMakerVisitor extends AbstractVisitor {
&& !exitBlock.getAttributes().contains(AttributeFlag.SYNTHETIC)) { && !exitBlock.getAttributes().contains(AttributeFlag.SYNTHETIC)) {
InsnNode returnInsn = exitBlock.getInstructions().get(0); InsnNode returnInsn = exitBlock.getInstructions().get(0);
List<BlockNode> preds = new ArrayList<BlockNode>(exitBlock.getPredecessors()); List<BlockNode> preds = new ArrayList<BlockNode>(exitBlock.getPredecessors());
if (returnInsn.getArgsCount() != 0 && !isReturnArgAssignInPred(mth, preds, returnInsn)) { if (returnInsn.getArgsCount() != 0 && !isReturnArgAssignInPred(preds, returnInsn)) {
return false; return false;
} }
split = true;
for (BlockNode pred : preds) { for (BlockNode pred : preds) {
BlockNode newRetBlock = startNewBlock(mth, exitBlock.getStartOffset()); BlockNode newRetBlock = startNewBlock(mth, exitBlock.getStartOffset());
newRetBlock.getAttributes().add(AttributeFlag.SYNTHETIC); newRetBlock.getAttributes().add(AttributeFlag.SYNTHETIC);
...@@ -433,14 +460,13 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -433,14 +460,13 @@ public class BlockMakerVisitor extends AbstractVisitor {
removeConnection(pred, exitBlock); removeConnection(pred, exitBlock);
connect(pred, newRetBlock); connect(pred, newRetBlock);
} }
}
if (split) {
cleanExitNodes(mth); cleanExitNodes(mth);
return true;
} }
return split; return false;
} }
private static boolean isReturnArgAssignInPred(MethodNode mth, List<BlockNode> preds, InsnNode returnInsn) { private static boolean isReturnArgAssignInPred(List<BlockNode> preds, InsnNode returnInsn) {
RegisterArg arg = (RegisterArg) returnInsn.getArg(0); RegisterArg arg = (RegisterArg) returnInsn.getArg(0);
int regNum = arg.getRegNum(); int regNum = arg.getRegNum();
for (BlockNode pred : preds) { for (BlockNode pred : preds) {
......
...@@ -46,22 +46,21 @@ public class ConstInlinerVisitor extends AbstractVisitor { ...@@ -46,22 +46,21 @@ public class ConstInlinerVisitor extends AbstractVisitor {
long lit = ((LiteralArg) arg).getLiteral(); long lit = ((LiteralArg) arg).getLiteral();
return replaceConst(mth, block, insn, lit); return replaceConst(mth, block, insn, lit);
} }
// TODO process string and class const
} }
return false; return false;
} }
private static boolean replaceConst(MethodNode mth, BlockNode block, InsnNode insn, long literal) { private static boolean replaceConst(MethodNode mth, BlockNode block, InsnNode insn, long literal) {
List<InsnArg> use = insn.getResult().getTypedVar().getUseList(); List<InsnArg> use = insn.getResult().getTypedVar().getUseList();
int replace = 0; int replaceCount = 0;
for (InsnArg arg : use) { for (InsnArg arg : use) {
InsnNode useInsn = arg.getParentInsn(); InsnNode useInsn = arg.getParentInsn();
if (useInsn == null) { if (arg == insn.getResult() || useInsn == null) {
continue; continue;
} }
BlockNode useBlock = BlockUtils.getBlockByInsn(mth, useInsn); BlockNode useBlock = BlockUtils.getBlockByInsn(mth, useInsn);
if (useBlock == block || useBlock.isDominator(block)) { boolean isDominator = useBlock == block || useBlock.isDominator(block);
if (arg != insn.getResult() && !registerReassignOnPath(block, useBlock, insn)) { if (isDominator && !registerReassignOnPath(block, useBlock, insn)) {
LiteralArg litArg; LiteralArg litArg;
if (use.size() == 2) { if (use.size() == 2) {
// arg used only in one place // arg used only in one place
...@@ -73,12 +72,11 @@ public class ConstInlinerVisitor extends AbstractVisitor { ...@@ -73,12 +72,11 @@ public class ConstInlinerVisitor extends AbstractVisitor {
} }
if (useInsn.replaceArg(arg, litArg)) { if (useInsn.replaceArg(arg, litArg)) {
fixTypes(mth, useInsn, litArg); fixTypes(mth, useInsn, litArg);
replace++; replaceCount++;
} }
} }
} }
} return replaceCount == use.size() - 1;
return (replace + 1) == use.size();
} }
private static boolean registerReassignOnPath(BlockNode block, BlockNode useBlock, InsnNode assignInsn) { private static boolean registerReassignOnPath(BlockNode block, BlockNode useBlock, InsnNode assignInsn) {
...@@ -86,7 +84,6 @@ public class ConstInlinerVisitor extends AbstractVisitor { ...@@ -86,7 +84,6 @@ public class ConstInlinerVisitor extends AbstractVisitor {
return false; return false;
} }
Set<BlockNode> blocks = BlockUtils.getAllPathsBlocks(block, useBlock); Set<BlockNode> blocks = BlockUtils.getAllPathsBlocks(block, useBlock);
// TODO store list of assign insn for each register
int regNum = assignInsn.getResult().getRegNum(); int regNum = assignInsn.getResult().getRegNum();
for (BlockNode b : blocks) { for (BlockNode b : blocks) {
for (InsnNode insn : b.getInstructions()) { for (InsnNode insn : b.getInstructions()) {
......
...@@ -12,6 +12,7 @@ import jadx.core.dex.instructions.SwitchNode; ...@@ -12,6 +12,7 @@ import jadx.core.dex.instructions.SwitchNode;
import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.Edge;
import jadx.core.dex.nodes.IRegion; 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;
...@@ -143,8 +144,6 @@ public class RegionMaker { ...@@ -143,8 +144,6 @@ public class RegionMaker {
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();
IfNode ifnode = null;
LoopRegion loopRegion = null;
Set<BlockNode> exitBlocksSet = loop.getExitNodes(); Set<BlockNode> exitBlocksSet = loop.getExitNodes();
// set exit blocks scan order by priority // set exit blocks scan order by priority
...@@ -163,6 +162,9 @@ public class RegionMaker { ...@@ -163,6 +162,9 @@ public class RegionMaker {
exitBlocks.addAll(exitBlocksSet); exitBlocks.addAll(exitBlocksSet);
exitBlocksSet = null; exitBlocksSet = null;
IfNode ifnode = null;
LoopRegion loopRegion = null;
// exit block with loop condition // exit block with loop condition
BlockNode condBlock = null; BlockNode condBlock = null;
...@@ -174,17 +176,15 @@ public class RegionMaker { ...@@ -174,17 +176,15 @@ public class RegionMaker {
|| 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) {
boolean found = true; continue;
}
ifnode = (IfNode) insn; ifnode = (IfNode) insn;
condBlock = exit; condBlock = exit;
loopRegion = new LoopRegion(curRegion, condBlock, condBlock == loop.getEnd());
if (loopRegion.isConditionAtEnd()) { loopRegion = new LoopRegion(curRegion, condBlock, condBlock == loop.getEnd());
// TODO: add some checks boolean found = true;
} else {
if (condBlock != loopStart) { if (condBlock != loopStart) {
if (condBlock.getPredecessors().contains(loopStart)) { if (condBlock.getPredecessors().contains(loopStart)) {
loopRegion.setPreCondition(loopStart); loopRegion.setPreCondition(loopStart);
...@@ -194,13 +194,14 @@ public class RegionMaker { ...@@ -194,13 +194,14 @@ public class RegionMaker {
found = false; found = false;
} }
} }
}
if (!found) { if (!found) {
ifnode = null; ifnode = null;
loopRegion = null; loopRegion = null;
condBlock = null; condBlock = null;
// try another exit // try another exit
} else { continue;
}
List<BlockNode> merged = new ArrayList<BlockNode>(2); List<BlockNode> merged = new ArrayList<BlockNode>(2);
IfInfo mergedIf = mergeNestedIfNodes(condBlock, IfInfo mergedIf = mergeNestedIfNodes(condBlock,
ifnode.getThenBlock(), ifnode.getElseBlock(), merged); ifnode.getThenBlock(), ifnode.getElseBlock(), merged);
...@@ -218,69 +219,36 @@ public class RegionMaker { ...@@ -218,69 +219,36 @@ public class RegionMaker {
} }
break; break;
} }
}
}
// endless loop // endless loop
if (loopRegion == null) { if (loopRegion == null) {
loopRegion = new LoopRegion(curRegion, null, false); return makeEndlessLoop(curRegion, stack, loop, loopStart);
curRegion.getSubBlocks().add(loopRegion);
loopStart.getAttributes().remove(AttributeType.LOOP);
stack.push(loopRegion);
Region body = makeRegion(loopStart, stack);
if (!RegionUtils.isRegionContainsBlock(body, loop.getEnd())) {
body.getSubBlocks().add(loop.getEnd());
} }
loopRegion.setBody(body);
stack.pop();
loopStart.getAttributes().add(loop);
BlockNode next = BlockUtils.getNextBlock(loop.getEnd()); if (bThen == null) {
if (!RegionUtils.isRegionContainsBlock(body, next)) { bThen = ifnode.getThenBlock();
return next; }
} else { BlockNode loopBody = null;
return null; for (BlockNode s : condBlock.getSuccessors()) {
if (loop.getLoopBlocks().contains(s)) {
loopBody = s;
break;
} }
} }
curRegion.getSubBlocks().add(loopRegion); curRegion.getSubBlocks().add(loopRegion);
stack.push(loopRegion);
exitBlocks.remove(condBlock); exitBlocks.remove(condBlock);
if (exitBlocks.size() > 0) { if (exitBlocks.size() > 0) {
// set BREAK or FORCE_RETURN attributes // add 'break' instruction before path cross between main loop exit and subexit
// before path cross between main loop exit and subexit BlockNode loopExit = BlockUtils.selectOther(loopBody, condBlock.getCleanSuccessors());
BlockNode loopExit = BlockUtils.getNextBlock(condBlock); for (Edge exitEdge : loop.getExitEdges()) {
for (BlockNode exit : exitBlocks) { if (!exitBlocks.contains(exitEdge.getSource())) {
BlockNode next = BlockUtils.getNextBlock(exit); continue;
while (next != null) {
if (isPathExists(loopExit, next)) {
// found cross
/*
if (next.getCleanSuccessors().size() == 1) {
BlockNode r = BlockUtils.getNextBlock(next);
if (r != null
&& r.getAttributes().contains(AttributeFlag.RETURN)
&& r.getInstructions().size() > 0
&& r.getInstructions().get(0).getType() == InsnType.RETURN) {
next.getAttributes().add(new ForceReturnAttr(r.getInstructions().get(0)));
} else {
next.getAttributes().add(AttributeFlag.BREAK);
stack.addExit(r);
}
} else {
stack.addExit(next);
}
*/
break;
}
next = BlockUtils.getNextBlock(next);
}
} }
insertBreak(stack, loopExit, exitEdge);
} }
if (bThen == null) {
bThen = ifnode.getThenBlock();
} }
BlockNode out; BlockNode out;
...@@ -290,20 +258,10 @@ public class RegionMaker { ...@@ -290,20 +258,10 @@ public class RegionMaker {
loopStart.getAttributes().remove(AttributeType.LOOP); loopStart.getAttributes().remove(AttributeType.LOOP);
stack.push(loopRegion);
stack.addExit(loop.getEnd()); stack.addExit(loop.getEnd());
loopRegion.setBody(makeRegion(loopStart, stack)); loopRegion.setBody(makeRegion(loopStart, stack));
loopStart.getAttributes().add(loop); loopStart.getAttributes().add(loop);
stack.pop();
} else { } else {
Set<BlockNode> loopBlocks = loop.getLoopBlocks();
BlockNode loopBody = null;
for (BlockNode s : condBlock.getSuccessors()) {
if (loopBlocks.contains(s)) {
loopBody = s;
break;
}
}
if (bThen != loopBody) { if (bThen != loopBody) {
loopRegion.setCondition(IfCondition.invert(loopRegion.getCondition())); loopRegion.setCondition(IfCondition.invert(loopRegion.getCondition()));
} }
...@@ -313,21 +271,55 @@ public class RegionMaker { ...@@ -313,21 +271,55 @@ public class RegionMaker {
&& outAttrs.get(AttributeType.LOOP) != loop && outAttrs.get(AttributeType.LOOP) != loop
&& stack.peekRegion() instanceof LoopRegion) { && stack.peekRegion() instanceof LoopRegion) {
LoopRegion outerLoop = (LoopRegion) stack.peekRegion(); LoopRegion outerLoop = (LoopRegion) stack.peekRegion();
if (outerLoop.getBody() == null /* processing not yet finished */ boolean notYetProcessed = outerLoop.getBody() == null;
|| RegionUtils.isRegionContainsBlock(outerLoop, out)) { if (notYetProcessed || RegionUtils.isRegionContainsBlock(outerLoop, out)) {
// exit to outer loop which already processed // exit to outer loop which already processed
out = null; out = null;
} }
} }
stack.push(loopRegion);
stack.addExit(out); stack.addExit(out);
loopRegion.setBody(makeRegion(loopBody, stack)); loopRegion.setBody(makeRegion(loopBody, stack));
stack.pop();
} }
stack.pop();
return out; return out;
} }
private BlockNode makeEndlessLoop(IRegion curRegion, RegionStack stack, LoopAttr loop, BlockNode loopStart) {
LoopRegion loopRegion;
loopRegion = new LoopRegion(curRegion, null, false);
curRegion.getSubBlocks().add(loopRegion);
loopStart.getAttributes().remove(AttributeType.LOOP);
stack.push(loopRegion);
Region body = makeRegion(loopStart, stack);
if (!RegionUtils.isRegionContainsBlock(body, loop.getEnd())) {
body.getSubBlocks().add(loop.getEnd());
}
loopRegion.setBody(body);
stack.pop();
loopStart.getAttributes().add(loop);
BlockNode next = BlockUtils.getNextBlock(loop.getEnd());
return RegionUtils.isRegionContainsBlock(body, next) ? null : next;
}
private void insertBreak(RegionStack stack, BlockNode loopExit, Edge exitEdge) {
BlockNode prev = null;
BlockNode exit = exitEdge.getTarget();
while (exit != null) {
if (prev != null && isPathExists(loopExit, exit)) {
// found cross
if (!exit.getAttributes().contains(AttributeFlag.RETURN)) {
prev.getInstructions().add(new InsnNode(InsnType.BREAK, 0));
stack.addExit(exit);
}
return;
}
prev = exit;
exit = BlockUtils.getNextBlock(exit);
}
}
private 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) {
...@@ -509,8 +501,8 @@ public class RegionMaker { ...@@ -509,8 +501,8 @@ public class RegionMaker {
IfCondition nestedCondition = IfCondition.fromIfNode(nestedIfInsn); IfCondition nestedCondition = IfCondition.fromIfNode(nestedIfInsn);
if (isPathExists(bElse, nestedIfBlock)) { if (isPathExists(bElse, nestedIfBlock)) {
// else branch // else branch
if (!isEqualReturns(bThen, nbThen)) { if (!isEqualPaths(bThen, nbThen)) {
if (!isEqualReturns(bThen, nbElse)) { if (!isEqualPaths(bThen, nbElse)) {
// not connected conditions // not connected conditions
break; break;
} }
...@@ -520,8 +512,8 @@ public class RegionMaker { ...@@ -520,8 +512,8 @@ public class RegionMaker {
condition = IfCondition.merge(Mode.OR, condition, nestedCondition); condition = IfCondition.merge(Mode.OR, condition, nestedCondition);
} else { } else {
// then branch // then branch
if (!isEqualReturns(bElse, nbElse)) { if (!isEqualPaths(bElse, nbElse)) {
if (!isEqualReturns(bElse, nbThen)) { if (!isEqualPaths(bElse, nbThen)) {
// not connected conditions // not connected conditions
break; break;
} }
...@@ -533,7 +525,7 @@ public class RegionMaker { ...@@ -533,7 +525,7 @@ public class RegionMaker {
result = new IfInfo(); result = new IfInfo();
result.setCondition(condition); result.setCondition(condition);
nestedIfBlock.getAttributes().add(AttributeFlag.SKIP); nestedIfBlock.getAttributes().add(AttributeFlag.SKIP);
bThen.getAttributes().add(AttributeFlag.SKIP); skipSimplePath(bThen);
if (merged != null) { if (merged != null) {
merged.add(nestedIfBlock); merged.add(nestedIfBlock);
...@@ -702,27 +694,54 @@ public class RegionMaker { ...@@ -702,27 +694,54 @@ public class RegionMaker {
handler.getHandlerRegion().getAttributes().add(excHandlerAttr); handler.getHandlerRegion().getAttributes().add(excHandlerAttr);
} }
private static boolean isEqualReturns(BlockNode b1, BlockNode b2) { private void skipSimplePath(BlockNode block) {
while (block != null
&& block.getCleanSuccessors().size() < 2
&& block.getPredecessors().size() == 1) {
block.getAttributes().add(AttributeFlag.SKIP);
block = BlockUtils.getNextBlock(block);
}
}
private static boolean isEqualPaths(BlockNode b1, BlockNode b2) {
if (b1 == b2) { if (b1 == b2) {
return true; return true;
} }
if (b1 == null || b2 == null) { if (b1 == null || b2 == null) {
return false; return false;
} }
List<InsnNode> b1Insns = b1.getInstructions(); if (isReturnBlocks(b1, b2)) {
List<InsnNode> b2Insns = b2.getInstructions(); return true;
if (b1Insns.size() != 1 || b2Insns.size() != 1) { }
if (isSyntheticPath(b1, b2)) {
return true;
}
return false;
}
private static boolean isSyntheticPath(BlockNode b1, BlockNode b2) {
if (!b1.isSynthetic() || !b2.isSynthetic()) {
return false; return false;
} }
if (!b1.getAttributes().contains(AttributeFlag.RETURN) BlockNode n1 = BlockUtils.getNextBlock(b1);
|| !b2.getAttributes().contains(AttributeFlag.RETURN)) { BlockNode n2 = BlockUtils.getNextBlock(b2);
return isEqualPaths(n1, n2);
}
private static boolean isReturnBlocks(BlockNode b1, BlockNode b2) {
if (!b1.isReturnBlock() || !b2.isReturnBlock()) {
return false; return false;
} }
if (b1Insns.get(0).getArgsCount() > 0) { List<InsnNode> b1Insns = b1.getInstructions();
if (b1Insns.get(0).getArg(0) != b2Insns.get(0).getArg(0)) { List<InsnNode> b2Insns = b2.getInstructions();
if (b1Insns.size() != 1 || b2Insns.size() != 1) {
return false; return false;
} }
InsnNode i1 = b1Insns.get(0);
InsnNode i2 = b2Insns.get(0);
if (i1.getArgsCount() == 0 || i2.getArgsCount() == 0) {
return false;
} }
return true; return i1.getArg(0).equals(i2.getArg(0));
} }
} }
...@@ -10,7 +10,11 @@ import jadx.core.dex.visitors.AbstractVisitor; ...@@ -10,7 +10,11 @@ import jadx.core.dex.visitors.AbstractVisitor;
import java.util.List; import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TypeResolver extends AbstractVisitor { public class TypeResolver extends AbstractVisitor {
private static final Logger LOG = LoggerFactory.getLogger(TypeResolver.class);
@Override @Override
public void visit(MethodNode mth) { public void visit(MethodNode mth) {
...@@ -67,11 +71,22 @@ public class TypeResolver extends AbstractVisitor { ...@@ -67,11 +71,22 @@ public class TypeResolver extends AbstractVisitor {
} }
} while (changed); } while (changed);
int i = 0;
do {
changed = false;
for (BlockNode block : mth.getBasicBlocks()) { for (BlockNode block : mth.getBasicBlocks()) {
for (BlockNode dest : block.getSuccessors()) { for (BlockNode dest : block.getSuccessors()) {
connectEdges(mth, block, dest, false); if (connectEdges(mth, block, dest, false)) {
changed = true;
}
}
} }
i++;
if (i > 10) {
LOG.warn("Can't resolve types (forward connectEdges pass) in method: {}", mth);
break;
} }
} while (changed && i < 10);
} }
private static boolean connectEdges(MethodNode mth, BlockNode from, BlockNode to, boolean back) { private static boolean connectEdges(MethodNode mth, BlockNode from, BlockNode to, boolean back) {
......
...@@ -10,16 +10,15 @@ import static org.junit.Assert.assertThat; ...@@ -10,16 +10,15 @@ import static org.junit.Assert.assertThat;
public class TestReturnWrapping extends InternalJadxTest { public class TestReturnWrapping extends InternalJadxTest {
public static class TestCls { public static class TestCls {
/**/
public static int f1(int arg0) { public static int f1(int arg0) {
switch (arg0) { switch (arg0) {
case 1: case 1:
return 255; return 255;
} }
return arg0 + 1; return arg0 + 1;
}/**/ }
/**/
public static Object f2(Object arg0, int arg1) { public static Object f2(Object arg0, int arg1) {
Object ret = null; Object ret = null;
int i = arg1; int i = arg1;
...@@ -34,9 +33,8 @@ public class TestReturnWrapping extends InternalJadxTest { ...@@ -34,9 +33,8 @@ public class TestReturnWrapping extends InternalJadxTest {
} }
return i > 128 ? arg0.toString() + ret.toString() : i; return i > 128 ? arg0.toString() + ret.toString() : i;
} }
}/**/ }
/**/
public static int f3(int arg0) { public static int f3(int arg0) {
while (arg0 > 10) { while (arg0 > 10) {
int abc = 951; int abc = 951;
...@@ -46,7 +44,7 @@ public class TestReturnWrapping extends InternalJadxTest { ...@@ -46,7 +44,7 @@ public class TestReturnWrapping extends InternalJadxTest {
arg0 -= abc; arg0 -= abc;
} }
return arg0; return arg0;
}/**/ }
} }
@Test @Test
...@@ -56,7 +54,6 @@ public class TestReturnWrapping extends InternalJadxTest { ...@@ -56,7 +54,6 @@ public class TestReturnWrapping extends InternalJadxTest {
assertThat(code, containsString("return 255;")); assertThat(code, containsString("return 255;"));
assertThat(code, containsString("return arg0 + 1;")); assertThat(code, containsString("return arg0 + 1;"));
//assertThat(code, containsString("return Integer.toHexString(i);"));
assertThat(code, containsString("return i > 128 ? arg0.toString() + ret.toString() : Integer.valueOf(i);")); assertThat(code, containsString("return i > 128 ? arg0.toString() + ret.toString() : Integer.valueOf(i);"));
assertThat(code, containsString("return arg0 + 2;")); assertThat(code, containsString("return arg0 + 2;"));
assertThat(code, containsString("arg0 -= 951;")); assertThat(code, containsString("arg0 -= 951;"));
......
package jadx.tests.internal.loops;
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.assertEquals;
import static org.junit.Assert.assertThat;
public class TestBreakInLoop extends InternalJadxTest {
public static class TestCls {
private int f;
private void test(int[] a, int b) {
int i = 0;
while (i < a.length) {
a[i]++;
if (i < b) {
break;
}
i++;
}
this.f++;
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
assertEquals(count(code, "this.f++;"), 1);
assertThat(code, containsString("if (i < b) {"));
assertThat(code, containsString("break;"));
}
}
...@@ -10,8 +10,7 @@ import static org.junit.Assert.assertThat; ...@@ -10,8 +10,7 @@ import static org.junit.Assert.assertThat;
public class TestLoopCondition extends InternalJadxTest { public class TestLoopCondition extends InternalJadxTest {
@SuppressWarnings("serial") public static class TestCls {
public static class TestCls extends Exception {
public String f; public String f;
private void setEnabled(boolean r1z) { private void setEnabled(boolean r1z) {
...@@ -32,14 +31,6 @@ public class TestLoopCondition extends InternalJadxTest { ...@@ -32,14 +31,6 @@ public class TestLoopCondition extends InternalJadxTest {
setEnabled(false); setEnabled(false);
} }
public int testComplexIfInLoop(boolean a) {
int i = 0;
while (a && i < 10) {
i++;
}
return i;
}
private void testMoreComplexIfInLoop(java.util.ArrayList<String> list) throws Exception { private void testMoreComplexIfInLoop(java.util.ArrayList<String> list) throws Exception {
for (int i = 0; i != 16 && i < 255; i++) { for (int i = 0; i != 16 && i < 255; i++) {
list.set(i, "ABC"); list.set(i, "ABC");
...@@ -55,9 +46,9 @@ public class TestLoopCondition extends InternalJadxTest { ...@@ -55,9 +46,9 @@ public class TestLoopCondition extends InternalJadxTest {
public void test() { public void test() {
ClassNode cls = getClassNode(TestCls.class); ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString(); String code = cls.getCode().toString();
System.out.println(code);
assertThat(code, containsString("i < this.f.length()")); assertThat(code, containsString("i < this.f.length()"));
assertThat(code, containsString("while (a && i < 10) {"));
assertThat(code, containsString("list.set(i, \"ABC\")")); assertThat(code, containsString("list.set(i, \"ABC\")"));
assertThat(code, containsString("list.set(i, \"DEF\")")); assertThat(code, containsString("list.set(i, \"DEF\")"));
} }
......
package jadx.tests.internal.loops;
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 TestLoopCondition2 extends InternalJadxTest {
public static class TestCls {
public int test(boolean a) {
int i = 0;
while (a && i < 10) {
i++;
}
return i;
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
assertThat(code, containsString("while (a && i < 10) {"));
}
}
...@@ -222,15 +222,13 @@ public class TestCF3 extends AbstractTest { ...@@ -222,15 +222,13 @@ public class TestCF3 extends AbstractTest {
new ArrayList<String>(Arrays.asList("a1", "b2")))); new ArrayList<String>(Arrays.asList("a1", "b2"))));
List<String> list1 = Arrays.asList(null, "a", "b"); List<String> list1 = Arrays.asList(null, "a", "b");
// TODO this line required to omit generic information because it create List<Object>
// List<String> list2 = Arrays.asList(null, null, null);
assertEquals(testReturnInLoop(list1), "a"); assertEquals(testReturnInLoop(list1), "a");
assertEquals(testReturnInLoop2(list1), "a"); assertEquals(testReturnInLoop2(list1), "a");
// assertEquals(testReturnInLoop(list2), "error"); // TODO this line required to omit generic information because it create List<Object>
// assertEquals(testReturnInLoop2(list2), "error"); // List<String> list2 = Arrays.asList(null, null, null);
// assertEquals(testReturnInLoop(list2), "error");
// assertEquals(testReturnInLoop2(list2), "error");
// assertTrue(testLabeledBreakContinue()); // assertTrue(testLabeledBreakContinue());
......
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