From e2018535efa78846d3d2201c07a14266ccabbb7e Mon Sep 17 00:00:00 2001 From: Skylot Date: Tue, 19 Aug 2014 22:27:51 +0400 Subject: [PATCH] core: add ternary conditions processing --- .../java/jadx/core/codegen/ConditionGen.java | 14 +++++- .../dex/instructions/mods/TernaryInsn.java | 20 +++++++- .../jadx/core/dex/regions/IfCondition.java | 18 ++++++- .../core/dex/visitors/SimplifyVisitor.java | 4 +- .../dex/visitors/regions/IfMakerHelper.java | 50 +++++++++++++++++-- .../internal/conditions/TestConditions14.java | 9 ---- .../internal/conditions/TestTernaryInIf.java | 36 +++++++++++++ 7 files changed, 133 insertions(+), 18 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/internal/conditions/TestTernaryInIf.java diff --git a/jadx-core/src/main/java/jadx/core/codegen/ConditionGen.java b/jadx-core/src/main/java/jadx/core/codegen/ConditionGen.java index 38e55025..5f754efd 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ConditionGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ConditionGen.java @@ -33,6 +33,10 @@ public class ConditionGen extends InsnGen { addCompare(code, condition.getCompare()); break; + case TERNARY: + addTernary(code, condition); + break; + case NOT: addNot(code, condition); break; @@ -43,7 +47,7 @@ public class ConditionGen extends InsnGen { break; default: - throw new JadxRuntimeException("Unknown condition mode: " + condition); + throw new JadxRuntimeException("Unknown condition mode: " + condition.getMode()); } } @@ -94,6 +98,14 @@ public class ConditionGen extends InsnGen { addArg(code, secondArg, isArgWrapNeeded(secondArg)); } + private void addTernary(CodeWriter code, IfCondition condition) throws CodegenException { + add(code, condition.first()); + code.add(" ? "); + add(code, condition.second()); + code.add(" : "); + add(code, condition.third()); + } + private void addNot(CodeWriter code, IfCondition condition) throws CodegenException { code.add('!'); wrap(code, condition.getArgs().get(0)); diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java index 69570e5d..9e219ad4 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java @@ -11,7 +11,11 @@ import jadx.core.utils.Utils; public class TernaryInsn extends InsnNode { - private final IfCondition condition; + private IfCondition condition; + + public TernaryInsn(IfCondition condition, RegisterArg result) { + this(condition, result, LiteralArg.TRUE, LiteralArg.FALSE); + } public TernaryInsn(IfCondition condition, RegisterArg result, InsnArg th, InsnArg els) { super(InsnType.TERNARY, 2); @@ -33,6 +37,20 @@ public class TernaryInsn extends InsnNode { return condition; } + public void simplifyCondition() { + condition = IfCondition.simplify(condition); + if (condition.getMode() == IfCondition.Mode.NOT) { + invert(); + } + } + + private void invert() { + condition = IfCondition.invert(condition); + InsnArg tmp = getArg(0); + setArg(0, getArg(1)); + setArg(1, tmp); + } + @Override public boolean equals(Object obj) { if (this == obj) { diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/IfCondition.java b/jadx-core/src/main/java/jadx/core/dex/regions/IfCondition.java index bde7b99f..b4bced40 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/IfCondition.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/IfCondition.java @@ -22,6 +22,7 @@ public final class IfCondition { public enum Mode { COMPARE, + TERNARY, NOT, AND, OR @@ -64,6 +65,10 @@ public final class IfCondition { return new IfCondition(new Compare(insn)); } + public static IfCondition ternary(IfCondition a, IfCondition b, IfCondition c) { + return new IfCondition(Mode.TERNARY, Arrays.asList(a, b, c)); + } + public static IfCondition merge(Mode mode, IfCondition a, IfCondition b) { if (a.getMode() == mode) { IfCondition n = new IfCondition(a); @@ -89,6 +94,10 @@ public final class IfCondition { return args.get(1); } + public IfCondition third() { + return args.get(2); + } + public void addArg(IfCondition c) { args.add(c); } @@ -106,6 +115,8 @@ public final class IfCondition { switch (mode) { case COMPARE: return new IfCondition(cond.getCompare().invert()); + case TERNARY: + return ternary(not(cond.first()), cond.third(), cond.second()); case NOT: return cond.first(); case AND: @@ -154,7 +165,10 @@ public final class IfCondition { cond = new IfCondition(cond.getMode(), args); } if (cond.getMode() == Mode.NOT && cond.first().getMode() == Mode.NOT) { - cond = cond.first().first(); + cond = invert(cond.first()); + } + if (cond.getMode() == Mode.TERNARY && cond.first().getMode() == Mode.NOT) { + cond = invert(cond); } // for condition with a lot of negations => make invert @@ -216,6 +230,8 @@ public final class IfCondition { switch (mode) { case COMPARE: return compare.toString(); + case TERNARY: + return first() + " ? " + second() + " : " + third(); case NOT: return "!" + first(); case AND: diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java index c215784e..1fd54ee9 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java @@ -65,7 +65,7 @@ public class SimplifyVisitor extends AbstractVisitor { simplifyIf((IfNode) insn); break; case TERNARY: - simplifyTernary((TernaryInsn)insn); + simplifyTernary((TernaryInsn) insn); break; case INVOKE: @@ -117,6 +117,8 @@ public class SimplifyVisitor extends AbstractVisitor { IfCondition condition = insn.getCondition(); if (condition.isCompare()) { simplifyIf(condition.getCompare().getInsn()); + } else { + insn.simplifyCondition(); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfMakerHelper.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfMakerHelper.java index 363e663b..bc7243a6 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfMakerHelper.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfMakerHelper.java @@ -101,6 +101,11 @@ public class IfMakerHelper { return c1.size() == c2.size() && c1.containsAll(c2); } + static IfInfo searchNestedIf(IfInfo info) { + IfInfo tmp = mergeNestedIfNodes(info); + return tmp != null ? tmp : info; + } + static IfInfo mergeNestedIfNodes(IfInfo currentIf) { BlockNode curThen = currentIf.getThenBlock(); BlockNode curElse = currentIf.getElseBlock(); @@ -126,12 +131,13 @@ public class IfMakerHelper { if (!RegionMaker.isEqualPaths(curElse, nextIf.getElseBlock()) && !RegionMaker.isEqualPaths(curThen, nextIf.getThenBlock())) { // complex condition, run additional checks - if (checkConditionBranches(curThen, curElse) || checkConditionBranches(curElse, curThen)) { + if (checkConditionBranches(curThen, curElse) + || checkConditionBranches(curElse, curThen)) { return null; } BlockNode otherBranchBlock = followThenBranch ? curElse : curThen; if (!isPathExists(nextIf.getIfBlock(), otherBranchBlock)) { - return null; + return checkForTernaryInCondition(currentIf); } if (isPathExists(nextIf.getThenBlock(), otherBranchBlock) && isPathExists(nextIf.getElseBlock(), otherBranchBlock)) { @@ -152,9 +158,43 @@ public class IfMakerHelper { IfInfo result = mergeIfInfo(currentIf, nextIf, followThenBranch); // search next nested if block - IfInfo next = mergeNestedIfNodes(result); - if (next != null) { - return next; + return searchNestedIf(result); + } + + private static IfInfo checkForTernaryInCondition(IfInfo currentIf) { + IfInfo nextThen = getNextIf(currentIf, currentIf.getThenBlock()); + IfInfo nextElse = getNextIf(currentIf, currentIf.getElseBlock()); + if (nextThen == null || nextElse == null) { + return null; + } + if (!nextThen.getIfBlock().getDomFrontier().equals(nextElse.getIfBlock().getDomFrontier())) { + return null; + } + nextThen = searchNestedIf(nextThen); + nextElse = searchNestedIf(nextElse); + if (nextThen.getThenBlock() == nextElse.getThenBlock() + && nextThen.getElseBlock() == nextElse.getElseBlock()) { + return mergeTernaryConditions(currentIf, nextThen, nextElse); + } + if (nextThen.getThenBlock() == nextElse.getElseBlock() + && nextThen.getElseBlock() == nextElse.getThenBlock()) { + nextElse = IfInfo.invert(nextElse); + return mergeTernaryConditions(currentIf, nextThen, nextElse); + } + + return null; + } + + private static IfInfo mergeTernaryConditions(IfInfo currentIf, IfInfo nextThen, IfInfo nextElse) { + IfCondition newCondition = IfCondition.ternary(currentIf.getCondition(), + nextThen.getCondition(), nextElse.getCondition()); + IfInfo result = new IfInfo(newCondition, nextThen.getThenBlock(), nextThen.getElseBlock()); + result.setIfBlock(currentIf.getIfBlock()); + result.getMergedBlocks().addAll(currentIf.getMergedBlocks()); + result.getMergedBlocks().addAll(nextThen.getMergedBlocks()); + result.getMergedBlocks().addAll(nextElse.getMergedBlocks()); + for (BlockNode blockNode : result.getMergedBlocks()) { + blockNode.add(AFlag.SKIP); } return result; } diff --git a/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions14.java b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions14.java index d4f3dc8a..f63c1acb 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions14.java +++ b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions14.java @@ -20,15 +20,6 @@ public class TestConditions14 extends InternalJadxTest { System.out.println("1"); return true; } - -// public static boolean test2(Object a, Object b) { -// if (a == null ? b != null : !a.equals(b)) { -// return false; -// } -// System.out.println("2"); -// return true; -// } - } @Test diff --git a/jadx-core/src/test/java/jadx/tests/internal/conditions/TestTernaryInIf.java b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestTernaryInIf.java new file mode 100644 index 00000000..1448df85 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestTernaryInIf.java @@ -0,0 +1,36 @@ +package jadx.tests.internal.conditions; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static jadx.tests.utils.JadxMatchers.containsOne; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +public class TestTernaryInIf extends InternalJadxTest { + + public static class TestCls { + public boolean test1(boolean a, boolean b, boolean c) { + return a ? b : c; + } + + public int test2(boolean a, boolean b, boolean c) { + return (a ? b : c) ? 1 : 2; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsOne("return a ? b : c;")); + assertThat(code, containsOne("return (a ? b : c) ? 1 : 2;")); + assertThat(code, not(containsString("if"))); + assertThat(code, not(containsString("else"))); + } +} -- GitLab