Process complex 'if' conditions, refactoring

This commit is contained in:
Skylot
2013-06-30 22:48:16 +04:00
parent 5a40d960b2
commit 3598a1279c
15 changed files with 344 additions and 109 deletions
+44 -42
View File
@@ -5,7 +5,6 @@ import jadx.dex.attributes.AttributeType;
import jadx.dex.attributes.DeclareVariableAttr;
import jadx.dex.attributes.ForceReturnAttr;
import jadx.dex.attributes.IAttribute;
import jadx.dex.instructions.IfNode;
import jadx.dex.instructions.IfOp;
import jadx.dex.instructions.SwitchNode;
import jadx.dex.instructions.args.ArgType;
@@ -17,6 +16,7 @@ import jadx.dex.nodes.IContainer;
import jadx.dex.nodes.IRegion;
import jadx.dex.nodes.InsnNode;
import jadx.dex.nodes.MethodNode;
import jadx.dex.regions.IfCondition;
import jadx.dex.regions.IfRegion;
import jadx.dex.regions.LoopRegion;
import jadx.dex.regions.Region;
@@ -104,8 +104,7 @@ public class RegionGen extends InsnGen {
}
private void makeIf(IfRegion region, CodeWriter code) throws CodegenException {
IfNode insn = region.getIfInsn();
code.add("if ").add(makeCondition(insn)).add(" {");
code.add("if (").add(makeCondition(region.getCondition())).add(") {");
makeRegionIndent(code, region.getThenRegion());
code.startLine('}');
@@ -116,9 +115,9 @@ public class RegionGen extends InsnGen {
// connect if-else-if block
if (els instanceof Region) {
Region re = (Region) els;
if (re.getSubBlocks().size() == 1
&& re.getSubBlocks().get(0) instanceof IfRegion) {
makeIf((IfRegion) re.getSubBlocks().get(0), code);
List<IContainer> subBlocks = re.getSubBlocks();
if (subBlocks.size() == 1 && subBlocks.get(0) instanceof IfRegion) {
makeIf((IfRegion) subBlocks.get(0), code);
return;
}
}
@@ -130,7 +129,8 @@ public class RegionGen extends InsnGen {
}
private CodeWriter makeLoop(LoopRegion region, CodeWriter code) throws CodegenException {
if (region.getConditionBlock() == null) {
IfCondition condition = region.getCondition();
if (condition == null) {
// infinite loop
code.startLine("while (true) {");
makeRegionIndent(code, region.getBody());
@@ -138,15 +138,14 @@ public class RegionGen extends InsnGen {
return code;
}
IfNode insn = region.getIfInsn();
if (!region.isConditionAtEnd()) {
code.startLine("while ").add(makeCondition(insn)).add(" {");
makeRegionIndent(code, region.getBody());
code.startLine('}');
} else {
if (region.isConditionAtEnd()) {
code.startLine("do {");
makeRegionIndent(code, region.getBody());
code.startLine("} while ").add(makeCondition(insn)).add(';');
code.startLine("} while (").add(makeCondition(condition)).add(");");
} else {
code.startLine("while (").add(makeCondition(condition)).add(") {");
makeRegionIndent(code, region.getBody());
code.startLine('}');
}
return code;
}
@@ -157,44 +156,47 @@ public class RegionGen extends InsnGen {
code.startLine('}');
}
private String makeCondition(IfNode insn) throws CodegenException {
String simple = simplifyCondition(insn);
if (simple != null)
return simple;
String second;
if (insn.isZeroCmp()) {
second = arg(InsnArg.lit(0, insn.getArg(0).getType()));
} else {
second = arg(insn.getArg(1));
private String makeCondition(IfCondition condition) throws CodegenException {
switch (condition.getMode()) {
case COMPARE:
return makeCompare(condition.getCompare());
case NOT:
return "!" + makeCondition(condition.getArgs().get(0));
case AND:
case OR:
String mode = condition.getMode() == IfCondition.MODE.AND ? " && " : " || ";
CodeWriter cw = new CodeWriter();
for (IfCondition arg : condition.getArgs()) {
if (cw.notEmpty()) {
cw.add(mode);
}
cw.add('(').add(makeCondition(arg)).add(')');
}
return cw.toString();
default:
return "??" + condition.toString();
}
return "(" + arg(insn.getArg(0)) + " " + insn.getOp().getSymbol() + " " + second + ")";
}
private String simplifyCondition(IfNode insn) throws CodegenException {
InsnArg firstArg = insn.getArg(0);
if (firstArg.getType().equals(ArgType.BOOLEAN)) {
IfOp op = insn.getOp();
if (insn.isZeroCmp()) {
private String makeCompare(IfCondition.Compare compare) throws CodegenException {
IfOp op = compare.getOp();
InsnArg firstArg = compare.getA();
InsnArg secondArg = compare.getB();
if (firstArg.getType().equals(ArgType.BOOLEAN)
&& secondArg.isLiteral()
&& secondArg.getType().equals(ArgType.BOOLEAN)) {
LiteralArg lit = (LiteralArg) secondArg;
if (lit.getLiteral() == 0)
op = op.invert();
} else {
InsnArg secondArg = insn.getArg(1);
if (!secondArg.isLiteral() || !secondArg.getType().equals(ArgType.BOOLEAN))
return null;
LiteralArg lit = (LiteralArg) secondArg;
if (lit.getLiteral() == 0)
op = op.invert();
}
if (op == IfOp.EQ) {
return "(" + arg(firstArg) + ")"; // == true
return arg(firstArg); // == true
} else if (op == IfOp.NE) {
return "(!" + arg(firstArg) + ")"; // != true
return "!" + arg(firstArg); // != true
}
LOG.warn(ErrorsCounter.formatErrorMsg(mth, "Unsupported boolean condition " + op.getSymbol()));
}
return null;
return arg(firstArg) + " " + op.getSymbol() + " " + arg(secondArg);
}
private CodeWriter makeSwitch(SwitchRegion sw, CodeWriter code) throws CodegenException {
@@ -14,6 +14,7 @@ public enum AttributeFlag {
DONT_SHRINK,
DONT_GENERATE,
SKIP,
INCONSISTENT_CODE, // warning about incorrect decompilation
}
@@ -1,5 +1,7 @@
package jadx.dex.instructions;
import jadx.utils.exceptions.JadxRuntimeException;
public enum IfOp {
EQ("=="),
NE("!="),
@@ -36,7 +38,7 @@ public enum IfOp {
return IfOp.LT;
default:
return null;
throw new JadxRuntimeException("Unknown if operations type: " + this);
}
}
}
@@ -1,7 +1,6 @@
package jadx.dex.nodes.parser;
import jadx.dex.attributes.SourceFileAttr;
import jadx.dex.info.LocalVarInfo;
import jadx.dex.instructions.args.InsnArg;
import jadx.dex.instructions.args.RegisterArg;
import jadx.dex.nodes.DexNode;
@@ -34,7 +33,7 @@ public class DebugInfoParser {
private final Section section;
private final DexNode dex;
private final LocalVarInfo[] locals;
private final LocalVar[] locals;
private final InsnArg[] activeRegisters;
private final InsnNode[] insnByOffset;
@@ -43,7 +42,7 @@ public class DebugInfoParser {
this.dex = mth.dex();
this.section = dex.openSection(debugOffset);
this.locals = new LocalVarInfo[mth.getRegsCount()];
this.locals = new LocalVar[mth.getRegsCount()];
this.activeRegisters = new InsnArg[mth.getRegsCount()];
this.insnByOffset = insnByOffset;
}
@@ -67,7 +66,7 @@ public class DebugInfoParser {
for (RegisterArg arg : mthArgs) {
int rn = arg.getRegNum();
locals[rn] = new LocalVarInfo(arg);
locals[rn] = new LocalVar(arg);
activeRegisters[rn] = arg;
}
@@ -90,7 +89,7 @@ public class DebugInfoParser {
int regNum = section.readUleb128();
int nameId = section.readUleb128() - 1;
int type = section.readUleb128() - 1;
LocalVarInfo var = new LocalVarInfo(dex, regNum, nameId, type, DexNode.NO_INDEX);
LocalVar var = new LocalVar(dex, regNum, nameId, type, DexNode.NO_INDEX);
startVar(var, addr, line);
break;
}
@@ -99,13 +98,13 @@ public class DebugInfoParser {
int nameId = section.readUleb128() - 1;
int type = section.readUleb128() - 1;
int sign = section.readUleb128() - 1;
LocalVarInfo var = new LocalVarInfo(dex, regNum, nameId, type, sign);
LocalVar var = new LocalVar(dex, regNum, nameId, type, sign);
startVar(var, addr, line);
break;
}
case DBG_RESTART_LOCAL: {
int regNum = section.readUleb128();
LocalVarInfo var = locals[regNum];
LocalVar var = locals[regNum];
if (var != null) {
var.end(addr, line);
setVar(var);
@@ -115,7 +114,7 @@ public class DebugInfoParser {
}
case DBG_END_LOCAL: {
int regNum = section.readUleb128();
LocalVarInfo var = locals[regNum];
LocalVar var = locals[regNum];
if (var != null) {
var.end(addr, line);
setVar(var);
@@ -153,7 +152,7 @@ public class DebugInfoParser {
c = section.readByte() & 0xFF;
}
for (LocalVarInfo var : locals) {
for (LocalVar var : locals) {
if (var != null && !var.isEnd()) {
var.end(addr, line);
setVar(var);
@@ -180,9 +179,9 @@ public class DebugInfoParser {
return newAddr;
}
private void startVar(LocalVarInfo var, int addr, int line) {
private void startVar(LocalVar var, int addr, int line) {
int regNum = var.getRegNum();
LocalVarInfo prev = locals[regNum];
LocalVar prev = locals[regNum];
if (prev != null && !prev.isEnd()) {
prev.end(addr, line);
setVar(prev);
@@ -191,7 +190,7 @@ public class DebugInfoParser {
locals[regNum] = var;
}
private void setVar(LocalVarInfo var) {
private void setVar(LocalVar var) {
int start = var.getStartAddr();
int end = var.getEndAddr();
@@ -203,7 +202,7 @@ public class DebugInfoParser {
merge(activeRegisters[var.getRegNum()], var);
}
private static void fillLocals(InsnNode insn, LocalVarInfo var) {
private static void fillLocals(InsnNode insn, LocalVar var) {
if (insn.getResult() != null)
merge(insn.getResult(), var);
@@ -211,7 +210,7 @@ public class DebugInfoParser {
merge(arg, var);
}
private static void merge(InsnArg arg, LocalVarInfo var) {
private static void merge(InsnArg arg, LocalVar var) {
if (arg != null && arg.isRegister()) {
if (var.getRegNum() == arg.getRegNum())
arg.setTypedVar(var.getTypedVar());
@@ -1,4 +1,4 @@
package jadx.dex.info;
package jadx.dex.nodes.parser;
import jadx.dex.instructions.args.ArgType;
import jadx.dex.instructions.args.RegisterArg;
@@ -6,14 +6,14 @@ import jadx.dex.instructions.args.TypedVar;
import jadx.dex.nodes.DexNode;
import jadx.utils.InsnUtils;
public class LocalVarInfo extends RegisterArg {
final class LocalVar extends RegisterArg {
private boolean isEnd;
private int startAddr;
private int endAddr;
public LocalVarInfo(DexNode dex, int rn, int nameId, int typeId, int signId) {
public LocalVar(DexNode dex, int rn, int nameId, int typeId, int signId) {
super(rn);
String name = (nameId == DexNode.NO_INDEX ? null : dex.getString(nameId));
ArgType type = (typeId == DexNode.NO_INDEX ? null : dex.getType(typeId));
@@ -22,7 +22,7 @@ public class LocalVarInfo extends RegisterArg {
init(name, type, sign);
}
public LocalVarInfo(RegisterArg arg) {
public LocalVar(RegisterArg arg) {
super(arg.getRegNum());
init(arg.getTypedVar().getName(), arg.getType(), null);
}
@@ -0,0 +1,114 @@
package jadx.dex.regions;
import jadx.dex.instructions.IfNode;
import jadx.dex.instructions.IfOp;
import jadx.dex.instructions.args.InsnArg;
import jadx.dex.nodes.BlockNode;
import java.util.Arrays;
import java.util.List;
public final class IfCondition {
public static IfCondition fromIfBlock(BlockNode header) {
if (header == null)
return null;
IfNode ifNode = (IfNode) header.getInstructions().get(0);
return new IfCondition(new Compare(ifNode));
}
public static IfCondition not(IfCondition a) {
return new IfCondition(MODE.NOT, Arrays.asList(a));
}
public static IfCondition and(IfCondition a, IfCondition b) {
return new IfCondition(MODE.AND, Arrays.asList(a, b));
}
public static IfCondition or(IfCondition a, IfCondition b) {
return new IfCondition(MODE.OR, Arrays.asList(a, b));
}
public static final class Compare {
private final IfNode insn;
public Compare(IfNode ifNode) {
this.insn = ifNode;
}
public IfOp getOp() {
return insn.getOp();
}
public InsnArg getA() {
return insn.getArg(0);
}
public InsnArg getB() {
if (insn.isZeroCmp())
return InsnArg.lit(0, getA().getType());
else
return insn.getArg(1);
}
@Override
public String toString() {
return getA() + " " + getOp().getSymbol() + " " + getB();
}
}
public static enum MODE {
COMPARE,
NOT,
AND,
OR
}
private final MODE mode;
private final List<IfCondition> args;
private final Compare compare;
private IfCondition(Compare compare) {
this.mode = MODE.COMPARE;
this.compare = compare;
this.args = null;
}
private IfCondition(MODE mode, List<IfCondition> args) {
this.mode = mode;
this.args = args;
this.compare = null;
}
public MODE getMode() {
return mode;
}
public List<IfCondition> getArgs() {
return args;
}
public boolean isCompare() {
return mode == MODE.COMPARE;
}
public Compare getCompare() {
return compare;
}
@Override
public String toString() {
switch (mode) {
case COMPARE:
return compare.toString();
case NOT:
return "!" + args;
case AND:
return "&& " + args;
case OR:
return "||" + args;
}
return "??";
}
}
+11 -9
View File
@@ -1,6 +1,5 @@
package jadx.dex.regions;
import jadx.dex.instructions.IfNode;
import jadx.dex.nodes.BlockNode;
import jadx.dex.nodes.IContainer;
import jadx.dex.nodes.IRegion;
@@ -11,22 +10,25 @@ import java.util.List;
public final class IfRegion extends AbstractRegion {
protected BlockNode header;
protected IContainer thenRegion;
protected IContainer elseRegion;
private final BlockNode header;
private IfCondition condition;
private IContainer thenRegion;
private IContainer elseRegion;
public IfRegion(IRegion parent, BlockNode header) {
super(parent);
assert header.getInstructions().size() == 1;
this.header = header;
this.condition = IfCondition.fromIfBlock(header);
}
public IfNode getIfInsn() {
return (IfNode) header.getInstructions().get(0);
public IfCondition getCondition() {
return condition;
}
public BlockNode getHeader() {
return header;
public void setCondition(IfCondition condition) {
this.condition = condition;
}
public IContainer getThenRegion() {
@@ -58,6 +60,6 @@ public final class IfRegion extends AbstractRegion {
@Override
public String toString() {
return "IF(" + header + ") then " + thenRegion + " else " + elseRegion;
return "IF(" + condition + ") then " + thenRegion + " else " + elseRegion;
}
}
@@ -14,6 +14,7 @@ import java.util.List;
public final class LoopRegion extends AbstractRegion {
// loop header contains one 'if' insn, equals null for infinite loop
private final IfCondition condition;
private final BlockNode conditionBlock;
// instruction which must be executed before condition in every loop
private BlockNode preCondition = null;
@@ -23,21 +24,18 @@ public final class LoopRegion extends AbstractRegion {
public LoopRegion(IRegion parent, BlockNode header, boolean reversed) {
super(parent);
this.conditionBlock = header;
this.condition = IfCondition.fromIfBlock(header);
this.conditionAtEnd = reversed;
}
public IfNode getIfInsn() {
return (IfNode) conditionBlock.getInstructions().get(conditionBlock.getInstructions().size() - 1);
public IfCondition getCondition() {
return condition;
}
public BlockNode getHeader() {
return conditionBlock;
}
public BlockNode getConditionBlock() {
return conditionBlock;
}
public IContainer getBody() {
return body;
}
@@ -57,6 +55,10 @@ public final class LoopRegion extends AbstractRegion {
this.preCondition = preCondition;
}
private IfNode getIfInsn() {
return (IfNode) conditionBlock.getInstructions().get(conditionBlock.getInstructions().size() - 1);
}
/**
* Check if pre-conditions can be inlined into loop condition
*/
@@ -41,10 +41,11 @@ public class CheckRegions extends AbstractVisitor {
if (mth.getBasicBlocks().size() != blocksInRegions.size()) {
for (BlockNode block : mth.getBasicBlocks()) {
if (!blocksInRegions.contains(block)) {
if (!block.getInstructions().isEmpty()) {
if (!block.getInstructions().isEmpty()
&& !block.getAttributes().contains(AttributeFlag.SKIP)) {
mth.getAttributes().add(AttributeFlag.INCONSISTENT_CODE);
if (Consts.DEBUG)
LOG.debug("Missing block: {} in {}", block, mth);
LOG.debug(" Missing block: {} in {}", block, mth);
else
break;
}
@@ -58,15 +59,14 @@ public class CheckRegions extends AbstractVisitor {
public void enterRegion(MethodNode mth, IRegion region) {
if (region instanceof LoopRegion) {
LoopRegion loop = (LoopRegion) region;
if (loop.getConditionBlock() != null
&& loop.getConditionBlock().getInstructions().size() != 1) {
ErrorsCounter.methodError(mth, "Incorrect condition in loop: " + loop.getConditionBlock());
BlockNode loopHeader = loop.getHeader();
if (loopHeader != null && loopHeader.getInstructions().size() != 1) {
ErrorsCounter.methodError(mth, "Incorrect condition in loop: " + loopHeader);
mth.getAttributes().add(AttributeFlag.INCONSISTENT_CODE);
}
}
}
};
DepthRegionTraverser.traverseAll(mth, checkLoops);
}
}
@@ -14,6 +14,7 @@ import jadx.dex.nodes.BlockNode;
import jadx.dex.nodes.IRegion;
import jadx.dex.nodes.InsnNode;
import jadx.dex.nodes.MethodNode;
import jadx.dex.regions.IfCondition;
import jadx.dex.regions.IfRegion;
import jadx.dex.regions.LoopRegion;
import jadx.dex.regions.Region;
@@ -41,12 +42,20 @@ public class RegionMaker {
private static final Logger LOG = LoggerFactory.getLogger(RegionMaker.class);
private final MethodNode mth;
private final BitSet processedBlocks;
public RegionMaker(MethodNode mth) {
this.mth = mth;
this.processedBlocks = new BitSet(mth.getBasicBlocks().size());
}
public Region makeRegion(BlockNode startBlock, RegionStack stack) {
int id = startBlock.getId();
if (processedBlocks.get(id))
LOG.debug(" Block already processed: " + startBlock + ", mth: " + mth);
else
processedBlocks.set(id);
Region r = new Region(stack.peekRegion());
BlockNode next = startBlock;
while (next != null) {
@@ -351,6 +360,11 @@ public class RegionMaker {
ifnode.invertOp(bThen.getStartOffset());
if (block.getAttributes().contains(AttributeFlag.SKIP)) {
// block already included in other if region
return bThen;
}
BlockNode out = null;
BlockNode thenBlock;
BlockNode elseBlock = null;
@@ -358,13 +372,9 @@ public class RegionMaker {
thenBlock = bThen;
// select else and exit blocks
if (block.getDominatesOn().size() == 2) {
if (bElse.getPredecessors().size() == 1)
elseBlock = bElse;
else
out = bElse;
elseBlock = bElse;
} else {
if (bElse.getPredecessors().size() != 1) {
elseBlock = null;
out = bElse;
} else {
elseBlock = bElse;
@@ -381,14 +391,47 @@ public class RegionMaker {
out = null;
}
IfRegion ifRegion = new IfRegion(currentRegion, block);
currentRegion.getSubBlocks().add(ifRegion);
if (elseBlock != null) {
if (elseBlock.getPredecessors().size() > 1) {
// if else block shares between several 'if' instructions => merge conditions
for (BlockNode pred : elseBlock.getPredecessors()) {
if (!pred.equals(block)) {
List<InsnNode> insns = pred.getInstructions();
if (insns.size() == 1 && insns.get(0).getType() == InsnType.IF) {
IfNode otherNode = (IfNode) insns.get(0);
int elseOffset = elseBlock.getStartOffset();
if (elseOffset != otherNode.getTarget()) {
otherNode.invertOp(elseOffset);
}
IfCondition newArg = IfCondition.fromIfBlock(pred);
IfCondition condition;
if (otherNode.getTarget() != pred.getStartOffset()) {
condition = IfCondition.and(ifRegion.getCondition(), newArg);
} else {
condition = IfCondition.or(ifRegion.getCondition(), newArg);
}
ifRegion.setCondition(condition);
pred.getAttributes().add(AttributeFlag.SKIP);
}
}
}
for (BlockNode d : block.getDominatesOn()) {
if (d != bThen && d != bElse) {
out = d;
break;
}
}
}
}
if (elseBlock != null) {
if (stack.containsExit(elseBlock))
elseBlock = null;
}
IfRegion ifRegion = new IfRegion(currentRegion, block);
currentRegion.getSubBlocks().add(ifRegion);
stack.push(ifRegion);
stack.addExit(out);
@@ -4,28 +4,29 @@ import jadx.dex.nodes.BlockNode;
import jadx.dex.nodes.IRegion;
import jadx.dex.nodes.MethodNode;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RegionStack {
final class RegionStack {
private static final Logger LOG = LoggerFactory.getLogger(RegionStack.class);
private static final boolean DEBUG = false;
static {
if (DEBUG)
LOG.debug("Debug enabled for " + RegionStack.class);
LOG.debug("Debug enabled for {}", RegionStack.class);
}
private static class State {
private static final class State {
final Set<BlockNode> exits;
IRegion region;
public State() {
exits = new HashSet<BlockNode>();
exits = new HashSet<BlockNode>(4);
}
private State(State c) {
@@ -38,17 +39,17 @@ public class RegionStack {
@Override
public String toString() {
return "Exits: " + exits;
return "Region: " + region + ", exits: " + exits;
}
}
private final Stack<State> stack;
private final Deque<State> stack;
private State curState;
public RegionStack(MethodNode mth) {
if (DEBUG)
LOG.debug("New RegionStack: {}", mth);
this.stack = new Stack<RegionStack.State>();
this.stack = new ArrayDeque<State>();
this.curState = new State();
}
@@ -59,16 +60,14 @@ public class RegionStack {
curState = curState.copy();
curState.region = region;
if (DEBUG) {
LOG.debug("Stack push: {} = {}", region, curState);
LOG.debug("Stack size: {}", size());
}
if (DEBUG)
LOG.debug("Stack push: {}: {}", size(), curState);
}
public void pop() {
curState = stack.pop();
if (DEBUG)
LOG.debug("Stack pop : {}", curState);
LOG.debug("Stack pop: {}: {}", size(), curState);
}
/**
@@ -10,6 +10,12 @@ public abstract class AbstractTest {
}
}
public static void assertFalse(boolean condition) {
if (condition) {
throw new AssertionError();
}
}
public static void assertTrue(boolean condition, String msg) {
if (!condition) {
throw new AssertionError(msg);
+2 -3
View File
@@ -22,7 +22,7 @@ public class RunTests {
System.exit(1);
}
int timeout = 3 * clsList.size();
int timeout = 2 * clsList.size();
System.err.println("Set timeout to " + timeout + " seconds");
new Timer().schedule(new TerminateTask(), timeout * 1000);
@@ -50,9 +50,8 @@ public class RunTests {
Class<?> cls = Class.forName(clsName);
if (cls.getSuperclass() == AbstractTest.class) {
Method mth = cls.getMethod("testRun");
AbstractTest test = (AbstractTest) cls.getConstructor().newInstance();
try {
AbstractTest test = (AbstractTest) cls.getConstructor().newInstance();
pass = (Boolean) mth.invoke(test);
} catch (InvocationTargetException e) {
pass = false;
@@ -0,0 +1,57 @@
package jadx.samples;
/**
* Failed tests for current jadx version
*/
public class TestConditions extends AbstractTest {
public int test1(int num) {
boolean inRange = (num >= 59 && num <= 66);
if (inRange) {
num++;
}
return num;
}
public int test1a(int num) {
boolean notInRange = (num < 59 || num > 66);
if (notInRange) {
num--;
}
return num;
}
public int test1b(int num) {
boolean inc = (num >= 59 && num <= 66 && num != 62);
if (inc) {
num++;
}
return num;
}
public boolean test1c(int num) {
return num == 4 || num == 6;
}
@Override
public boolean testRun() throws Exception {
TestConditions c = new TestConditions();
assertEquals(c.test1(50), 50);
assertEquals(c.test1(60), 61);
assertEquals(c.test1a(50), 49);
assertEquals(c.test1a(60), 60);
assertEquals(c.test1b(60), 61);
assertEquals(c.test1b(62), 62);
assertTrue(c.test1c(4));
assertFalse(c.test1c(5));
return true;
}
public static void main(String[] args) throws Exception {
new TestConditions().testRun();
}
}
@@ -2,14 +2,23 @@ package jadx.samples;
public class TestStringProcessing extends AbstractTest {
@Override
public boolean testRun() {
public void testStringEscape() {
String str = "test\tstr\n";
assertTrue(str.length() == 9);
str = "test\bunicode\u1234";
assertTrue(str.charAt(4) == '\b');
return true;
}
public void testStringConcat() {
String s = "1";
assertEquals("a" + s, "a1");
}
@Override
public boolean testRun() throws Exception {
testStringEscape();
testStringConcat();
return true;
}
}