From bb4ef4f0a289d6d2e22fa3fc33f6431659db03b2 Mon Sep 17 00:00:00 2001 From: Skylot Date: Thu, 20 Feb 2014 22:45:05 +0400 Subject: [PATCH] core: simplify conditions --- .../main/java/jadx/core/codegen/InsnGen.java | 8 +- .../jadx/core/dex/instructions/GotoNode.java | 6 +- .../jadx/core/dex/instructions/IfNode.java | 46 +++----- .../core/dex/instructions/args/ArgType.java | 2 +- .../dex/instructions/args/LiteralArg.java | 9 +- .../java/jadx/core/dex/regions/Compare.java | 16 ++- .../jadx/core/dex/regions/IfCondition.java | 101 +++++++++++++++-- .../java/jadx/core/dex/regions/IfRegion.java | 17 +++ .../dex/visitors/ConstInlinerVisitor.java | 16 +-- .../core/dex/visitors/SimplifyVisitor.java | 6 +- .../dex/visitors/regions/RegionMaker.java | 4 +- .../visitors/regions/RegionMakerVisitor.java | 33 ++++-- .../dex/visitors/regions/TernaryVisitor.java | 2 +- .../typeresolver/finish/PostTypeResolver.java | 14 +-- .../tests/functional/TestIfCondition.java | 105 ++++++++++++++++++ .../jadx/tests/internal/TestConditions.java | 30 +++++ .../{TestTmp2.java => TestConditions2.java} | 2 +- .../java/jadx/samples/TestConditions.java | 8 ++ 18 files changed, 336 insertions(+), 89 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/functional/TestIfCondition.java create mode 100644 jadx-core/src/test/java/jadx/tests/internal/TestConditions.java rename jadx-core/src/test/java/jadx/tests/internal/{TestTmp2.java => TestConditions2.java} (93%) diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index c0a8fe8c2..b08b6b9b3 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -425,9 +425,11 @@ public class InsnGen { case IF: assert isFallback() : "if insn in not fallback mode"; IfNode ifInsn = (IfNode) insn; - String cond = arg(insn.getArg(0)) + " " + ifInsn.getOp().getSymbol() + " " - + (ifInsn.isZeroCmp() ? "0" : arg(insn.getArg(1))); - code.add("if (").add(cond).add(") goto ").add(MethodGen.getLabelName(ifInsn.getTarget())); + code.add("if ("); + code.add(arg(insn.getArg(0))).add(' '); + code.add(ifInsn.getOp().getSymbol()).add(' '); + code.add(arg(insn.getArg(1))); + code.add(") goto ").add(MethodGen.getLabelName(ifInsn.getTarget())); break; case GOTO: diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/GotoNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/GotoNode.java index 9a0f3f13d..be6f63169 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/GotoNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/GotoNode.java @@ -8,11 +8,11 @@ public class GotoNode extends InsnNode { protected int target; public GotoNode(int target) { - this(InsnType.GOTO, target); + this(InsnType.GOTO, target, 0); } - protected GotoNode(InsnType type, int target) { - super(type); + protected GotoNode(InsnType type, int target, int argsCount) { + super(type, argsCount); this.target = target; } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/IfNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/IfNode.java index 9927e108b..602baa2d8 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/IfNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/IfNode.java @@ -2,7 +2,6 @@ package jadx.core.dex.instructions; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; -import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.PrimitiveType; import jadx.core.dex.nodes.BlockNode; import jadx.core.utils.InsnUtils; @@ -14,37 +13,32 @@ import static jadx.core.utils.BlockUtils.selectOther; public class IfNode extends GotoNode { - protected boolean zeroCmp; + private static final ArgType ARG_TYPE = ArgType.unknown( + PrimitiveType.INT, PrimitiveType.OBJECT, PrimitiveType.ARRAY, + PrimitiveType.BOOLEAN, PrimitiveType.SHORT, PrimitiveType.CHAR); + protected IfOp op; private BlockNode thenBlock; private BlockNode elseBlock; public IfNode(DecodedInstruction insn, IfOp op) { - super(InsnType.IF, insn.getTarget()); + this(op, insn.getTarget(), + InsnArg.reg(insn, 0, ARG_TYPE), + insn.getRegisterCount() == 1 ? InsnArg.lit(0, ARG_TYPE) : InsnArg.reg(insn, 1, ARG_TYPE)); + } + + public IfNode(IfOp op, int targetOffset, InsnArg arg1, InsnArg arg2) { + super(InsnType.IF, targetOffset, 2); this.op = op; - - ArgType type = ArgType.unknown( - PrimitiveType.INT, PrimitiveType.OBJECT, PrimitiveType.ARRAY, - PrimitiveType.BOOLEAN, PrimitiveType.SHORT, PrimitiveType.CHAR); - - addReg(insn, 0, type); - if (insn.getRegisterCount() == 1) { - zeroCmp = true; - } else { - zeroCmp = false; - addReg(insn, 1, type); - } + addArg(arg1); + addArg(arg2); } public IfOp getOp() { return op; } - public boolean isZeroCmp() { - return zeroCmp; - } - public void invertCondition() { op = op.invert(); BlockNode tmp = thenBlock; @@ -53,17 +47,10 @@ public class IfNode extends GotoNode { target = thenBlock.getStartOffset(); } - public void changeCondition(InsnArg arg1, InsnArg arg2, IfOp op) { + public void changeCondition(IfOp op, InsnArg arg1, InsnArg arg2) { this.op = op; - this.zeroCmp = arg2.isLiteral() && ((LiteralArg) arg2).getLiteral() == 0; setArg(0, arg1); - if (!zeroCmp) { - if (getArgsCount() == 2) { - setArg(1, arg2); - } else { - addArg(arg2); - } - } + setArg(1, arg2); } public void initBlocks(BlockNode curBlock) { @@ -87,8 +74,7 @@ public class IfNode extends GotoNode { public String toString() { return InsnUtils.formatOffset(offset) + ": " + InsnUtils.insnTypeToString(insnType) - + getArg(0) + " " + op.getSymbol() - + " " + (zeroCmp ? "0" : getArg(1)) + + getArg(0) + " " + op.getSymbol() + " " + getArg(1) + " -> " + (thenBlock != null ? thenBlock : InsnUtils.formatOffset(target)); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java index 8020c5aca..e15cacf0f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java @@ -299,7 +299,7 @@ public abstract class ArgType { } public String getObject() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("ArgType.getObject()"); } public boolean isObject() { diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/LiteralArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/LiteralArg.java index 7ab875ff4..6d4bc84c5 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/LiteralArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/LiteralArg.java @@ -5,6 +5,9 @@ import jadx.core.utils.exceptions.JadxRuntimeException; public final class LiteralArg extends InsnArg { + public static final LiteralArg TRUE = new LiteralArg(1, ArgType.BOOLEAN); + public static final LiteralArg FALSE = new LiteralArg(0, ArgType.BOOLEAN); + private final long literal; public LiteralArg(long value, ArgType type) { @@ -62,7 +65,11 @@ public final class LiteralArg extends InsnArg { @Override public String toString() { try { - return "(" + TypeGen.literalToString(literal, getType()) + " " + typedVar + ")"; + String value = TypeGen.literalToString(literal, getType()); + if (getType().equals(ArgType.BOOLEAN) && (value.equals("true") || value.equals("false"))) { + return value; + } + return "(" + value + " " + typedVar + ")"; } catch (JadxRuntimeException ex) { // can't convert literal to string return "(" + literal + " " + typedVar + ")"; diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/Compare.java b/jadx-core/src/main/java/jadx/core/dex/regions/Compare.java index aca8b41e7..5716f4eca 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/Compare.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/Compare.java @@ -3,6 +3,7 @@ package jadx.core.dex.regions; import jadx.core.dex.instructions.IfNode; import jadx.core.dex.instructions.IfOp; import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.LiteralArg; public final class Compare { private final IfNode insn; @@ -20,11 +21,7 @@ public final class Compare { } public InsnArg getB() { - if (insn.isZeroCmp()) { - return InsnArg.lit(0, getA().getType()); - } else { - return insn.getArg(1); - } + return insn.getArg(1); } public Compare invert() { @@ -32,6 +29,15 @@ public final class Compare { return this; } + /** + * Change 'a != false' to 'a == true' + */ + public void normalize() { + if (getOp() == IfOp.NE && getB().isLiteral() && getB().equals(LiteralArg.FALSE)) { + insn.changeCondition(IfOp.EQ, getA(), LiteralArg.TRUE); + } + } + @Override public String toString() { return getA() + " " + getOp().getSymbol() + " " + getB(); 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 1b72a786b..490b7d344 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 @@ -1,13 +1,17 @@ package jadx.core.dex.regions; import jadx.core.dex.instructions.IfNode; +import jadx.core.dex.instructions.IfOp; import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.BlockNode; import jadx.core.utils.exceptions.JadxRuntimeException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -52,7 +56,7 @@ public final class IfCondition { private IfCondition(Compare compare) { this.mode = Mode.COMPARE; this.compare = compare; - this.args = null; + this.args = Collections.emptyList(); } private IfCondition(Mode mode, List args) { @@ -64,7 +68,11 @@ public final class IfCondition { private IfCondition(IfCondition c) { this.mode = c.mode; this.compare = c.compare; - this.args = new ArrayList(c.args); + if (c.mode == Mode.COMPARE) { + this.args = Collections.emptyList(); + } else { + this.args = new ArrayList(c.args); + } } public Mode getMode() { @@ -75,6 +83,14 @@ public final class IfCondition { return args; } + public IfCondition first() { + return args.get(0); + } + + public IfCondition second() { + return args.get(1); + } + public void addArg(IfCondition c) { args.add(c); } @@ -87,23 +103,80 @@ public final class IfCondition { return compare; } - public IfCondition invert() { + public static IfCondition invert(IfCondition cond) { + Mode mode = cond.getMode(); switch (mode) { case COMPARE: - return new IfCondition(compare.invert()); + return new IfCondition(cond.getCompare().invert()); case NOT: - return new IfCondition(args.get(0)); + return cond.first(); case AND: case OR: + List args = cond.getArgs(); List newArgs = new ArrayList(args.size()); for (IfCondition arg : args) { - newArgs.add(arg.invert()); + newArgs.add(invert(arg)); } return new IfCondition(mode == Mode.AND ? Mode.OR : Mode.AND, newArgs); } throw new JadxRuntimeException("Unknown mode for invert: " + mode); } + public static IfCondition not(IfCondition cond) { + if (cond.getMode() == Mode.NOT) { + return cond.first(); + } + return new IfCondition(Mode.NOT, Collections.singletonList(cond)); + } + + public static IfCondition simplify(IfCondition cond) { + if (cond.isCompare()) { + Compare c = cond.getCompare(); + if (c.getOp() == IfOp.EQ && c.getB().isLiteral() && c.getB().equals(LiteralArg.FALSE)) { + return not(new IfCondition(c.invert())); + } else { + c.normalize(); + } + return cond; + } + List args = null; + for (int i = 0; i < cond.getArgs().size(); i++) { + IfCondition arg = cond.getArgs().get(i); + IfCondition simpl = simplify(arg); + if (simpl != arg) { + if (args == null) { + args = new ArrayList(cond.getArgs()); + } + args.set(i, simpl); + } + } + if (args != null) { + // arguments was changed + cond = new IfCondition(cond.getMode(), args); + } + if (cond.getMode() == Mode.NOT && cond.first().getMode() == Mode.NOT) { + cond = cond.first().first(); + } + + // for condition with a lot of negations => make invert + if (cond.getMode() == Mode.OR || cond.getMode() == Mode.AND) { + int count = cond.getArgs().size(); + if (count > 1) { + int negCount = 0; + for (IfCondition arg : cond.getArgs()) { + if (arg.getMode() == Mode.NOT + || (arg.isCompare() && arg.getCompare().getOp() == IfOp.NE)) { + negCount++; + } + } + if (negCount > count / 2) { + return not(invert(cond)); + } + } + } + return cond; + } + public List getRegisterArgs() { List list = new LinkedList(); if (mode == Mode.COMPARE) { @@ -129,11 +202,21 @@ public final class IfCondition { case COMPARE: return compare.toString(); case NOT: - return "!" + args; + return "!" + first(); case AND: - return "&& " + args; case OR: - return "|| " + args; + String op = mode == Mode.OR ? " || " : " && "; + StringBuilder sb = new StringBuilder(); + sb.append('('); + for (Iterator it = args.iterator(); it.hasNext(); ) { + IfCondition arg = it.next(); + sb.append(arg); + if (it.hasNext()) { + sb.append(op); + } + } + sb.append(')'); + return sb.toString(); } return "??"; } diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/IfRegion.java b/jadx-core/src/main/java/jadx/core/dex/regions/IfRegion.java index 93bb760a1..10b6767ef 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/IfRegion.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/IfRegion.java @@ -61,6 +61,23 @@ public final class IfRegion extends AbstractRegion { return ternRegion; } + public boolean simplifyCondition() { + IfCondition cond = IfCondition.simplify(condition); + if (cond != condition) { + condition = cond; + return true; + } + return false; + } + + public void invert() { + condition = IfCondition.invert(condition); + // swap regions + IContainer tmp = thenRegion; + thenRegion = elseRegion; + elseRegion = tmp; + } + @Override public List getSubBlocks() { if (ternRegion != null) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java index a3d1fb3fc..dd0caac78 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java @@ -1,7 +1,6 @@ package jadx.core.dex.visitors; import jadx.core.dex.info.FieldInfo; -import jadx.core.dex.instructions.IfNode; import jadx.core.dex.instructions.IndexInsnNode; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InvokeNode; @@ -124,15 +123,12 @@ public class ConstInlinerVisitor extends AbstractVisitor { } case IF: { - IfNode ifnode = (IfNode) insn; - if (!ifnode.isZeroCmp()) { - InsnArg arg0 = insn.getArg(0); - InsnArg arg1 = insn.getArg(1); - if (arg0 == litArg) { - arg0.merge(arg1); - } else { - arg1.merge(arg0); - } + InsnArg arg0 = insn.getArg(0); + InsnArg arg1 = insn.getArg(1); + if (arg0 == litArg) { + arg0.merge(arg1); + } else { + arg1.merge(arg0); } break; } 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 7c9fd3212..5711b8a4f 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 @@ -96,9 +96,9 @@ public class SimplifyVisitor extends AbstractVisitor { if (f.isInsnWrap()) { InsnNode wi = ((InsnWrapArg) f).getWrapInsn(); if (wi.getType() == InsnType.CMP_L || wi.getType() == InsnType.CMP_G) { - if (ifb.isZeroCmp() - || ((LiteralArg) ifb.getArg(1)).getLiteral() == 0) { - ifb.changeCondition(wi.getArg(0), wi.getArg(1), ifb.getOp()); + if (ifb.getArg(1).isLiteral() + && ((LiteralArg) ifb.getArg(1)).getLiteral() == 0) { + ifb.changeCondition(ifb.getOp(), wi.getArg(0), wi.getArg(1)); } else { LOG.warn("TODO: cmp" + ifb); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java index 0841d2727..20b58be39 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java @@ -208,7 +208,7 @@ public class RegionMaker { condBlock = mergedIf.getIfnode(); if (!loop.getLoopBlocks().contains(mergedIf.getThenBlock())) { // invert loop condition if it points to exit - loopRegion.setCondition(mergedIf.getCondition().invert()); + loopRegion.setCondition(IfCondition.invert(mergedIf.getCondition())); bThen = mergedIf.getElseBlock(); } else { loopRegion.setCondition(mergedIf.getCondition()); @@ -303,7 +303,7 @@ public class RegionMaker { } } if (bThen != loopBody) { - loopRegion.setCondition(loopRegion.getCondition().invert()); + loopRegion.setCondition(IfCondition.invert(loopRegion.getCondition())); } out = selectOther(loopBody, condBlock.getSuccessors()); AttributesList outAttrs = out.getAttributes(); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java index 2b4ea49c4..c634f1598 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java @@ -59,21 +59,12 @@ public class RegionMakerVisitor extends AbstractVisitor { CleanRegions.process(mth); - // mark if-else-if chains DepthRegionTraverser.traverseAll(mth, new AbstractRegionVisitor() { @Override public void leaveRegion(MethodNode mth, IRegion region) { if (region instanceof IfRegion) { - IfRegion ifregion = (IfRegion) region; - IContainer elsRegion = ifregion.getElseRegion(); - if (elsRegion instanceof IfRegion) { - elsRegion.getAttributes().add(AttributeFlag.ELSE_IF_CHAIN); - } else if (elsRegion instanceof Region) { - List subBlocks = ((Region) elsRegion).getSubBlocks(); - if (subBlocks.size() == 1 && subBlocks.get(0) instanceof IfRegion) { - subBlocks.get(0).getAttributes().add(AttributeFlag.ELSE_IF_CHAIN); - } - } + processIfRegion((IfRegion) region); + } } }); @@ -83,4 +74,24 @@ public class RegionMakerVisitor extends AbstractVisitor { DepthRegionTraverser.traverseAll(mth, new ProcessReturnInsns()); } } + + private static void processIfRegion(IfRegion ifRegion) { + if (ifRegion.simplifyCondition()) { +// IfCondition condition = ifRegion.getCondition(); +// if (condition.getMode() == IfCondition.Mode.NOT) { +// ifRegion.invert(); +// } + } + + // mark if-else-if chains + IContainer elsRegion = ifRegion.getElseRegion(); + if (elsRegion instanceof IfRegion) { + elsRegion.getAttributes().add(AttributeFlag.ELSE_IF_CHAIN); + } else if (elsRegion instanceof Region) { + List subBlocks = ((Region) elsRegion).getSubBlocks(); + if (subBlocks.size() == 1 && subBlocks.get(0) instanceof IfRegion) { + subBlocks.get(0).getAttributes().add(AttributeFlag.ELSE_IF_CHAIN); + } + } + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryVisitor.java index e53840276..c41c72218 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryVisitor.java @@ -102,7 +102,7 @@ public class TernaryVisitor extends AbstractRegionVisitor implements IDexTreeVis IfCondition condition = ifRegion.getCondition(); if (inverted) { - condition = condition.invert(); + condition = IfCondition.invert(condition); InsnArg tmp = thenArg; thenArg = elseArg; elseArg = tmp; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/finish/PostTypeResolver.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/finish/PostTypeResolver.java index ae416efc8..324a25516 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/finish/PostTypeResolver.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/finish/PostTypeResolver.java @@ -1,7 +1,6 @@ package jadx.core.dex.visitors.typeresolver.finish; import jadx.core.dex.info.MethodInfo; -import jadx.core.dex.instructions.IfNode; import jadx.core.dex.instructions.InvokeNode; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; @@ -56,14 +55,11 @@ public class PostTypeResolver { case IF: { boolean change = false; - IfNode ifnode = (IfNode) insn; - if (!ifnode.isZeroCmp()) { - if (insn.getArg(1).merge(insn.getArg(0))) { - change = true; - } - if (insn.getArg(0).merge(insn.getArg(1))) { - change = true; - } + if (insn.getArg(1).merge(insn.getArg(0))) { + change = true; + } + if (insn.getArg(0).merge(insn.getArg(1))) { + change = true; } return change; } diff --git a/jadx-core/src/test/java/jadx/tests/functional/TestIfCondition.java b/jadx-core/src/test/java/jadx/tests/functional/TestIfCondition.java new file mode 100644 index 000000000..4a6922f73 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/functional/TestIfCondition.java @@ -0,0 +1,105 @@ +package jadx.tests.functional; + +import jadx.core.dex.instructions.IfNode; +import jadx.core.dex.instructions.IfOp; +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.LiteralArg; +import jadx.core.dex.regions.Compare; +import jadx.core.dex.regions.IfCondition; + +import org.junit.Test; + +import static jadx.core.dex.regions.IfCondition.Mode; +import static jadx.core.dex.regions.IfCondition.merge; +import static jadx.core.dex.regions.IfCondition.not; +import static jadx.core.dex.regions.IfCondition.simplify; +import static org.junit.Assert.assertEquals; + +public class TestIfCondition { + + private static IfCondition makeCondition(IfOp op, InsnArg a, InsnArg b) { + return IfCondition.fromIfNode(new IfNode(op, -1, a, b)); + } + + private static IfCondition makeSimpleCondition() { + return makeCondition(IfOp.EQ, mockArg(), LiteralArg.TRUE); + } + + private static IfCondition makeNegCondition() { + return makeCondition(IfOp.NE, mockArg(), LiteralArg.TRUE); + } + + private static InsnArg mockArg() { + return InsnArg.reg(0, ArgType.INT); + } + + @Test + public void testNormalize() { + // 'a != false' => 'a == true' + InsnArg a = mockArg(); + IfCondition c = makeCondition(IfOp.NE, a, LiteralArg.FALSE); + IfCondition simp = simplify(c); + + assertEquals(simp.getMode(), Mode.COMPARE); + Compare compare = simp.getCompare(); + assertEquals(compare.getA(), a); + assertEquals(compare.getB(), LiteralArg.TRUE); + } + + @Test + public void testMerge() { + IfCondition a = makeSimpleCondition(); + IfCondition b = makeSimpleCondition(); + IfCondition c = merge(Mode.OR, a, b); + + assertEquals(c.getMode(), Mode.OR); + assertEquals(c.first(), a); + assertEquals(c.second(), b); + } + + @Test + public void testSimplifyNot() { + // !(!a) => a + IfCondition a = not(not(makeSimpleCondition())); + assertEquals(simplify(a), a); + } + + @Test + public void testSimplifyNot2() { + // !(!a) => a + IfCondition a = not(makeNegCondition()); + assertEquals(simplify(a), a); + } + + @Test + public void testSimplify() { + // '!(!a || !b)' => 'a && b' + IfCondition a = makeSimpleCondition(); + IfCondition b = makeSimpleCondition(); + IfCondition c = not(merge(Mode.OR, not(a), not(b))); + IfCondition simp = simplify(c); + + assertEquals(simp.getMode(), Mode.AND); + assertEquals(simp.first(), a); + assertEquals(simp.second(), b); + } + + @Test + public void testSimplify2() { + // '(!a || !b) && !c' => '!((a && b) || c)' + IfCondition a = makeSimpleCondition(); + IfCondition b = makeSimpleCondition(); + IfCondition c = makeSimpleCondition(); + IfCondition cond = merge(Mode.AND, merge(Mode.OR, not(a), not(b)), not(c)); + IfCondition simp = simplify(cond); + + assertEquals(simp.getMode(), Mode.NOT); + IfCondition f = simp.first(); + assertEquals(f.getMode(), Mode.OR); + assertEquals(f.first().getMode(), Mode.AND); + assertEquals(f.first().first(), a); + assertEquals(f.first().second(), b); + assertEquals(f.second(), c); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestConditions.java b/jadx-core/src/test/java/jadx/tests/internal/TestConditions.java new file mode 100644 index 000000000..9c6c46f22 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/TestConditions.java @@ -0,0 +1,30 @@ +package jadx.tests.internal; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +public class TestConditions extends InternalJadxTest { + + public static class TestCls { + private boolean f1(boolean a, boolean b, boolean c) { + return (a && b) || c; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, not(containsString("(!a || !b) && !c"))); + assertThat(code, containsString("(a && b) || c")); +// assertThat(code, containsString("return (a && b) || c;")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestTmp2.java b/jadx-core/src/test/java/jadx/tests/internal/TestConditions2.java similarity index 93% rename from jadx-core/src/test/java/jadx/tests/internal/TestTmp2.java rename to jadx-core/src/test/java/jadx/tests/internal/TestConditions2.java index 2d4ed1189..a88b65c52 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/TestTmp2.java +++ b/jadx-core/src/test/java/jadx/tests/internal/TestConditions2.java @@ -9,7 +9,7 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertThat; -public class TestTmp2 extends InternalJadxTest { +public class TestConditions2 extends InternalJadxTest { public static class TestCls extends Exception { int c; diff --git a/jadx-samples/src/main/java/jadx/samples/TestConditions.java b/jadx-samples/src/main/java/jadx/samples/TestConditions.java index aeafc398f..cab348196 100644 --- a/jadx-samples/src/main/java/jadx/samples/TestConditions.java +++ b/jadx-samples/src/main/java/jadx/samples/TestConditions.java @@ -60,6 +60,10 @@ public class TestConditions extends AbstractTest { return num > 5 && (num < 10 || num == 7); } + private boolean test6(boolean a, boolean b, boolean c) { + return (a && b) || c; + } + public boolean accept(String name) { return name.startsWith("Test") && name.endsWith(".class") && !name.contains("$"); } @@ -87,6 +91,10 @@ public class TestConditions extends AbstractTest { assertTrue(test5(6)); assertTrue(test5(7)); assertTrue(test5(8)); + + assertTrue(test6(true, true, false)); + assertTrue(test6(false, false, true)); + assertFalse(test6(true, false, false)); return true; }