Commit c18074f6 authored by Skylot's avatar Skylot

core: insert 'continue' instruction

parent 8a706193
...@@ -440,7 +440,7 @@ public class InsnGen { ...@@ -440,7 +440,7 @@ public class InsnGen {
makeTernary((TernaryInsn) insn, code, state); makeTernary((TernaryInsn) insn, code, state);
break; break;
case ARGS: case ONE_ARG:
addArg(code, insn.getArg(0)); addArg(code, insn.getArg(0));
break; break;
......
package jadx.core.dex.instructions; package jadx.core.dex.instructions;
public enum InsnType { public enum InsnType {
NOP, // replacement for removed instructions
CONST, CONST,
CONST_STR, CONST_STR,
...@@ -48,16 +47,24 @@ public enum InsnType { ...@@ -48,16 +47,24 @@ public enum InsnType {
INVOKE, INVOKE,
// additional instructions // *** Additional instructions ***
// replacement for removed instructions
NOP,
TERNARY,
CONSTRUCTOR, CONSTRUCTOR,
BREAK, BREAK,
CONTINUE, CONTINUE,
STR_CONCAT, // strings concatenation // strings concatenation
STR_CONCAT,
TERNARY, // just generate one argument
ARGS, // just generate arguments ONE_ARG,
PHI, PHI,
NEW_MULTIDIM_ARRAY // TODO: now multidimensional arrays created using Array.newInstance function // TODO: now multidimensional arrays created using Array.newInstance function
NEW_MULTIDIM_ARRAY
} }
...@@ -36,7 +36,7 @@ public class InsnNode extends LineAttrNode { ...@@ -36,7 +36,7 @@ public class InsnNode extends LineAttrNode {
} }
public static InsnNode wrapArg(InsnArg arg) { public static InsnNode wrapArg(InsnArg arg) {
InsnNode insn = new InsnNode(InsnType.ARGS, 1); InsnNode insn = new InsnNode(InsnType.ONE_ARG, 1);
insn.addArg(arg); insn.addArg(arg);
return insn; return insn;
} }
......
...@@ -34,6 +34,7 @@ import java.util.List; ...@@ -34,6 +34,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -519,7 +520,7 @@ public class MethodNode extends LineAttrNode implements ILoadable { ...@@ -519,7 +520,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
return debugInfoOffset; return debugInfoOffset;
} }
public SSAVar makeNewSVar(int regNum, int[] versions, RegisterArg arg) { public SSAVar makeNewSVar(int regNum, int[] versions, @NotNull RegisterArg arg) {
SSAVar var = new SSAVar(regNum, versions[regNum], arg); SSAVar var = new SSAVar(regNum, versions[regNum], arg);
versions[regNum]++; versions[regNum]++;
if (sVars.isEmpty()) { if (sVars.isEmpty()) {
......
...@@ -427,9 +427,9 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -427,9 +427,9 @@ public class BlockMakerVisitor extends AbstractVisitor {
return true; return true;
} }
} }
// insert additional blocks if loop has several exits
if (loops.size() == 1) { if (loops.size() == 1) {
LoopInfo loop = loops.get(0); LoopInfo loop = loops.get(0);
// insert additional blocks for possible 'break' insertion
List<Edge> edges = loop.getExitEdges(); List<Edge> edges = loop.getExitEdges();
if (!edges.isEmpty()) { if (!edges.isEmpty()) {
boolean change = false; boolean change = false;
...@@ -444,6 +444,21 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -444,6 +444,21 @@ public class BlockMakerVisitor extends AbstractVisitor {
return true; return true;
} }
} }
// insert additional blocks for possible 'continue' insertion
BlockNode loopEnd = loop.getEnd();
if (loopEnd.getPredecessors().size() > 1) {
boolean change = false;
List<BlockNode> nodes = new ArrayList<BlockNode>(loopEnd.getPredecessors());
for (BlockNode pred : nodes) {
if (!pred.contains(AFlag.SYNTHETIC)) {
insertBlockBetween(mth, pred, loopEnd);
change = true;
}
}
if (change) {
return true;
}
}
} }
} }
return splitReturn(mth); return splitReturn(mth);
......
...@@ -239,14 +239,20 @@ public class IfMakerHelper { ...@@ -239,14 +239,20 @@ public class IfMakerHelper {
} }
static void confirmMerge(IfInfo info) { static void confirmMerge(IfInfo info) {
if (info.getMergedBlocks().size() > 1) {
for (BlockNode block : info.getMergedBlocks()) { for (BlockNode block : info.getMergedBlocks()) {
if (block != info.getIfBlock()) {
block.add(AFlag.SKIP); block.add(AFlag.SKIP);
} }
}
}
if (!info.getSkipBlocks().isEmpty()) {
for (BlockNode block : info.getSkipBlocks()) { for (BlockNode block : info.getSkipBlocks()) {
block.add(AFlag.SKIP); block.add(AFlag.SKIP);
} }
info.getSkipBlocks().clear(); info.getSkipBlocks().clear();
} }
}
private static IfInfo getNextIf(IfInfo info, BlockNode block) { private static IfInfo getNextIf(IfInfo info, BlockNode block) {
if (!canSelectNext(info, block)) { if (!canSelectNext(info, block)) {
......
...@@ -166,7 +166,9 @@ public class RegionMaker { ...@@ -166,7 +166,9 @@ public class RegionMaker {
LoopRegion loopRegion = makeLoopRegion(curRegion, loop, exitBlocks); LoopRegion loopRegion = makeLoopRegion(curRegion, loop, exitBlocks);
if (loopRegion == null) { if (loopRegion == null) {
return makeEndlessLoop(curRegion, stack, loop, loopStart); BlockNode exit = makeEndlessLoop(curRegion, stack, loop, loopStart);
insertContinueInsns(loop);
return exit;
} }
curRegion.getSubBlocks().add(loopRegion); curRegion.getSubBlocks().add(loopRegion);
IRegion outerRegion = stack.peekRegion(); IRegion outerRegion = stack.peekRegion();
...@@ -233,6 +235,7 @@ public class RegionMaker { ...@@ -233,6 +235,7 @@ public class RegionMaker {
loopRegion.setBody(body); loopRegion.setBody(body);
} }
stack.pop(); stack.pop();
insertContinueInsns(loop);
return out; return out;
} }
...@@ -359,12 +362,15 @@ public class RegionMaker { ...@@ -359,12 +362,15 @@ public class RegionMaker {
if (source.contains(AType.CATCH_BLOCK) if (source.contains(AType.CATCH_BLOCK)
&& source.getSuccessors().size() == 2) { && source.getSuccessors().size() == 2) {
BlockNode other = BlockUtils.selectOther(loopExit, source.getSuccessors()); BlockNode other = BlockUtils.selectOther(loopExit, source.getSuccessors());
if (other != null && other.contains(AType.EXC_HANDLER)) { if (other != null) {
other = BlockUtils.skipSyntheticSuccessor(other);
if (other.contains(AType.EXC_HANDLER)) {
insertBlock = source; insertBlock = source;
confirm = true; confirm = true;
} }
} }
} }
}
if (!confirm) { if (!confirm) {
while (exit != null) { while (exit != null) {
if (insertBlock != null && isPathExists(loopExit, exit)) { if (insertBlock != null && isPathExists(loopExit, exit)) {
...@@ -405,6 +411,45 @@ public class RegionMaker { ...@@ -405,6 +411,45 @@ public class RegionMaker {
return true; return true;
} }
private static void insertContinueInsns(LoopInfo loop) {
BlockNode loopEnd = loop.getEnd();
List<BlockNode> predecessors = loopEnd.getPredecessors();
if (predecessors.size() <= 1) {
return;
}
for (BlockNode pred : predecessors) {
if (!pred.contains(AFlag.SYNTHETIC)
|| BlockUtils.checkLastInsnType(pred, InsnType.CONTINUE)) {
continue;
}
List<BlockNode> nodes = pred.getPredecessors();
if (nodes.isEmpty()) {
continue;
}
BlockNode codePred = nodes.get(0);
if (codePred.contains(AFlag.SKIP)) {
continue;
}
if (!isDominatedOnBlocks(codePred, predecessors)) {
for (BlockNode blockNode : predecessors) {
if (blockNode != pred && BlockUtils.isPathExists(codePred, blockNode)) {
InsnNode cont = new InsnNode(InsnType.CONTINUE, 0);
pred.getInstructions().add(cont);
}
}
}
}
}
private static boolean isDominatedOnBlocks(BlockNode dom, List<BlockNode> blocks) {
for (BlockNode node : blocks) {
if (!node.isDominator(dom)) {
return false;
}
}
return true;
}
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) {
......
...@@ -439,4 +439,14 @@ public class BlockUtils { ...@@ -439,4 +439,14 @@ public class BlockUtils {
} }
return block == end; return block == end;
} }
/**
* Return successor of synthetic block or same block otherwise.
*/
public static BlockNode skipSyntheticSuccessor(BlockNode block) {
if (block.isSynthetic() && !block.getSuccessors().isEmpty()) {
return block.getSuccessors().get(0);
}
return block;
}
} }
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.assertThat;
public class TestContinueInLoop extends IntegrationTest {
public static class TestCls {
private int f;
private void test(int[] a, int b) {
for (int i = 0; i < a.length; i++) {
int v = a[i];
if (v < b) {
a[i]++;
} else if (v > b) {
a[i]--;
} else {
continue;
}
if (i < b) {
break;
}
}
this.f++;
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("for (int i = 0; i < a.length; i++) {"));
assertThat(code, containsOne("if (i < b) {"));
assertThat(code, containsOne("continue;"));
assertThat(code, containsOne("break;"));
assertThat(code, containsOne("this.f++;"));
}
}
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