Commit 577176dd authored by Skylot's avatar Skylot

core: implement 'finally' block extraction

parent a135eb44
......@@ -2,7 +2,6 @@ package jadx.core;
import jadx.api.IJadxArgs;
import jadx.core.codegen.CodeGen;
import jadx.core.dex.visitors.BlockMakerVisitor;
import jadx.core.dex.visitors.ClassModifier;
import jadx.core.dex.visitors.CodeShrinker;
import jadx.core.dex.visitors.ConstInlinerVisitor;
......@@ -16,6 +15,11 @@ import jadx.core.dex.visitors.ModVisitor;
import jadx.core.dex.visitors.PrepareForCodeGen;
import jadx.core.dex.visitors.ReSugarCode;
import jadx.core.dex.visitors.SimplifyVisitor;
import jadx.core.dex.visitors.blocksmaker.BlockExceptionHandler;
import jadx.core.dex.visitors.blocksmaker.BlockFinallyExtract;
import jadx.core.dex.visitors.blocksmaker.BlockFinish;
import jadx.core.dex.visitors.blocksmaker.BlockProcessor;
import jadx.core.dex.visitors.blocksmaker.BlockSplitter;
import jadx.core.dex.visitors.regions.CheckRegions;
import jadx.core.dex.visitors.regions.IfRegionVisitor;
import jadx.core.dex.visitors.regions.LoopRegionVisitor;
......@@ -55,10 +59,16 @@ public class Jadx {
if (args.isFallbackMode()) {
passes.add(new FallbackModeVisitor());
} else {
passes.add(new BlockMakerVisitor());
passes.add(new BlockSplitter());
passes.add(new BlockProcessor());
passes.add(new BlockExceptionHandler());
passes.add(new BlockFinallyExtract());
passes.add(new BlockFinish());
passes.add(new SSATransform());
passes.add(new DebugInfoVisitor());
passes.add(new TypeInference());
if (args.isRawCFGOutput()) {
passes.add(DotGraphVisitor.dumpRaw(outDir));
}
......@@ -72,6 +82,7 @@ public class Jadx {
passes.add(new CodeShrinker());
passes.add(new ReSugarCode());
if (args.isCFGOutput()) {
passes.add(DotGraphVisitor.dump(outDir));
}
......
......@@ -446,6 +446,8 @@ public class InsnGen {
break;
case PHI:
assert isFallback();
code.add("PHI(").add(String.valueOf(insn.getArgsCount())).add(")");
break;
/* fallback mode instructions */
......
......@@ -28,13 +28,13 @@ import jadx.core.dex.regions.loops.ForLoop;
import jadx.core.dex.regions.loops.LoopRegion;
import jadx.core.dex.regions.loops.LoopType;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.RegionUtils;
import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -282,27 +282,28 @@ public class RegionGen extends InsnGen {
}
private void makeTryCatch(TryCatchRegion region, CodeWriter code) throws CodegenException {
TryCatchBlock tryCatchBlock = region.geTryCatchBlock();
code.startLine("try {");
makeRegionIndent(code, region.getTryRegion());
// TODO: move search of 'allHandler' to 'TryCatchRegion'
ExceptionHandler allHandler = null;
for (ExceptionHandler handler : tryCatchBlock.getHandlers()) {
if (!handler.isCatchAll()) {
makeCatchBlock(code, handler);
} else {
for (Map.Entry<ExceptionHandler, IContainer> entry : region.getCatchRegions().entrySet()) {
ExceptionHandler handler = entry.getKey();
if (handler.isCatchAll()) {
if (allHandler != null) {
LOG.warn("Several 'all' handlers in try/catch block in {}", mth);
}
allHandler = handler;
} else {
makeCatchBlock(code, handler);
}
}
if (allHandler != null) {
makeCatchBlock(code, allHandler);
}
if (tryCatchBlock.getFinalRegion() != null) {
IContainer finallyRegion = region.getFinallyRegion();
if (finallyRegion != null) {
code.startLine("} finally {");
makeRegionIndent(code, tryCatchBlock.getFinalRegion());
makeRegionIndent(code, finallyRegion);
}
code.startLine('}');
}
......
......@@ -10,6 +10,7 @@ public enum AFlag {
SYNTHETIC,
RETURN, // block contains only return instruction
ORIG_RETURN,
DECLARE_VAR,
DONT_WRAP,
......@@ -18,6 +19,7 @@ public enum AFlag {
DONT_INLINE,
DONT_GENERATE,
SKIP,
REMOVE,
SKIP_FIRST_ARG,
ANONYMOUS_CONSTRUCTOR,
......
......@@ -7,6 +7,7 @@ import jadx.core.dex.attributes.nodes.EnumClassAttr;
import jadx.core.dex.attributes.nodes.EnumMapAttr;
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
import jadx.core.dex.attributes.nodes.IgnoreEdgeAttr;
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
import jadx.core.dex.attributes.nodes.JumpInfo;
import jadx.core.dex.attributes.nodes.LoopInfo;
......@@ -51,4 +52,5 @@ public class AType<T extends IAttribute> {
public static final AType<SourceFileAttr> SOURCE_FILE = new AType<SourceFileAttr>();
public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<DeclareVariablesAttr>();
public static final AType<LoopLabelAttr> LOOP_LABEL = new AType<LoopLabelAttr>();
public static final AType<IgnoreEdgeAttr> IGNORE_EDGE = new AType<IgnoreEdgeAttr>();
}
package jadx.core.dex.attributes.nodes;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.IAttribute;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.utils.Utils;
import java.util.HashSet;
import java.util.Set;
public class IgnoreEdgeAttr implements IAttribute {
private final Set<BlockNode> blocks = new HashSet<BlockNode>();
public Set<BlockNode> getBlocks() {
return blocks;
}
public boolean contains(BlockNode block) {
return blocks.contains(block);
}
@Override
public AType<IgnoreEdgeAttr> getType() {
return AType.IGNORE_EDGE;
}
@Override
public String toString() {
return "IGNORE_EDGES: " + Utils.listToString(blocks);
}
}
......@@ -3,6 +3,7 @@ package jadx.core.dex.nodes;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrNode;
import jadx.core.dex.attributes.nodes.IgnoreEdgeAttr;
import jadx.core.dex.attributes.nodes.LoopInfo;
import jadx.core.utils.InsnUtils;
......@@ -99,6 +100,10 @@ public class BlockNode extends AttrNode implements IBlock {
toRemove.add(loop.getStart());
}
}
IgnoreEdgeAttr ignoreEdgeAttr = block.get(AType.IGNORE_EDGE);
if (ignoreEdgeAttr != null) {
toRemove.addAll(ignoreEdgeAttr.getBlocks());
}
if (toRemove.isEmpty()) {
return sucList;
}
......
......@@ -237,31 +237,32 @@ public class DebugInfoParser {
}
private static void merge(InsnArg arg, LocalVar var) {
if (arg != null && arg.isRegister()) {
RegisterArg reg = (RegisterArg) arg;
if (var.getRegNum() == reg.getRegNum()) {
SSAVar ssaVar = reg.getSVar();
boolean mergeRequired = false;
if (ssaVar != null) {
int ssaEnd = ssaVar.getEndAddr();
int ssaStart = ssaVar.getStartAddr();
int localStart = var.getStartAddr();
int localEnd = var.getEndAddr();
boolean isIntersected = !((localEnd < ssaStart) || (ssaEnd < localStart));
if (isIntersected && (ssaEnd <= localEnd)) {
mergeRequired = true;
}
} else {
mergeRequired = true;
}
if (mergeRequired) {
reg.mergeDebugInfo(var.getType(), var.getName());
}
if (arg == null || !arg.isRegister()) {
return;
}
RegisterArg reg = (RegisterArg) arg;
if (var.getRegNum() != reg.getRegNum()) {
return;
}
boolean mergeRequired = false;
SSAVar ssaVar = reg.getSVar();
if (ssaVar != null) {
int ssaEnd = ssaVar.getEndAddr();
int ssaStart = ssaVar.getStartAddr();
int localStart = var.getStartAddr();
int localEnd = var.getEndAddr();
boolean isIntersected = !((localEnd < ssaStart) || (ssaEnd < localStart));
if (isIntersected && (ssaEnd <= localEnd)) {
mergeRequired = true;
}
} else {
mergeRequired = true;
}
if (mergeRequired) {
reg.mergeDebugInfo(var.getType(), var.getName());
}
}
}
......@@ -8,12 +8,15 @@ import jadx.core.utils.Utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public final class TryCatchRegion extends AbstractRegion {
private final IContainer tryRegion;
private List<IContainer> catchRegions = Collections.emptyList();
private Map<ExceptionHandler, IContainer> catchRegions = Collections.emptyMap();
private IContainer finallyRegion;
private TryCatchBlock tryCatchBlock;
public TryCatchRegion(IRegion parent, IContainer tryRegion) {
......@@ -21,31 +24,50 @@ public final class TryCatchRegion extends AbstractRegion {
this.tryRegion = tryRegion;
}
public void setTryCatchBlock(TryCatchBlock tryCatchBlock) {
this.tryCatchBlock = tryCatchBlock;
int count = tryCatchBlock.getHandlersCount();
this.catchRegions = new LinkedHashMap<ExceptionHandler, IContainer>(count);
for (ExceptionHandler handler : tryCatchBlock.getHandlers()) {
IContainer handlerRegion = handler.getHandlerRegion();
if (handlerRegion != null) {
if (handler.isFinally()) {
finallyRegion = handlerRegion;
} else {
catchRegions.put(handler, handlerRegion);
}
}
}
}
public IContainer getTryRegion() {
return tryRegion;
}
public List<IContainer> getCatchRegions() {
public Map<ExceptionHandler, IContainer> getCatchRegions() {
return catchRegions;
}
public TryCatchBlock geTryCatchBlock() {
public TryCatchBlock getTryCatchBlock() {
return tryCatchBlock;
}
public void setTryCatchBlock(TryCatchBlock tryCatchBlock) {
this.tryCatchBlock = tryCatchBlock;
this.catchRegions = new ArrayList<IContainer>(tryCatchBlock.getHandlersCount());
for (ExceptionHandler handler : tryCatchBlock.getHandlers()) {
catchRegions.add(handler.getHandlerRegion());
}
public IContainer getFinallyRegion() {
return finallyRegion;
}
public void setFinallyRegion(IContainer finallyRegion) {
this.finallyRegion = finallyRegion;
}
@Override
public List<IContainer> getSubBlocks() {
List<IContainer> all = new ArrayList<IContainer>(1 + catchRegions.size());
List<IContainer> all = new ArrayList<IContainer>(2 + catchRegions.size());
all.add(tryRegion);
all.addAll(catchRegions);
all.addAll(catchRegions.values());
if (finallyRegion != null) {
all.add(finallyRegion);
}
return Collections.unmodifiableList(all);
}
......@@ -57,6 +79,7 @@ public final class TryCatchRegion extends AbstractRegion {
@Override
public String toString() {
return "Try: " + tryRegion
+ " catches: " + Utils.listToString(catchRegions);
+ " catches: " + Utils.listToString(catchRegions.values())
+ (finallyRegion == null ? "" : " finally: " + finallyRegion);
}
}
......@@ -116,6 +116,6 @@ public final class IfRegion extends AbstractRegion {
@Override
public String toString() {
return "IF(" + condition + ") then " + thenRegion + " else " + elseRegion;
return "IF " + header + " then " + thenRegion + " else " + elseRegion;
}
}
......@@ -28,8 +28,8 @@ public class ExcHandlerAttr implements IAttribute {
@Override
public String toString() {
return "ExcHandler: "
+ (handler.isCatchAll() ? "all" : handler.getCatchType())
+ " " + handler.getArg();
return "ExcHandler: " + (handler.isFinally()
? " FINALLY"
: (handler.isCatchAll() ? "all" : handler.getCatchType()) + " " + handler.getArg());
}
}
......@@ -21,6 +21,7 @@ public class ExceptionHandler {
private InsnArg arg;
private TryCatchBlock tryBlock;
private boolean isFinally;
public ExceptionHandler(int addr, ClassInfo type) {
this.handleOffset = addr;
......@@ -79,6 +80,14 @@ public class ExceptionHandler {
return tryBlock;
}
public boolean isFinally() {
return isFinally;
}
public void setFinally(boolean isFinally) {
this.isFinally = isFinally;
}
@Override
public int hashCode() {
return (catchType == null ? 0 : 31 * catchType.hashCode()) + handleOffset;
......
package jadx.core.dex.trycatch;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IBlock;
import jadx.core.dex.nodes.IContainer;
import jadx.core.dex.nodes.InsnContainer;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.InstructionRemover;
import jadx.core.utils.Utils;
import java.util.ArrayList;
......@@ -19,7 +16,6 @@ import java.util.List;
public class TryCatchBlock {
private final List<ExceptionHandler> handlers;
private IContainer finalRegion;
// references for fast remove/modify
private final List<InsnNode> insns;
......@@ -55,6 +51,7 @@ public class TryCatchBlock {
for (Iterator<ExceptionHandler> it = handlers.iterator(); it.hasNext(); ) {
ExceptionHandler h = it.next();
if (h == handler) {
unbindHandler(h);
it.remove();
break;
}
......@@ -64,29 +61,20 @@ public class TryCatchBlock {
}
}
private void unbindHandler(ExceptionHandler handler) {
for (BlockNode block : handler.getBlocks()) {
block.add(AFlag.SKIP);
}
}
private void removeWholeBlock(MethodNode mth) {
if (finalRegion != null) {
// search catch attr
for (BlockNode block : mth.getBasicBlocks()) {
CatchAttr cb = block.get(AType.CATCH_BLOCK);
if (cb == attr) {
for (ExceptionHandler eh : mth.getExceptionHandlers()) {
if (eh.getBlocks().contains(block)) {
TryCatchBlock tb = eh.getTryBlock();
tb.setFinalRegionFromInsns(mth, ((IBlock) finalRegion).getInstructions());
}
}
}
}
} else {
// self destruction
for (InsnNode insn : insns) {
insn.removeAttr(attr);
}
insns.clear();
for (BlockNode block : mth.getBasicBlocks()) {
block.removeAttr(attr);
}
// self destruction
for (InsnNode insn : insns) {
insn.removeAttr(attr);
}
insns.clear();
for (BlockNode block : mth.getBasicBlocks()) {
block.removeAttr(attr);
}
}
......@@ -108,36 +96,11 @@ public class TryCatchBlock {
return attr;
}
public IContainer getFinalRegion() {
return finalRegion;
}
public void setFinalRegion(IContainer finalRegion) {
this.finalRegion = finalRegion;
}
public void setFinalRegionFromInsns(MethodNode mth, List<InsnNode> insns) {
List<InsnNode> finalBlockInsns = new ArrayList<InsnNode>(insns);
setFinalRegion(new InsnContainer(finalBlockInsns));
InstructionRemover.unbindInsnList(mth, finalBlockInsns);
// remove these instructions from other handlers
for (ExceptionHandler h : getHandlers()) {
for (BlockNode ehb : h.getBlocks()) {
ehb.getInstructions().removeAll(finalBlockInsns);
}
}
// remove from blocks with this catch
for (BlockNode b : mth.getBasicBlocks()) {
CatchAttr ca = b.get(AType.CATCH_BLOCK);
if (attr == ca) {
b.getInstructions().removeAll(finalBlockInsns);
}
public boolean merge(MethodNode mth, TryCatchBlock tryBlock) {
if (tryBlock == this) {
return false;
}
}
public void merge(MethodNode mth, TryCatchBlock tryBlock) {
for (InsnNode insn : tryBlock.getInsns()) {
this.addInsn(insn);
}
......@@ -148,6 +111,7 @@ public class TryCatchBlock {
// clear
tryBlock.handlers.clear();
tryBlock.removeWholeBlock(mth);
return true;
}
@Override
......
......@@ -208,7 +208,7 @@ public class CodeShrinker extends AbstractVisitor {
// }
SSAVar sVar = arg.getSVar();
// allow inline only one use arg or 'this'
if (sVar.getVariableUseCount() != 1 && !arg.isThis()) {
if (sVar == null || (sVar.getVariableUseCount() != 1 && !arg.isThis())) {
continue;
}
InsnNode assignInsn = sVar.getAssign().getParentInsn();
......
......@@ -6,6 +6,7 @@ import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.parser.DebugInfoParser;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.exceptions.JadxException;
public class DebugInfoVisitor extends AbstractVisitor {
......@@ -23,18 +24,21 @@ public class DebugInfoVisitor extends AbstractVisitor {
mth.setSourceLine(line - 1);
}
}
if (!mth.getReturnType().equals(ArgType.VOID)
&& mth.getExitBlocks().size() > 1) {
if (!mth.getReturnType().equals(ArgType.VOID)) {
// fix debug for splitter 'return' instructions
for (BlockNode exit : mth.getExitBlocks()) {
InsnNode ret = exit.getInstructions().get(0);
InsnNode ret = BlockUtils.getLastInsn(exit);
if (ret == null) {
continue;
}
InsnNode oldRet = insnArr[ret.getOffset()];
if (oldRet != ret) {
RegisterArg oldArg = (RegisterArg) oldRet.getArg(0);
RegisterArg newArg = (RegisterArg) ret.getArg(0);
newArg.mergeDebugInfo(oldArg.getType(), oldArg.getName());
ret.setSourceLine(oldRet.getSourceLine());
if (oldRet == ret) {
continue;
}
RegisterArg oldArg = (RegisterArg) oldRet.getArg(0);
RegisterArg newArg = (RegisterArg) ret.getArg(0);
newArg.mergeDebugInfo(oldArg.getType(), oldArg.getName());
ret.setSourceLine(oldRet.getSourceLine());
}
}
}
......
......@@ -28,7 +28,6 @@ import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.utils.InstructionRemover;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -51,10 +50,6 @@ public class ModVisitor extends AbstractVisitor {
removeStep(mth, remover);
checkArgsNames(mth);
for (BlockNode block : mth.getBasicBlocks()) {
processExceptionHandler(mth, block);
}
}
private static void replaceStep(MethodNode mth, InstructionRemover remover) {
......@@ -110,6 +105,10 @@ public class ModVisitor extends AbstractVisitor {
}
break;
case MOVE_EXCEPTION:
processMoveException(mth, block, insn, remover);
break;
default:
break;
}
......@@ -284,70 +283,33 @@ public class ModVisitor extends AbstractVisitor {
}
}
private static void processExceptionHandler(MethodNode mth, BlockNode block) {
ExcHandlerAttr handlerAttr = block.get(AType.EXC_HANDLER);
if (handlerAttr == null) {
private static void processMoveException(MethodNode mth, BlockNode block, InsnNode insn,
InstructionRemover remover) {
ExcHandlerAttr excHandlerAttr = block.get(AType.EXC_HANDLER);
if (excHandlerAttr == null) {
return;
}
ExceptionHandler excHandler = handlerAttr.getHandler();
boolean noExitNode = true; // check if handler has exit edge to block not from this handler
boolean reThrow = false;
for (BlockNode excBlock : excHandler.getBlocks()) {
if (noExitNode) {
noExitNode = excHandler.getBlocks().containsAll(excBlock.getCleanSuccessors());
}
ExceptionHandler excHandler = excHandlerAttr.getHandler();
List<InsnNode> insns = excBlock.getInstructions();
int size = insns.size();
if (excHandler.isCatchAll()
&& size > 0
&& insns.get(size - 1).getType() == InsnType.THROW) {
reThrow = true;
InstructionRemover.remove(mth, excBlock, size - 1);
// move not removed instructions to 'finally' block
if (!insns.isEmpty()) {
// TODO: support instructions from several blocks
// tryBlock.setFinalBlockFromInsns(mth, insns);
// TODO: because of incomplete realization don't extract final block,
// just remove unnecessary instructions
insns.clear();
}
}
}
List<InsnNode> blockInsns = block.getInstructions();
if (!blockInsns.isEmpty()) {
InsnNode insn = blockInsns.get(0);
if (insn.getType() == InsnType.MOVE_EXCEPTION) {
// result arg used both in this insn and exception handler,
RegisterArg resArg = insn.getResult();
ArgType type = excHandler.isCatchAll() ? ArgType.THROWABLE : excHandler.getCatchType().getType();
String name = excHandler.isCatchAll() ? "th" : "e";
if (resArg.getName() == null) {
resArg.setName(name);
}
SSAVar sVar = insn.getResult().getSVar();
if (sVar.getUseCount() == 0) {
excHandler.setArg(new NamedArg(name, type));
InstructionRemover.remove(mth, block, 0);
} else if (sVar.isUsedInPhi()) {
// exception var moved to external variable => replace with 'move' insn
InsnNode moveInsn = new InsnNode(InsnType.MOVE, 1);
moveInsn.setResult(insn.getResult());
NamedArg namedArg = new NamedArg(name, type);
moveInsn.addArg(namedArg);
excHandler.setArg(namedArg);
replaceInsn(block, 0, moveInsn);
}
}
}
int totalSize = 0;
for (BlockNode excBlock : excHandler.getBlocks()) {
totalSize += excBlock.getInstructions().size();
// result arg used both in this insn and exception handler,
RegisterArg resArg = insn.getResult();
ArgType type = excHandler.isCatchAll() ? ArgType.THROWABLE : excHandler.getCatchType().getType();
String name = excHandler.isCatchAll() ? "th" : "e";
if (resArg.getName() == null) {
resArg.setName(name);
}
if (totalSize == 0 && noExitNode && reThrow) {
handlerAttr.getTryBlock().removeHandler(mth, excHandler);
SSAVar sVar = insn.getResult().getSVar();
if (sVar.getUseCount() == 0) {
excHandler.setArg(new NamedArg(name, type));
remover.add(insn);
} else if (sVar.isUsedInPhi()) {
// exception var moved to external variable => replace with 'move' insn
InsnNode moveInsn = new InsnNode(InsnType.MOVE, 1);
moveInsn.setResult(insn.getResult());
NamedArg namedArg = new NamedArg(name, type);
moveInsn.addArg(namedArg);
excHandler.setArg(namedArg);
replaceInsn(block, 0, moveInsn);
}
}
......
package jadx.core.dex.visitors;
package jadx.core.dex.visitors.blocksmaker;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
......@@ -14,14 +13,16 @@ import jadx.core.dex.trycatch.CatchAttr;
import jadx.core.dex.trycatch.ExcHandlerAttr;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.InstructionRemover;
import java.util.List;
import java.util.Iterator;
public class BlockProcessingHelper {
public class BlockExceptionHandler extends AbstractVisitor {
public static void visit(MethodNode mth) {
@Override
public void visit(MethodNode mth) {
if (mth.isNoCode()) {
return;
}
......@@ -30,7 +31,6 @@ public class BlockProcessingHelper {
}
for (BlockNode block : mth.getBasicBlocks()) {
block.updateCleanSuccessors();
initBlocksInIfNodes(block);
}
for (BlockNode block : mth.getBasicBlocks()) {
processExceptionHandlers(mth, block);
......@@ -38,6 +38,16 @@ public class BlockProcessingHelper {
for (BlockNode block : mth.getBasicBlocks()) {
processTryCatchBlocks(mth, block);
}
for (BlockNode block : mth.getBasicBlocks()) {
Iterator<InsnNode> it = block.getInstructions().iterator();
while (it.hasNext()) {
InsnNode insn = it.next();
if (insn.getType() == InsnType.NOP) {
it.remove();
}
}
}
}
/**
......@@ -88,24 +98,35 @@ public class BlockProcessingHelper {
// if 'throw' in exception handler block have 'catch' - merge these catch blocks
for (InsnNode insn : excBlock.getInstructions()) {
if (insn.getType() == InsnType.THROW) {
CatchAttr catchAttr = insn.get(AType.CATCH_BLOCK);
if (catchAttr != null) {
TryCatchBlock handlerBlock = handlerAttr.getTryBlock();
TryCatchBlock catchBlock = catchAttr.getTryBlock();
if (handlerBlock != catchBlock) { // TODO: why it can be?
handlerBlock.merge(mth, catchBlock);
catchBlock.removeInsn(insn);
}
}
CatchAttr catchAttr = insn.get(AType.CATCH_BLOCK);
if (catchAttr == null) {
continue;
}
if (insn.getType() == InsnType.THROW
|| onlyAllHandler(catchAttr.getTryBlock())) {
TryCatchBlock handlerBlock = handlerAttr.getTryBlock();
TryCatchBlock catchBlock = catchAttr.getTryBlock();
handlerBlock.merge(mth, catchBlock);
}
}
}
}
}
private static boolean onlyAllHandler(TryCatchBlock tryBlock) {
if (tryBlock.getHandlersCount() == 1) {
ExceptionHandler eh = tryBlock.getHandlers().iterator().next();
if (eh.isCatchAll() || eh.isFinally()) {
return true;
}
}
return false;
}
/**
* If all instructions in block have same 'catch' attribute mark it as 'TryCatch' block.
*/
private static void processTryCatchBlocks(MethodNode mth, BlockNode block) {
// if all instructions in block have same 'catch' attribute mark it as 'TryCatch' block
CatchAttr commonCatchAttr = null;
for (InsnNode insn : block.getInstructions()) {
CatchAttr catchAttr = insn.get(AType.CATCH_BLOCK);
......@@ -138,17 +159,4 @@ public class BlockProcessingHelper {
}
}
}
/**
* Init 'then' and 'else' blocks for 'if' instruction.
*/
private static void initBlocksInIfNodes(BlockNode block) {
List<InsnNode> instructions = block.getInstructions();
if (instructions.size() == 1) {
InsnNode insn = instructions.get(0);
if (insn.getType() == InsnType.IF) {
((IfNode) insn).initBlocks(block);
}
}
}
}
package jadx.core.dex.visitors.blocksmaker;
import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.visitors.AbstractVisitor;
import java.util.List;
public class BlockFinish extends AbstractVisitor {
@Override
public void visit(MethodNode mth) {
if (mth.isNoCode()) {
return;
}
for (BlockNode block : mth.getBasicBlocks()) {
block.updateCleanSuccessors();
initBlocksInIfNodes(block);
}
mth.finishBasicBlocks();
}
/**
* Init 'then' and 'else' blocks for 'if' instruction.
*/
private static void initBlocksInIfNodes(BlockNode block) {
List<InsnNode> instructions = block.getInstructions();
if (instructions.size() == 1) {
InsnNode insn = instructions.get(0);
if (insn.getType() == InsnType.IF) {
((IfNode) insn).initBlocks(block);
}
}
}
}
package jadx.core.dex.visitors.blocksmaker;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.JumpInfo;
import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.trycatch.CatchAttr;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.SplitterBlockAttr;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.utils.exceptions.JadxRuntimeException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class BlockSplitter extends AbstractVisitor {
// leave these instructions alone in block node
private static final Set<InsnType> SEPARATE_INSNS = EnumSet.of(
InsnType.RETURN,
InsnType.IF,
InsnType.SWITCH,
InsnType.MONITOR_ENTER,
InsnType.MONITOR_EXIT,
InsnType.THROW
);
@Override
public void visit(MethodNode mth) {
if (mth.isNoCode()) {
return;
}
mth.checkInstructions();
mth.initBasicBlocks();
splitBasicBlocks(mth);
removeInsns(mth);
}
private static void splitBasicBlocks(MethodNode mth) {
InsnNode prevInsn = null;
Map<Integer, BlockNode> blocksMap = new HashMap<Integer, BlockNode>();
BlockNode curBlock = startNewBlock(mth, 0);
mth.setEnterBlock(curBlock);
// split into blocks
for (InsnNode insn : mth.getInstructions()) {
if (insn == null) {
continue;
}
boolean startNew = false;
if (prevInsn != null) {
InsnType type = prevInsn.getType();
if (type == InsnType.GOTO
|| type == InsnType.THROW
|| SEPARATE_INSNS.contains(type)) {
if (type == InsnType.RETURN || type == InsnType.THROW) {
mth.addExitBlock(curBlock);
}
BlockNode block = startNewBlock(mth, insn.getOffset());
if (type == InsnType.MONITOR_ENTER || type == InsnType.MONITOR_EXIT) {
connect(curBlock, block);
}
curBlock = block;
startNew = true;
} else {
startNew = isSplitByJump(prevInsn, insn)
|| SEPARATE_INSNS.contains(insn.getType())
|| isDoWhile(blocksMap, curBlock, insn)
|| prevInsn.contains(AFlag.TRY_LEAVE)
|| prevInsn.getType() == InsnType.MOVE_EXCEPTION;
if (startNew) {
BlockNode block = startNewBlock(mth, insn.getOffset());
connect(curBlock, block);
curBlock = block;
}
}
}
// for try/catch make empty block for connect handlers
if (insn.contains(AFlag.TRY_ENTER)) {
BlockNode block;
if (insn.getOffset() != 0 && !startNew) {
block = startNewBlock(mth, insn.getOffset());
connect(curBlock, block);
curBlock = block;
}
blocksMap.put(insn.getOffset(), curBlock);
// add this insn in new block
block = startNewBlock(mth, -1);
curBlock.add(AFlag.SYNTHETIC);
SplitterBlockAttr splitter = new SplitterBlockAttr(curBlock);
block.addAttr(splitter);
curBlock.addAttr(splitter);
connect(curBlock, block);
curBlock = block;
} else {
blocksMap.put(insn.getOffset(), curBlock);
}
curBlock.getInstructions().add(insn);
prevInsn = insn;
}
// setup missing connections
setupConnections(mth, blocksMap);
}
static BlockNode startNewBlock(MethodNode mth, int offset) {
BlockNode block = new BlockNode(mth.getBasicBlocks().size(), offset);
mth.getBasicBlocks().add(block);
return block;
}
static void connect(BlockNode from, BlockNode to) {
if (!from.getSuccessors().contains(to)) {
from.getSuccessors().add(to);
}
if (!to.getPredecessors().contains(from)) {
to.getPredecessors().add(from);
}
}
static void removeConnection(BlockNode from, BlockNode to) {
from.getSuccessors().remove(to);
to.getPredecessors().remove(from);
}
static BlockNode insertBlockBetween(MethodNode mth, BlockNode source, BlockNode target) {
BlockNode newBlock = startNewBlock(mth, target.getStartOffset());
newBlock.add(AFlag.SYNTHETIC);
removeConnection(source, target);
connect(source, newBlock);
connect(newBlock, target);
return newBlock;
}
private static void setupConnections(MethodNode mth, Map<Integer, BlockNode> blocksMap) {
for (BlockNode block : mth.getBasicBlocks()) {
for (InsnNode insn : block.getInstructions()) {
List<JumpInfo> jumps = insn.getAll(AType.JUMP);
for (JumpInfo jump : jumps) {
BlockNode srcBlock = getBlock(jump.getSrc(), blocksMap);
BlockNode thisBlock = getBlock(jump.getDest(), blocksMap);
connect(srcBlock, thisBlock);
}
// connect exception handlers
CatchAttr catches = insn.get(AType.CATCH_BLOCK);
// get synthetic block for handlers
SplitterBlockAttr spl = block.get(AType.SPLITTER_BLOCK);
if (catches != null && spl != null) {
BlockNode splitterBlock = spl.getBlock();
boolean tryEnd = insn.contains(AFlag.TRY_LEAVE);
for (ExceptionHandler h : catches.getTryBlock().getHandlers()) {
BlockNode handlerBlock = getBlock(h.getHandleOffset(), blocksMap);
// skip self loop in handler
if (splitterBlock != handlerBlock) {
handlerBlock.addAttr(spl);
connect(splitterBlock, handlerBlock);
}
if (tryEnd) {
connect(block, handlerBlock);
}
}
}
}
}
}
private static boolean isSplitByJump(InsnNode prevInsn, InsnNode currentInsn) {
List<JumpInfo> pJumps = prevInsn.getAll(AType.JUMP);
for (JumpInfo jump : pJumps) {
if (jump.getSrc() == prevInsn.getOffset()) {
return true;
}
}
List<JumpInfo> cJumps = currentInsn.getAll(AType.JUMP);
for (JumpInfo jump : cJumps) {
if (jump.getDest() == currentInsn.getOffset()) {
return true;
}
}
return false;
}
private static boolean isDoWhile(Map<Integer, BlockNode> blocksMap, BlockNode curBlock, InsnNode insn) {
// split 'do-while' block (last instruction: 'if', target this block)
if (insn.getType() == InsnType.IF) {
IfNode ifs = (IfNode) (insn);
BlockNode targetBlock = blocksMap.get(ifs.getTarget());
if (targetBlock == curBlock) {
return true;
}
}
return false;
}
private static BlockNode getBlock(int offset, Map<Integer, BlockNode> blocksMap) {
BlockNode block = blocksMap.get(offset);
if (block == null) {
throw new JadxRuntimeException("Missing block: " + offset);
}
return block;
}
private static void removeInsns(MethodNode mth) {
for (BlockNode block : mth.getBasicBlocks()) {
Iterator<InsnNode> it = block.getInstructions().iterator();
while (it.hasNext()) {
InsnType insnType = it.next().getType();
if (insnType == InsnType.GOTO || insnType == InsnType.NOP) {
it.remove();
}
}
}
}
}
package jadx.core.dex.visitors.blocksmaker.helpers;
import jadx.core.dex.nodes.BlockNode;
public final class BlocksPair {
private final BlockNode first;
private final BlockNode second;
public BlocksPair(BlockNode first, BlockNode second) {
this.first = first;
this.second = second;
}
public BlockNode getFirst() {
return first;
}
public BlockNode getSecond() {
return second;
}
@Override
public int hashCode() {
return 31 * first.hashCode() + second.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof BlocksPair)) {
return false;
}
BlocksPair other = (BlocksPair) o;
return first.equals(other.first) && second.equals(other.second);
}
@Override
public String toString() {
return "(" + first + ", " + second + ")";
}
}
package jadx.core.dex.visitors.blocksmaker.helpers;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.BlockNode;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
public final class BlocksRemoveInfo {
private final Set<BlocksPair> processed = new HashSet<BlocksPair>();
private final Set<BlocksPair> outs = new HashSet<BlocksPair>();
private final Map<RegisterArg, RegisterArg> regMap = new HashMap<RegisterArg, RegisterArg>();
private final BlocksPair start;
private int startSplitIndex;
public BlocksRemoveInfo(BlocksPair start) {
this.start = start;
}
public Set<BlocksPair> getProcessed() {
return processed;
}
public Set<BlocksPair> getOuts() {
return outs;
}
public BlocksPair getStart() {
return start;
}
public int getStartSplitIndex() {
return startSplitIndex;
}
public void setStartSplitIndex(int startSplitIndex) {
this.startSplitIndex = startSplitIndex;
}
public Map<RegisterArg, RegisterArg> getRegMap() {
return regMap;
}
@Nullable
public BlockNode getByFirst(BlockNode first) {
for (BlocksPair blocksPair : processed) {
if (blocksPair.getFirst() == first) {
return blocksPair.getSecond();
}
}
return null;
}
@Nullable
public BlockNode getBySecond(BlockNode second) {
for (BlocksPair blocksPair : processed) {
if (blocksPair.getSecond() == second) {
return blocksPair.getSecond();
}
}
return null;
}
@Override
public String toString() {
return "BRI start: " + start
+ ", list: " + processed
+ ", outs: " + outs
+ ", regMap: " + regMap
+ ", split: " + startSplitIndex;
}
}
......@@ -4,6 +4,7 @@ import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IBlock;
import jadx.core.dex.nodes.IContainer;
import jadx.core.dex.nodes.IRegion;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.regions.loops.LoopRegion;
......@@ -11,9 +12,8 @@ import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.exceptions.JadxException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.LinkedHashSet;
import java.util.Set;
import org.slf4j.Logger;
......@@ -30,9 +30,11 @@ public class CheckRegions extends AbstractVisitor {
return;
}
// printRegion(mth, mth.getRegion(), "|");
// check if all blocks included in regions
final Set<BlockNode> blocksInRegions = new HashSet<BlockNode>();
DepthRegionTraversal.traverseAll(mth, new AbstractRegionVisitor() {
DepthRegionTraversal.traverse(mth, new AbstractRegionVisitor() {
@Override
public void processBlock(MethodNode mth, IBlock container) {
if (!(container instanceof BlockNode)) {
......@@ -49,7 +51,7 @@ public class CheckRegions extends AbstractVisitor {
// TODO
// mth.add(AFlag.INCONSISTENT_CODE);
LOG.debug(" Duplicated block: {} in {}", block, mth);
// printRegionsWithBlock(mth, block);
printRegionsWithBlock(mth, block);
}
}
});
......@@ -79,7 +81,7 @@ public class CheckRegions extends AbstractVisitor {
}
private static void printRegionsWithBlock(MethodNode mth, final BlockNode block) {
final List<IRegion> regions = new ArrayList<IRegion>();
final Set<IRegion> regions = new LinkedHashSet<IRegion>();
DepthRegionTraversal.traverseAll(mth, new TracedRegionVisitor() {
@Override
public void processBlockTraced(MethodNode mth, IBlock container, IRegion currentRegion) {
......@@ -90,4 +92,15 @@ public class CheckRegions extends AbstractVisitor {
});
LOG.debug(" Found block: {} in regions: {}", block, regions);
}
private void printRegion(MethodNode mth, IRegion region, String indent) {
LOG.debug(indent + region);
for (IContainer container : region.getSubBlocks()) {
if (container instanceof IRegion) {
printRegion(mth, (IRegion) container, indent + " ");
} else {
LOG.debug(indent + " " + container);
}
}
}
}
......@@ -22,6 +22,7 @@ import jadx.core.dex.regions.conditions.IfRegion;
import jadx.core.dex.regions.loops.LoopRegion;
import jadx.core.dex.trycatch.ExcHandlerAttr;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.SplitterBlockAttr;
import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.ErrorsCounter;
......@@ -316,7 +317,8 @@ public class RegionMaker {
Region body = makeRegion(loopStart, stack);
BlockNode loopEnd = loop.getEnd();
if (!RegionUtils.isRegionContainsBlock(body, loopEnd)
&& !loopEnd.contains(AType.EXC_HANDLER)) {
&& !loopEnd.contains(AType.EXC_HANDLER)
&& !inExceptionHandlerBlocks(loopEnd)) {
body.getSubBlocks().add(loopEnd);
}
loopRegion.setBody(body);
......@@ -330,6 +332,18 @@ public class RegionMaker {
return loopExit;
}
private boolean inExceptionHandlerBlocks(BlockNode loopEnd) {
if (mth.getExceptionHandlersCount() == 0) {
return false;
}
for (ExceptionHandler eh : mth.getExceptionHandlers()) {
if (eh.getBlocks().contains(loopEnd)) {
return true;
}
}
return false;
}
private boolean canInsertBreak(BlockNode exit) {
if (exit.contains(AFlag.RETURN)
|| BlockUtils.checkLastInsnType(exit, InsnType.BREAK)) {
......@@ -747,23 +761,33 @@ public class RegionMaker {
}
}
// TODO add blocks common for several handlers to some region
private void processExcHandler(ExceptionHandler handler, Set<BlockNode> exits) {
BlockNode start = handler.getHandlerBlock();
if (start == null) {
return;
}
// TODO extract finally part which exists in all handlers from same try block
// TODO add blocks common for several handlers to some region
RegionStack stack = new RegionStack(mth);
stack.addExits(exits);
BlockNode exit = BlockUtils.traverseWhileDominates(start, start);
if (exit != null && RegionUtils.isRegionContainsBlock(mth.getRegion(), exit)) {
stack.addExit(exit);
BlockNode dom;
if (handler.isFinally()) {
SplitterBlockAttr splitterAttr = start.get(AType.SPLITTER_BLOCK);
if (splitterAttr == null) {
return;
}
dom = splitterAttr.getBlock();
} else {
dom = start;
stack.addExits(exits);
}
BitSet domFrontier = dom.getDomFrontier();
List<BlockNode> handlerExits = BlockUtils.bitSetToBlocks(mth, domFrontier);
boolean inLoop = mth.getLoopForBlock(start) != null;
for (BlockNode exit : handlerExits) {
if ((!inLoop || BlockUtils.isPathExists(start, exit))
&& RegionUtils.isRegionContainsBlock(mth.getRegion(), exit)) {
stack.addExit(exit);
}
}
handler.setHandlerRegion(makeRegion(start, stack));
ExcHandlerAttr excHandlerAttr = start.get(AType.EXC_HANDLER);
......
......@@ -105,16 +105,17 @@ public class SSATransform extends AbstractVisitor {
for (InsnNode insn : block.getInstructions()) {
if (insn.getType() != InsnType.PHI) {
for (InsnArg arg : insn.getArguments()) {
if (arg.isRegister()) {
RegisterArg reg = (RegisterArg) arg;
int regNum = reg.getRegNum();
SSAVar var = vars[regNum];
if (var == null) {
var = mth.makeNewSVar(regNum, vers, null);
vars[regNum] = var;
}
var.use(reg);
if (!arg.isRegister()) {
continue;
}
RegisterArg reg = (RegisterArg) arg;
int regNum = reg.getRegNum();
SSAVar var = vars[regNum];
if (var == null) {
throw new JadxRuntimeException("Not initialized variable reg: " + regNum
+ ", insn: " + insn + ", block:" + block + ", method: " + mth);
}
var.use(reg);
}
}
RegisterArg result = insn.getResult();
......@@ -125,24 +126,24 @@ public class SSATransform extends AbstractVisitor {
}
for (BlockNode s : block.getSuccessors()) {
PhiListAttr phiList = s.get(AType.PHI_LIST);
if (phiList != null) {
int j = s.getPredecessors().indexOf(block);
if (j == -1) {
throw new JadxRuntimeException("Can't find predecessor for " + block + " " + s);
if (phiList == null) {
continue;
}
int j = s.getPredecessors().indexOf(block);
if (j == -1) {
throw new JadxRuntimeException("Can't find predecessor for " + block + " " + s);
}
for (PhiInsn phiInsn : phiList.getList()) {
if (j >= phiInsn.getArgsCount()) {
continue;
}
for (PhiInsn phiInsn : phiList.getList()) {
if (j >= phiInsn.getArgsCount()) {
continue;
}
int regNum = phiInsn.getResult().getRegNum();
SSAVar var = vars[regNum];
if (var == null) {
var = mth.makeNewSVar(regNum, vers, null);
vars[regNum] = var;
}
var.use(phiInsn.getArg(j));
var.setUsedInPhi(phiInsn);
int regNum = phiInsn.getResult().getRegNum();
SSAVar var = vars[regNum];
if (var == null) {
continue;
}
var.use(phiInsn.getArg(j));
var.setUsedInPhi(phiInsn);
}
}
for (BlockNode domOn : block.getDominatesOn()) {
......@@ -231,7 +232,10 @@ public class SSATransform extends AbstractVisitor {
for (PhiInsn phiInsn : insnToRemove) {
if (list.remove(phiInsn)) {
for (InsnArg arg : phiInsn.getArguments()) {
((RegisterArg) arg).getSVar().setUsedInPhi(null);
SSAVar sVar = ((RegisterArg) arg).getSVar();
if (sVar != null) {
sVar.setUsedInPhi(null);
}
}
InstructionRemover.remove(mth, block, phiInsn);
}
......
......@@ -23,6 +23,8 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
public class BlockUtils {
private BlockUtils() {
......@@ -112,12 +114,17 @@ public class BlockUtils {
}
public static boolean checkLastInsnType(BlockNode block, InsnType expectedType) {
InsnNode insn = getLastInsn(block);
return insn != null && insn.getType() == expectedType;
}
@Nullable
public static InsnNode getLastInsn(BlockNode block) {
List<InsnNode> insns = block.getInstructions();
if (insns.isEmpty()) {
return false;
return null;
}
InsnNode insn = insns.get(insns.size() - 1);
return insn.getType() == expectedType;
return insns.get(insns.size() - 1);
}
public static BlockNode getBlockByInsn(MethodNode mth, InsnNode insn) {
......@@ -135,7 +142,7 @@ public class BlockUtils {
return null;
}
private static BlockNode searchBlockWithPhi(MethodNode mth, PhiInsn insn) {
public static BlockNode searchBlockWithPhi(MethodNode mth, PhiInsn insn) {
for (BlockNode block : mth.getBasicBlocks()) {
PhiListAttr phiListAttr = block.get(AType.PHI_LIST);
if (phiListAttr != null) {
......@@ -225,7 +232,11 @@ public class BlockUtils {
}
public static List<BlockNode> bitSetToBlocks(MethodNode mth, BitSet bs) {
List<BlockNode> blocks = new ArrayList<BlockNode>(bs.cardinality());
int size = bs.cardinality();
if (size == 0) {
return Collections.emptyList();
}
List<BlockNode> blocks = new ArrayList<BlockNode>(size);
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
BlockNode block = mth.getBasicBlocks().get(i);
blocks.add(block);
......
......@@ -183,10 +183,6 @@ public class RegionUtils {
return true;
}
}
if (tb.getFinalRegion() != null
&& isRegionContainsRegion(tb.getFinalRegion(), region)) {
return true;
}
}
if (isRegionContainsRegion(b, region)) {
return true;
......
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 TestDoWhileBreak extends IntegrationTest {
public static class TestCls {
public int test(int k) throws InterruptedException {
int i = 3;
do {
if (k > 9) {
i = 0;
break;
}
i++;
} while (i < 5);
return i;
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("while ("));
}
}
......@@ -38,5 +38,8 @@ public class TestSynchronized extends IntegrationTest {
assertThat(code, containsString("synchronized (this.o) {"));
assertThat(code, not(containsString(indent(3) + ";")));
assertThat(code, not(containsString("try {")));
assertThat(code, not(containsString("} catch (Throwable th) {")));
assertThat(code, not(containsString("throw th;")));
}
}
package jadx.tests.integration.trycatch;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import java.io.IOException;
import org.junit.Test;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.junit.Assert.assertThat;
public class TestFinallyExtract extends IntegrationTest {
public static class TestCls {
public String test() throws IOException {
boolean success = false;
try {
String value = test();
success = true;
return value;
} finally {
if (!success) {
test();
}
}
}
}
@Test
public void test() {
setOutputCFG();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("success = true;"));
assertThat(code, containsOne("return value;"));
assertThat(code, containsOne("try {"));
assertThat(code, containsOne("} finally {"));
assertThat(code, containsOne("if (!success) {"));
}
}
......@@ -6,27 +6,47 @@ import jadx.tests.api.IntegrationTest;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class TestTryCatch3 extends IntegrationTest {
public static class TestCls {
private final static Object obj = new Object();
private boolean mDiscovering;
private int f = 0;
private boolean test(Object obj) {
this.mDiscovering = false;
boolean res;
try {
exc(obj);
res = exc(obj);
} catch (Exception e) {
e.toString();
res = false;
} finally {
mDiscovering = true;
f++;
}
return mDiscovering;
return res;
}
private void exc(Object obj) throws Exception {
private boolean exc(Object obj) throws Exception {
if ("r".equals(obj)) {
throw new AssertionError();
}
return true;
}
public void check() {
f = 0;
assertTrue(test(null));
assertEquals(1, f);
f = 0;
try {
test("r");
} catch (AssertionError e) {
// pass
}
assertEquals(1, f);
}
}
......@@ -38,5 +58,16 @@ public class TestTryCatch3 extends IntegrationTest {
assertThat(code, containsString("try {"));
assertThat(code, containsString("exc(obj);"));
assertThat(code, containsString("} catch (Exception e) {"));
assertThat(code, not(containsString("throw th;")));
}
@Test
public void test2() {
noDebugInfo();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, not(containsString("throw th;")));
}
}
package jadx.tests.integration.trycatch;
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;
import static org.junit.Assert.assertTrue;
public class TestTryCatchFinally extends IntegrationTest {
public static class TestCls {
public boolean f;
private boolean test(Object obj) {
this.f = false;
try {
exc(obj);
} catch (Exception e) {
e.getMessage();
} finally {
f = true;
}
return f;
}
private static boolean exc(Object obj) throws Exception {
if (obj == null) {
throw new Exception("test");
}
return (obj instanceof String);
}
public void check() {
assertTrue(test("a"));
assertTrue(test(null));
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("exc(obj);"));
assertThat(code, containsOne("} catch (Exception e) {"));
assertThat(code, containsOne("e.getMessage();"));
assertThat(code, containsOne("} finally {"));
assertThat(code, containsOne("f = true;"));
assertThat(code, containsOne("return this.f;"));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment