Commit 68f5565b authored by Skylot's avatar Skylot

core: improve processing of 'try/catch' and 'break' in loops

parent c552fb85
...@@ -44,7 +44,7 @@ public class InsnDecoder { ...@@ -44,7 +44,7 @@ public class InsnDecoder {
while (in.hasMore()) { while (in.hasMore()) {
decoded[in.cursor()] = DecodedInstruction.decode(in); decoded[in.cursor()] = DecodedInstruction.decode(in);
} }
} catch (EOFException e) { } catch (Exception e) {
throw new DecodeException(method, "", e); throw new DecodeException(method, "", e);
} }
insnArr = decoded; insnArr = decoded;
......
...@@ -230,6 +230,9 @@ public class MethodNode extends LineAttrNode implements ILoadable { ...@@ -230,6 +230,9 @@ public class MethodNode extends LineAttrNode implements ILoadable {
InsnNode[] insnByOffset = instructions; InsnNode[] insnByOffset = instructions;
CatchHandler[] catchBlocks = mthCode.getCatchHandlers(); CatchHandler[] catchBlocks = mthCode.getCatchHandlers();
Try[] tries = mthCode.getTries(); Try[] tries = mthCode.getTries();
if (catchBlocks.length == 0 && tries.length == 0) {
return;
}
int hc = 0; int hc = 0;
Set<Integer> addrs = new HashSet<Integer>(); Set<Integer> addrs = new HashSet<Integer>();
...@@ -287,13 +290,17 @@ public class MethodNode extends LineAttrNode implements ILoadable { ...@@ -287,13 +290,17 @@ public class MethodNode extends LineAttrNode implements ILoadable {
int offset = aTry.getStartAddress(); int offset = aTry.getStartAddress();
int end = offset + aTry.getInstructionCount() - 1; int end = offset + aTry.getInstructionCount() - 1;
insnByOffset[offset].add(AFlag.TRY_ENTER); InsnNode insn = insnByOffset[offset];
insn.add(AFlag.TRY_ENTER);
while (offset <= end && offset >= 0) { while (offset <= end && offset >= 0) {
catchBlock.addInsn(insnByOffset[offset]); insn = insnByOffset[offset];
catchBlock.addInsn(insn);
offset = InsnDecoder.getNextInsnOffset(insnByOffset, offset); offset = InsnDecoder.getNextInsnOffset(insnByOffset, offset);
} }
if (insnByOffset[end] != null) { if (insnByOffset[end] != null) {
insnByOffset[end].add(AFlag.TRY_LEAVE); insnByOffset[end].add(AFlag.TRY_LEAVE);
} else {
insn.add(AFlag.TRY_LEAVE);
} }
} }
} }
...@@ -498,13 +505,12 @@ public class MethodNode extends LineAttrNode implements ILoadable { ...@@ -498,13 +505,12 @@ public class MethodNode extends LineAttrNode implements ILoadable {
/** workaround for non-static inner class constructor, that has /** workaround for non-static inner class constructor, that has
* synthetic argument */ * synthetic argument */
if ((parentClass != null) && parentClass.getClassInfo().isInner()) { if (parentClass.getClassInfo().isInner()
if (!parentClass.getAccessFlags().isStatic()) { && !parentClass.getAccessFlags().isStatic()) {
ClassNode outerCls = parentClass.getParentClass(); ClassNode outerCls = parentClass.getParentClass();
if ((argsList != null) && (argsList.size() >= 1)) { if ((argsList != null) && (argsList.size() >= 1)) {
if (argsList.get(0).getType().equals(outerCls.getClassInfo().getType())) { if (argsList.get(0).getType().equals(outerCls.getClassInfo().getType())) {
defaultArgCount = 1; defaultArgCount = 1;
}
} }
} }
} }
......
...@@ -88,25 +88,24 @@ public final class LoopRegion extends AbstractRegion { ...@@ -88,25 +88,24 @@ public final class LoopRegion extends AbstractRegion {
InsnNode insn = insns.get(i); InsnNode insn = insns.get(i);
if (insn.getResult() == null) { if (insn.getResult() == null) {
return false; return false;
} else { }
RegisterArg res = insn.getResult(); RegisterArg res = insn.getResult();
if (res.getSVar().getUseCount() > 1) { if (res.getSVar().getUseCount() > 1) {
return false; return false;
} }
boolean found = false; boolean found = false;
// search result arg in other insns // search result arg in other insns
for (int j = i + 1; j < size; j++) { for (int j = i + 1; j < size; j++) {
if (insns.get(i).containsArg(res)) { if (insns.get(i).containsArg(res)) {
found = true;
}
}
// or in if insn
if (!found && ifInsn.containsArg(res)) {
found = true; found = true;
} }
if (!found) { }
return false; // or in if insn
} if (!found && ifInsn.containsArg(res)) {
found = true;
}
if (!found) {
return false;
} }
} }
return true; return true;
...@@ -163,6 +162,6 @@ public final class LoopRegion extends AbstractRegion { ...@@ -163,6 +162,6 @@ public final class LoopRegion extends AbstractRegion {
@Override @Override
public String toString() { public String toString() {
return "LOOP: " + baseString(); return "LOOP:" + info.getId() + ": " + baseString();
} }
} }
...@@ -103,7 +103,9 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -103,7 +103,9 @@ public class BlockMakerVisitor extends AbstractVisitor {
// add this insn in new block // add this insn in new block
block = startNewBlock(mth, -1); block = startNewBlock(mth, -1);
curBlock.add(AFlag.SYNTHETIC); curBlock.add(AFlag.SYNTHETIC);
block.addAttr(new SplitterBlockAttr(curBlock)); SplitterBlockAttr splitter = new SplitterBlockAttr(curBlock);
block.addAttr(splitter);
curBlock.addAttr(splitter);
connect(curBlock, block); connect(curBlock, block);
curBlock = block; curBlock = block;
} else { } else {
...@@ -131,12 +133,16 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -131,12 +133,16 @@ public class BlockMakerVisitor extends AbstractVisitor {
// get synthetic block for handlers // get synthetic block for handlers
SplitterBlockAttr spl = block.get(AType.SPLITTER_BLOCK); SplitterBlockAttr spl = block.get(AType.SPLITTER_BLOCK);
if (catches != null && spl != null) { if (catches != null && spl != null) {
BlockNode connBlock = spl.getBlock(); BlockNode splitterBlock = spl.getBlock();
boolean tryEnd = insn.contains(AFlag.TRY_LEAVE);
for (ExceptionHandler h : catches.getTryBlock().getHandlers()) { for (ExceptionHandler h : catches.getTryBlock().getHandlers()) {
BlockNode destBlock = getBlock(h.getHandleOffset(), blocksMap); BlockNode handlerBlock = getBlock(h.getHandleOffset(), blocksMap);
// skip self loop in handler // skip self loop in handler
if (connBlock != destBlock) { if (splitterBlock != handlerBlock) {
connect(connBlock, destBlock); connect(splitterBlock, handlerBlock);
}
if (tryEnd) {
connect(block, handlerBlock);
} }
} }
} }
......
...@@ -182,7 +182,7 @@ public class RegionMaker { ...@@ -182,7 +182,7 @@ public class RegionMaker {
loopRegion.setCondition(condInfo.getCondition()); loopRegion.setCondition(condInfo.getCondition());
exitBlocks.removeAll(condInfo.getMergedBlocks()); exitBlocks.removeAll(condInfo.getMergedBlocks());
if (exitBlocks.size() > 0) { if (!exitBlocks.isEmpty()) {
BlockNode loopExit = condInfo.getElseBlock(); BlockNode loopExit = condInfo.getElseBlock();
if (loopExit != null) { if (loopExit != null) {
// add 'break' instruction before path cross between main loop exit and sub-exit // add 'break' instruction before path cross between main loop exit and sub-exit
...@@ -264,6 +264,25 @@ public class RegionMaker { ...@@ -264,6 +264,25 @@ public class RegionMaker {
found = false; found = false;
} }
if (found) { if (found) {
List<LoopInfo> list = mth.getAllLoopsForBlock(block);
if (list.size() >= 2) {
// bad condition if successors going out of all loops
boolean allOuter = true;
for (BlockNode outerBlock : block.getCleanSuccessors()) {
List<LoopInfo> outLoopList = mth.getAllLoopsForBlock(outerBlock);
outLoopList.remove(loop);
if (!outLoopList.isEmpty()) {
// goes to outer loop
allOuter = false;
break;
}
}
if (allOuter) {
found = false;
}
}
}
if (found) {
return loopRegion; return loopRegion;
} }
} }
...@@ -281,16 +300,13 @@ public class RegionMaker { ...@@ -281,16 +300,13 @@ public class RegionMaker {
BlockNode loopExit = null; BlockNode loopExit = null;
// insert 'break' for exits // insert 'break' for exits
List<Edge> exitEdges = loop.getExitEdges(); List<Edge> exitEdges = loop.getExitEdges();
if (exitEdges.size() == 1) { for (Edge exitEdge : exitEdges) {
for (Edge exitEdge : exitEdges) { BlockNode exit = exitEdge.getTarget();
BlockNode exit = exitEdge.getTarget(); if (tryInsertBreak(stack, exit, exitEdge)) {
if (canInsertBreak(exit)) { BlockNode nextBlock = getNextBlock(exit);
exit.getInstructions().add(new InsnNode(InsnType.BREAK, 0)); if (nextBlock != null) {
BlockNode nextBlock = getNextBlock(exit); stack.addExit(nextBlock);
if (nextBlock != null) { loopExit = nextBlock;
stack.addExit(nextBlock);
loopExit = nextBlock;
}
} }
} }
} }
...@@ -313,7 +329,8 @@ public class RegionMaker { ...@@ -313,7 +329,8 @@ public class RegionMaker {
} }
private boolean canInsertBreak(BlockNode exit) { private boolean canInsertBreak(BlockNode exit) {
if (exit.contains(AFlag.RETURN)) { if (exit.contains(AFlag.RETURN)
|| BlockUtils.checkLastInsnType(exit, InsnType.BREAK)) {
return false; return false;
} }
List<BlockNode> simplePath = BlockUtils.buildSimplePath(exit); List<BlockNode> simplePath = BlockUtils.buildSimplePath(exit);
...@@ -331,35 +348,60 @@ public class RegionMaker { ...@@ -331,35 +348,60 @@ public class RegionMaker {
return true; return true;
} }
private void tryInsertBreak(RegionStack stack, BlockNode loopExit, Edge exitEdge) { private boolean tryInsertBreak(RegionStack stack, BlockNode loopExit, Edge exitEdge) {
BlockNode prev = null;
BlockNode exit = exitEdge.getTarget(); BlockNode exit = exitEdge.getTarget();
while (exit != null) { BlockNode insertBlock = null;
if (prev != null && isPathExists(loopExit, exit)) { boolean confirm = false;
// found cross // process special cases
if (canInsertBreak(exit)) { if (loopExit == exit) {
InsnNode breakInsn = new InsnNode(InsnType.BREAK, 0); // try/catch at loop end
prev.getInstructions().add(breakInsn); BlockNode source = exitEdge.getSource();
stack.addExit(exit); if (source.contains(AType.CATCH_BLOCK)) {
// add label to 'break' if needed BlockNode other = BlockUtils.selectOther(loopExit, source.getSuccessors());
List<LoopInfo> loops = mth.getAllLoopsForBlock(exitEdge.getSource()); if (other != null && other.contains(AType.EXC_HANDLER)) {
if (loops.size() >= 2) { insertBlock = source;
// find parent loop confirm = true;
for (LoopInfo loop : loops) { }
if (loop.getParentLoop() == null) { }
LoopLabelAttr labelAttr = new LoopLabelAttr(loop); }
breakInsn.addAttr(labelAttr); if (!confirm) {
loop.getStart().addAttr(labelAttr); while (exit != null) {
break; if (insertBlock != null && isPathExists(loopExit, exit)) {
} // found cross
} if (canInsertBreak(insertBlock)) {
confirm = true;
break;
} }
return false;
}
insertBlock = exit;
List<BlockNode> cs = exit.getCleanSuccessors();
exit = cs.size() == 1 ? cs.get(0) : null;
}
}
if (!confirm) {
return false;
}
InsnNode breakInsn = new InsnNode(InsnType.BREAK, 0);
insertBlock.getInstructions().add(breakInsn);
stack.addExit(exit);
// add label to 'break' if needed
List<LoopInfo> loops = mth.getAllLoopsForBlock(exitEdge.getSource());
if (loops.size() >= 2) {
// find parent loop
for (LoopInfo loop : loops) {
LoopInfo parentLoop = loop.getParentLoop();
if (parentLoop == null
&& loop.getEnd() != exit
&& !loop.getExitNodes().contains(exit)) {
LoopLabelAttr labelAttr = new LoopLabelAttr(loop);
breakInsn.addAttr(labelAttr);
loop.getStart().addAttr(labelAttr);
break;
} }
return;
} }
prev = exit;
exit = getNextBlock(exit);
} }
return true;
} }
private final Set<BlockNode> cacheSet = new HashSet<BlockNode>(); private final Set<BlockNode> cacheSet = new HashSet<BlockNode>();
......
...@@ -246,16 +246,16 @@ public class BlockUtils { ...@@ -246,16 +246,16 @@ public class BlockUtils {
Set<BlockNode> set = new HashSet<BlockNode>(); Set<BlockNode> set = new HashSet<BlockNode>();
set.add(start); set.add(start);
if (start != end) { if (start != end) {
addPredcessors(set, end, start); addPredecessors(set, end, start);
} }
return set; return set;
} }
private static void addPredcessors(Set<BlockNode> set, BlockNode from, BlockNode until) { private static void addPredecessors(Set<BlockNode> set, BlockNode from, BlockNode until) {
set.add(from); set.add(from);
for (BlockNode pred : from.getPredecessors()) { for (BlockNode pred : from.getPredecessors()) {
if (pred != until && !set.contains(pred)) { if (pred != until && !set.contains(pred)) {
addPredcessors(set, pred, until); addPredecessors(set, pred, until);
} }
} }
} }
......
package jadx.tests.integration.loops;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import org.junit.Test;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class TestNestedLoops3 extends IntegrationTest {
public static class TestCls {
int c = 0;
public int test(int b) {
int i;
loop0:
while (true) {
f1();
i = 0;
while (true) {
f2();
if (i != 0) {
break loop0;
}
i += 3;
if (b >= 16) {
break loop0;
}
try {
exc();
break;
} catch (Exception e) {
//
}
}
}
return i;
}
private void exc() throws Exception {
if (c > 200) {
throw new Exception();
}
}
private void f1() {
c += 1;
}
private void f2() {
c += 100;
}
public void check() {
test(1);
assertEquals(302, c);
}
}
@Test
public void test() {
setOutputCFG();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("} catch (Exception e) {"));
}
}
package jadx.tests.integration.loops;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import org.junit.Test;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class TestTryCatchInLoop extends IntegrationTest {
public static class TestCls {
int c = 0;
public int test() {
while (true) {
try {
exc();
break;
} catch (Exception e) {
//
}
}
if (c == 5) {
System.out.println(c);
}
return 0;
}
private void exc() throws Exception {
c++;
if (c < 3) {
throw new Exception();
}
}
public void check() {
test();
assertEquals(3, c);
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("} catch (Exception e) {"));
assertThat(code, containsOne("break;"));
}
}
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