fix: resolve some cases of switch in loop (#876)

This commit is contained in:
Skylot
2020-03-21 18:41:54 +00:00
parent 4cdad0e83e
commit 2da772df8e
4 changed files with 78 additions and 14 deletions
@@ -119,14 +119,23 @@ public class SwitchNode extends TargetInsnNode {
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
for (int i = 0; i < targets.length; i++) {
sb.append(CodeWriter.NL);
sb.append(" case ").append(keys[i]);
sb.append(": goto ").append(InsnUtils.formatOffset(targets[i]));
}
if (def != -1) {
sb.append(CodeWriter.NL);
sb.append(" default: goto ").append(InsnUtils.formatOffset(def));
if (targetBlocks == null) {
for (int i = 0; i < keys.length; i++) {
sb.append(CodeWriter.NL);
sb.append(" case ").append(keys[i]).append(": goto ").append(InsnUtils.formatOffset(targets[i]));
}
if (def != -1) {
sb.append(CodeWriter.NL);
sb.append(" default: goto ").append(InsnUtils.formatOffset(def));
}
} else {
for (int i = 0; i < keys.length; i++) {
sb.append(CodeWriter.NL);
sb.append(" case ").append(keys[i]).append(": goto ").append(targetBlocks[i]);
}
if (def != -1) {
sb.append(CodeWriter.NL).append(" default: goto ").append(defTargetBlock);
}
}
return sb.toString();
}
@@ -761,14 +761,30 @@ public class RegionMaker {
out = calcPostDomOut(mth, block, mth.getExitBlocks());
} else {
BlockNode loopEnd = loop.getEnd();
// treat 'continue' as exit
out = calcPostDomOut(mth, block, loopEnd.getPredecessors());
if (out != null) {
insertContinueInSwitch(block, out, loopEnd);
stack.addExit(loop.getStart());
if (stack.containsExit(block)
|| block == loopEnd
|| loopEnd.getPredecessors().contains(block)) {
// in exits or last insn in loop => no 'out' block
out = null;
} else {
// no 'continue'
out = calcPostDomOut(mth, block, Collections.singletonList(loopEnd));
// treat 'continue' as exit
out = calcPostDomOut(mth, block, loopEnd.getPredecessors());
if (out != null) {
insertContinueInSwitch(block, out, loopEnd);
} else {
// no 'continue'
out = calcPostDomOut(mth, block, Collections.singletonList(loopEnd));
}
}
if (out == loop.getStart()) {
// no other outs instead back edge to loop start
out = null;
}
}
if (out != null && processedBlocks.get(out.getId())) {
// out block already processed, prevent endless loop
throw new JadxRuntimeException("Failed to find switch 'out' block");
}
SwitchRegion sw = new SwitchRegion(currentRegion, block);
@@ -21,6 +21,7 @@ import jadx.core.utils.exceptions.JadxOverflowException;
public class ErrorsCounter {
private static final Logger LOG = LoggerFactory.getLogger(ErrorsCounter.class);
private static final boolean PRINT_MTH_SIZE = true;
private final Set<IAttributeNode> errorNodes = new HashSet<>();
private int errorsCount;
@@ -40,6 +41,10 @@ public class ErrorsCounter {
errorsCount++;
String msg = formatMsg(node, error);
if (PRINT_MTH_SIZE && node instanceof MethodNode) {
long insnsCount = ((MethodNode) node).countInsns();
msg = "[" + insnsCount + "] " + msg;
}
if (e == null) {
LOG.error(msg);
} else if (e instanceof JadxOverflowException) {
@@ -0,0 +1,34 @@
package jadx.tests.integration.switches;
import org.junit.jupiter.api.Test;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestSwitchInLoop2 extends IntegrationTest {
public static class TestCls {
public boolean test() {
while (true) {
switch (call()) {
case 0:
return false;
case 1:
return true;
}
}
}
private int call() {
return 0;
}
}
@Test
public void test() {
assertThat(getClassNode(TestCls.class))
.code()
.containsOne("while (true) {")
.containsOne("switch (call()) {");
}
}