core: insert 'continue' instruction
This commit is contained in:
@@ -440,7 +440,7 @@ public class InsnGen {
|
||||
makeTernary((TernaryInsn) insn, code, state);
|
||||
break;
|
||||
|
||||
case ARGS:
|
||||
case ONE_ARG:
|
||||
addArg(code, insn.getArg(0));
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
public enum InsnType {
|
||||
NOP, // replacement for removed instructions
|
||||
|
||||
CONST,
|
||||
CONST_STR,
|
||||
@@ -48,16 +47,24 @@ public enum InsnType {
|
||||
|
||||
INVOKE,
|
||||
|
||||
// additional instructions
|
||||
// *** Additional instructions ***
|
||||
|
||||
// replacement for removed instructions
|
||||
NOP,
|
||||
|
||||
TERNARY,
|
||||
CONSTRUCTOR,
|
||||
|
||||
BREAK,
|
||||
CONTINUE,
|
||||
|
||||
STR_CONCAT, // strings concatenation
|
||||
// strings concatenation
|
||||
STR_CONCAT,
|
||||
|
||||
TERNARY,
|
||||
ARGS, // just generate arguments
|
||||
// just generate one argument
|
||||
ONE_ARG,
|
||||
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 {
|
||||
}
|
||||
|
||||
public static InsnNode wrapArg(InsnArg arg) {
|
||||
InsnNode insn = new InsnNode(InsnType.ARGS, 1);
|
||||
InsnNode insn = new InsnNode(InsnType.ONE_ARG, 1);
|
||||
insn.addArg(arg);
|
||||
return insn;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -519,7 +520,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
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);
|
||||
versions[regNum]++;
|
||||
if (sVars.isEmpty()) {
|
||||
|
||||
@@ -427,9 +427,9 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// insert additional blocks if loop has several exits
|
||||
if (loops.size() == 1) {
|
||||
LoopInfo loop = loops.get(0);
|
||||
// insert additional blocks for possible 'break' insertion
|
||||
List<Edge> edges = loop.getExitEdges();
|
||||
if (!edges.isEmpty()) {
|
||||
boolean change = false;
|
||||
@@ -444,6 +444,21 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
||||
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);
|
||||
|
||||
@@ -239,13 +239,19 @@ public class IfMakerHelper {
|
||||
}
|
||||
|
||||
static void confirmMerge(IfInfo info) {
|
||||
for (BlockNode block : info.getMergedBlocks()) {
|
||||
block.add(AFlag.SKIP);
|
||||
if (info.getMergedBlocks().size() > 1) {
|
||||
for (BlockNode block : info.getMergedBlocks()) {
|
||||
if (block != info.getIfBlock()) {
|
||||
block.add(AFlag.SKIP);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (BlockNode block : info.getSkipBlocks()) {
|
||||
block.add(AFlag.SKIP);
|
||||
if (!info.getSkipBlocks().isEmpty()) {
|
||||
for (BlockNode block : info.getSkipBlocks()) {
|
||||
block.add(AFlag.SKIP);
|
||||
}
|
||||
info.getSkipBlocks().clear();
|
||||
}
|
||||
info.getSkipBlocks().clear();
|
||||
}
|
||||
|
||||
private static IfInfo getNextIf(IfInfo info, BlockNode block) {
|
||||
|
||||
@@ -166,7 +166,9 @@ public class RegionMaker {
|
||||
|
||||
LoopRegion loopRegion = makeLoopRegion(curRegion, loop, exitBlocks);
|
||||
if (loopRegion == null) {
|
||||
return makeEndlessLoop(curRegion, stack, loop, loopStart);
|
||||
BlockNode exit = makeEndlessLoop(curRegion, stack, loop, loopStart);
|
||||
insertContinueInsns(loop);
|
||||
return exit;
|
||||
}
|
||||
curRegion.getSubBlocks().add(loopRegion);
|
||||
IRegion outerRegion = stack.peekRegion();
|
||||
@@ -233,6 +235,7 @@ public class RegionMaker {
|
||||
loopRegion.setBody(body);
|
||||
}
|
||||
stack.pop();
|
||||
insertContinueInsns(loop);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -359,9 +362,12 @@ public class RegionMaker {
|
||||
if (source.contains(AType.CATCH_BLOCK)
|
||||
&& source.getSuccessors().size() == 2) {
|
||||
BlockNode other = BlockUtils.selectOther(loopExit, source.getSuccessors());
|
||||
if (other != null && other.contains(AType.EXC_HANDLER)) {
|
||||
insertBlock = source;
|
||||
confirm = true;
|
||||
if (other != null) {
|
||||
other = BlockUtils.skipSyntheticSuccessor(other);
|
||||
if (other.contains(AType.EXC_HANDLER)) {
|
||||
insertBlock = source;
|
||||
confirm = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,6 +411,45 @@ public class RegionMaker {
|
||||
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 BlockNode processMonitorEnter(IRegion curRegion, BlockNode block, InsnNode insn, RegionStack stack) {
|
||||
@@ -444,7 +489,7 @@ public class RegionMaker {
|
||||
* Traverse from monitor-enter thru successors and collect blocks contains monitor-exit
|
||||
*/
|
||||
private static void traverseMonitorExits(SynchronizedRegion region, InsnArg arg, BlockNode block,
|
||||
Set<BlockNode> exits, Set<BlockNode> visited) {
|
||||
Set<BlockNode> exits, Set<BlockNode> visited) {
|
||||
visited.add(block);
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
if (insn.getType() == InsnType.MONITOR_EXIT
|
||||
|
||||
@@ -439,4 +439,14 @@ public class BlockUtils {
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
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++;"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user