Commit 577176dd authored by Skylot's avatar Skylot

core: implement 'finally' block extraction

parent a135eb44
...@@ -2,7 +2,6 @@ package jadx.core; ...@@ -2,7 +2,6 @@ package jadx.core;
import jadx.api.IJadxArgs; import jadx.api.IJadxArgs;
import jadx.core.codegen.CodeGen; import jadx.core.codegen.CodeGen;
import jadx.core.dex.visitors.BlockMakerVisitor;
import jadx.core.dex.visitors.ClassModifier; import jadx.core.dex.visitors.ClassModifier;
import jadx.core.dex.visitors.CodeShrinker; import jadx.core.dex.visitors.CodeShrinker;
import jadx.core.dex.visitors.ConstInlinerVisitor; import jadx.core.dex.visitors.ConstInlinerVisitor;
...@@ -16,6 +15,11 @@ import jadx.core.dex.visitors.ModVisitor; ...@@ -16,6 +15,11 @@ import jadx.core.dex.visitors.ModVisitor;
import jadx.core.dex.visitors.PrepareForCodeGen; import jadx.core.dex.visitors.PrepareForCodeGen;
import jadx.core.dex.visitors.ReSugarCode; import jadx.core.dex.visitors.ReSugarCode;
import jadx.core.dex.visitors.SimplifyVisitor; 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.CheckRegions;
import jadx.core.dex.visitors.regions.IfRegionVisitor; import jadx.core.dex.visitors.regions.IfRegionVisitor;
import jadx.core.dex.visitors.regions.LoopRegionVisitor; import jadx.core.dex.visitors.regions.LoopRegionVisitor;
...@@ -55,10 +59,16 @@ public class Jadx { ...@@ -55,10 +59,16 @@ public class Jadx {
if (args.isFallbackMode()) { if (args.isFallbackMode()) {
passes.add(new FallbackModeVisitor()); passes.add(new FallbackModeVisitor());
} else { } 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 SSATransform());
passes.add(new DebugInfoVisitor()); passes.add(new DebugInfoVisitor());
passes.add(new TypeInference()); passes.add(new TypeInference());
if (args.isRawCFGOutput()) { if (args.isRawCFGOutput()) {
passes.add(DotGraphVisitor.dumpRaw(outDir)); passes.add(DotGraphVisitor.dumpRaw(outDir));
} }
...@@ -72,6 +82,7 @@ public class Jadx { ...@@ -72,6 +82,7 @@ public class Jadx {
passes.add(new CodeShrinker()); passes.add(new CodeShrinker());
passes.add(new ReSugarCode()); passes.add(new ReSugarCode());
if (args.isCFGOutput()) { if (args.isCFGOutput()) {
passes.add(DotGraphVisitor.dump(outDir)); passes.add(DotGraphVisitor.dump(outDir));
} }
......
...@@ -446,6 +446,8 @@ public class InsnGen { ...@@ -446,6 +446,8 @@ public class InsnGen {
break; break;
case PHI: case PHI:
assert isFallback();
code.add("PHI(").add(String.valueOf(insn.getArgsCount())).add(")");
break; break;
/* fallback mode instructions */ /* fallback mode instructions */
......
...@@ -28,13 +28,13 @@ import jadx.core.dex.regions.loops.ForLoop; ...@@ -28,13 +28,13 @@ import jadx.core.dex.regions.loops.ForLoop;
import jadx.core.dex.regions.loops.LoopRegion; import jadx.core.dex.regions.loops.LoopRegion;
import jadx.core.dex.regions.loops.LoopType; import jadx.core.dex.regions.loops.LoopType;
import jadx.core.dex.trycatch.ExceptionHandler; import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.utils.ErrorsCounter; import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.RegionUtils; import jadx.core.utils.RegionUtils;
import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxRuntimeException;
import java.util.List; import java.util.List;
import java.util.Map;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -282,27 +282,28 @@ public class RegionGen extends InsnGen { ...@@ -282,27 +282,28 @@ public class RegionGen extends InsnGen {
} }
private void makeTryCatch(TryCatchRegion region, CodeWriter code) throws CodegenException { private void makeTryCatch(TryCatchRegion region, CodeWriter code) throws CodegenException {
TryCatchBlock tryCatchBlock = region.geTryCatchBlock();
code.startLine("try {"); code.startLine("try {");
makeRegionIndent(code, region.getTryRegion()); makeRegionIndent(code, region.getTryRegion());
// TODO: move search of 'allHandler' to 'TryCatchRegion' // TODO: move search of 'allHandler' to 'TryCatchRegion'
ExceptionHandler allHandler = null; ExceptionHandler allHandler = null;
for (ExceptionHandler handler : tryCatchBlock.getHandlers()) { for (Map.Entry<ExceptionHandler, IContainer> entry : region.getCatchRegions().entrySet()) {
if (!handler.isCatchAll()) { ExceptionHandler handler = entry.getKey();
makeCatchBlock(code, handler); if (handler.isCatchAll()) {
} else {
if (allHandler != null) { if (allHandler != null) {
LOG.warn("Several 'all' handlers in try/catch block in {}", mth); LOG.warn("Several 'all' handlers in try/catch block in {}", mth);
} }
allHandler = handler; allHandler = handler;
} else {
makeCatchBlock(code, handler);
} }
} }
if (allHandler != null) { if (allHandler != null) {
makeCatchBlock(code, allHandler); makeCatchBlock(code, allHandler);
} }
if (tryCatchBlock.getFinalRegion() != null) { IContainer finallyRegion = region.getFinallyRegion();
if (finallyRegion != null) {
code.startLine("} finally {"); code.startLine("} finally {");
makeRegionIndent(code, tryCatchBlock.getFinalRegion()); makeRegionIndent(code, finallyRegion);
} }
code.startLine('}'); code.startLine('}');
} }
......
...@@ -10,6 +10,7 @@ public enum AFlag { ...@@ -10,6 +10,7 @@ public enum AFlag {
SYNTHETIC, SYNTHETIC,
RETURN, // block contains only return instruction RETURN, // block contains only return instruction
ORIG_RETURN,
DECLARE_VAR, DECLARE_VAR,
DONT_WRAP, DONT_WRAP,
...@@ -18,6 +19,7 @@ public enum AFlag { ...@@ -18,6 +19,7 @@ public enum AFlag {
DONT_INLINE, DONT_INLINE,
DONT_GENERATE, DONT_GENERATE,
SKIP, SKIP,
REMOVE,
SKIP_FIRST_ARG, SKIP_FIRST_ARG,
ANONYMOUS_CONSTRUCTOR, ANONYMOUS_CONSTRUCTOR,
......
...@@ -7,6 +7,7 @@ import jadx.core.dex.attributes.nodes.EnumClassAttr; ...@@ -7,6 +7,7 @@ import jadx.core.dex.attributes.nodes.EnumClassAttr;
import jadx.core.dex.attributes.nodes.EnumMapAttr; import jadx.core.dex.attributes.nodes.EnumMapAttr;
import jadx.core.dex.attributes.nodes.FieldReplaceAttr; import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
import jadx.core.dex.attributes.nodes.ForceReturnAttr; 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.JadxErrorAttr;
import jadx.core.dex.attributes.nodes.JumpInfo; import jadx.core.dex.attributes.nodes.JumpInfo;
import jadx.core.dex.attributes.nodes.LoopInfo; import jadx.core.dex.attributes.nodes.LoopInfo;
...@@ -51,4 +52,5 @@ public class AType<T extends IAttribute> { ...@@ -51,4 +52,5 @@ public class AType<T extends IAttribute> {
public static final AType<SourceFileAttr> SOURCE_FILE = new AType<SourceFileAttr>(); public static final AType<SourceFileAttr> SOURCE_FILE = new AType<SourceFileAttr>();
public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<DeclareVariablesAttr>(); public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<DeclareVariablesAttr>();
public static final AType<LoopLabelAttr> LOOP_LABEL = new AType<LoopLabelAttr>(); 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; ...@@ -3,6 +3,7 @@ package jadx.core.dex.nodes;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrNode; import jadx.core.dex.attributes.AttrNode;
import jadx.core.dex.attributes.nodes.IgnoreEdgeAttr;
import jadx.core.dex.attributes.nodes.LoopInfo; import jadx.core.dex.attributes.nodes.LoopInfo;
import jadx.core.utils.InsnUtils; import jadx.core.utils.InsnUtils;
...@@ -99,6 +100,10 @@ public class BlockNode extends AttrNode implements IBlock { ...@@ -99,6 +100,10 @@ public class BlockNode extends AttrNode implements IBlock {
toRemove.add(loop.getStart()); toRemove.add(loop.getStart());
} }
} }
IgnoreEdgeAttr ignoreEdgeAttr = block.get(AType.IGNORE_EDGE);
if (ignoreEdgeAttr != null) {
toRemove.addAll(ignoreEdgeAttr.getBlocks());
}
if (toRemove.isEmpty()) { if (toRemove.isEmpty()) {
return sucList; return sucList;
} }
......
...@@ -237,13 +237,16 @@ public class DebugInfoParser { ...@@ -237,13 +237,16 @@ public class DebugInfoParser {
} }
private static void merge(InsnArg arg, LocalVar var) { private static void merge(InsnArg arg, LocalVar var) {
if (arg != null && arg.isRegister()) { if (arg == null || !arg.isRegister()) {
return;
}
RegisterArg reg = (RegisterArg) arg; RegisterArg reg = (RegisterArg) arg;
if (var.getRegNum() == reg.getRegNum()) { if (var.getRegNum() != reg.getRegNum()) {
SSAVar ssaVar = reg.getSVar(); return;
}
boolean mergeRequired = false; boolean mergeRequired = false;
SSAVar ssaVar = reg.getSVar();
if (ssaVar != null) { if (ssaVar != null) {
int ssaEnd = ssaVar.getEndAddr(); int ssaEnd = ssaVar.getEndAddr();
int ssaStart = ssaVar.getStartAddr(); int ssaStart = ssaVar.getStartAddr();
...@@ -262,6 +265,4 @@ public class DebugInfoParser { ...@@ -262,6 +265,4 @@ public class DebugInfoParser {
reg.mergeDebugInfo(var.getType(), var.getName()); reg.mergeDebugInfo(var.getType(), var.getName());
} }
} }
}
}
} }
...@@ -8,12 +8,15 @@ import jadx.core.utils.Utils; ...@@ -8,12 +8,15 @@ import jadx.core.utils.Utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
public final class TryCatchRegion extends AbstractRegion { public final class TryCatchRegion extends AbstractRegion {
private final IContainer tryRegion; private final IContainer tryRegion;
private List<IContainer> catchRegions = Collections.emptyList(); private Map<ExceptionHandler, IContainer> catchRegions = Collections.emptyMap();
private IContainer finallyRegion;
private TryCatchBlock tryCatchBlock; private TryCatchBlock tryCatchBlock;
public TryCatchRegion(IRegion parent, IContainer tryRegion) { public TryCatchRegion(IRegion parent, IContainer tryRegion) {
...@@ -21,31 +24,50 @@ public final class TryCatchRegion extends AbstractRegion { ...@@ -21,31 +24,50 @@ public final class TryCatchRegion extends AbstractRegion {
this.tryRegion = tryRegion; 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() { public IContainer getTryRegion() {
return tryRegion; return tryRegion;
} }
public List<IContainer> getCatchRegions() { public Map<ExceptionHandler, IContainer> getCatchRegions() {
return catchRegions; return catchRegions;
} }
public TryCatchBlock geTryCatchBlock() { public TryCatchBlock getTryCatchBlock() {
return tryCatchBlock; return tryCatchBlock;
} }
public void setTryCatchBlock(TryCatchBlock tryCatchBlock) { public IContainer getFinallyRegion() {
this.tryCatchBlock = tryCatchBlock; return finallyRegion;
this.catchRegions = new ArrayList<IContainer>(tryCatchBlock.getHandlersCount());
for (ExceptionHandler handler : tryCatchBlock.getHandlers()) {
catchRegions.add(handler.getHandlerRegion());
} }
public void setFinallyRegion(IContainer finallyRegion) {
this.finallyRegion = finallyRegion;
} }
@Override @Override
public List<IContainer> getSubBlocks() { 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.add(tryRegion);
all.addAll(catchRegions); all.addAll(catchRegions.values());
if (finallyRegion != null) {
all.add(finallyRegion);
}
return Collections.unmodifiableList(all); return Collections.unmodifiableList(all);
} }
...@@ -57,6 +79,7 @@ public final class TryCatchRegion extends AbstractRegion { ...@@ -57,6 +79,7 @@ public final class TryCatchRegion extends AbstractRegion {
@Override @Override
public String toString() { public String toString() {
return "Try: " + tryRegion 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 { ...@@ -116,6 +116,6 @@ public final class IfRegion extends AbstractRegion {
@Override @Override
public String toString() { 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 { ...@@ -28,8 +28,8 @@ public class ExcHandlerAttr implements IAttribute {
@Override @Override
public String toString() { public String toString() {
return "ExcHandler: " return "ExcHandler: " + (handler.isFinally()
+ (handler.isCatchAll() ? "all" : handler.getCatchType()) ? " FINALLY"
+ " " + handler.getArg(); : (handler.isCatchAll() ? "all" : handler.getCatchType()) + " " + handler.getArg());
} }
} }
...@@ -21,6 +21,7 @@ public class ExceptionHandler { ...@@ -21,6 +21,7 @@ public class ExceptionHandler {
private InsnArg arg; private InsnArg arg;
private TryCatchBlock tryBlock; private TryCatchBlock tryBlock;
private boolean isFinally;
public ExceptionHandler(int addr, ClassInfo type) { public ExceptionHandler(int addr, ClassInfo type) {
this.handleOffset = addr; this.handleOffset = addr;
...@@ -79,6 +80,14 @@ public class ExceptionHandler { ...@@ -79,6 +80,14 @@ public class ExceptionHandler {
return tryBlock; return tryBlock;
} }
public boolean isFinally() {
return isFinally;
}
public void setFinally(boolean isFinally) {
this.isFinally = isFinally;
}
@Override @Override
public int hashCode() { public int hashCode() {
return (catchType == null ? 0 : 31 * catchType.hashCode()) + handleOffset; return (catchType == null ? 0 : 31 * catchType.hashCode()) + handleOffset;
......
package jadx.core.dex.trycatch; package jadx.core.dex.trycatch;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.nodes.BlockNode; 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.InsnNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.InstructionRemover;
import jadx.core.utils.Utils; import jadx.core.utils.Utils;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -19,7 +16,6 @@ import java.util.List; ...@@ -19,7 +16,6 @@ import java.util.List;
public class TryCatchBlock { public class TryCatchBlock {
private final List<ExceptionHandler> handlers; private final List<ExceptionHandler> handlers;
private IContainer finalRegion;
// references for fast remove/modify // references for fast remove/modify
private final List<InsnNode> insns; private final List<InsnNode> insns;
...@@ -55,6 +51,7 @@ public class TryCatchBlock { ...@@ -55,6 +51,7 @@ public class TryCatchBlock {
for (Iterator<ExceptionHandler> it = handlers.iterator(); it.hasNext(); ) { for (Iterator<ExceptionHandler> it = handlers.iterator(); it.hasNext(); ) {
ExceptionHandler h = it.next(); ExceptionHandler h = it.next();
if (h == handler) { if (h == handler) {
unbindHandler(h);
it.remove(); it.remove();
break; break;
} }
...@@ -64,21 +61,13 @@ public class TryCatchBlock { ...@@ -64,21 +61,13 @@ public class TryCatchBlock {
} }
} }
private void removeWholeBlock(MethodNode mth) { private void unbindHandler(ExceptionHandler handler) {
if (finalRegion != null) { for (BlockNode block : handler.getBlocks()) {
// search catch attr block.add(AFlag.SKIP);
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 { private void removeWholeBlock(MethodNode mth) {
// self destruction // self destruction
for (InsnNode insn : insns) { for (InsnNode insn : insns) {
insn.removeAttr(attr); insn.removeAttr(attr);
...@@ -88,7 +77,6 @@ public class TryCatchBlock { ...@@ -88,7 +77,6 @@ public class TryCatchBlock {
block.removeAttr(attr); block.removeAttr(attr);
} }
} }
}
public void addInsn(InsnNode insn) { public void addInsn(InsnNode insn) {
insns.add(insn); insns.add(insn);
...@@ -108,36 +96,11 @@ public class TryCatchBlock { ...@@ -108,36 +96,11 @@ public class TryCatchBlock {
return attr; return attr;
} }
public IContainer getFinalRegion() { public boolean merge(MethodNode mth, TryCatchBlock tryBlock) {
return finalRegion; if (tryBlock == this) {
} return false;
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 void merge(MethodNode mth, TryCatchBlock tryBlock) {
for (InsnNode insn : tryBlock.getInsns()) { for (InsnNode insn : tryBlock.getInsns()) {
this.addInsn(insn); this.addInsn(insn);
} }
...@@ -148,6 +111,7 @@ public class TryCatchBlock { ...@@ -148,6 +111,7 @@ public class TryCatchBlock {
// clear // clear
tryBlock.handlers.clear(); tryBlock.handlers.clear();
tryBlock.removeWholeBlock(mth); tryBlock.removeWholeBlock(mth);
return true;
} }
@Override @Override
......
...@@ -208,7 +208,7 @@ public class CodeShrinker extends AbstractVisitor { ...@@ -208,7 +208,7 @@ public class CodeShrinker extends AbstractVisitor {
// } // }
SSAVar sVar = arg.getSVar(); SSAVar sVar = arg.getSVar();
// allow inline only one use arg or 'this' // allow inline only one use arg or 'this'
if (sVar.getVariableUseCount() != 1 && !arg.isThis()) { if (sVar == null || (sVar.getVariableUseCount() != 1 && !arg.isThis())) {
continue; continue;
} }
InsnNode assignInsn = sVar.getAssign().getParentInsn(); InsnNode assignInsn = sVar.getAssign().getParentInsn();
......
...@@ -6,6 +6,7 @@ import jadx.core.dex.nodes.BlockNode; ...@@ -6,6 +6,7 @@ import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.parser.DebugInfoParser; import jadx.core.dex.nodes.parser.DebugInfoParser;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxException;
public class DebugInfoVisitor extends AbstractVisitor { public class DebugInfoVisitor extends AbstractVisitor {
...@@ -23,13 +24,17 @@ public class DebugInfoVisitor extends AbstractVisitor { ...@@ -23,13 +24,17 @@ public class DebugInfoVisitor extends AbstractVisitor {
mth.setSourceLine(line - 1); mth.setSourceLine(line - 1);
} }
} }
if (!mth.getReturnType().equals(ArgType.VOID) if (!mth.getReturnType().equals(ArgType.VOID)) {
&& mth.getExitBlocks().size() > 1) {
// fix debug for splitter 'return' instructions // fix debug for splitter 'return' instructions
for (BlockNode exit : mth.getExitBlocks()) { 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()]; InsnNode oldRet = insnArr[ret.getOffset()];
if (oldRet != ret) { if (oldRet == ret) {
continue;
}
RegisterArg oldArg = (RegisterArg) oldRet.getArg(0); RegisterArg oldArg = (RegisterArg) oldRet.getArg(0);
RegisterArg newArg = (RegisterArg) ret.getArg(0); RegisterArg newArg = (RegisterArg) ret.getArg(0);
newArg.mergeDebugInfo(oldArg.getType(), oldArg.getName()); newArg.mergeDebugInfo(oldArg.getType(), oldArg.getName());
...@@ -37,7 +42,6 @@ public class DebugInfoVisitor extends AbstractVisitor { ...@@ -37,7 +42,6 @@ public class DebugInfoVisitor extends AbstractVisitor {
} }
} }
} }
}
mth.unloadInsnArr(); mth.unloadInsnArr();
} }
} }
...@@ -28,7 +28,6 @@ import jadx.core.dex.trycatch.ExceptionHandler; ...@@ -28,7 +28,6 @@ import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.utils.InstructionRemover; import jadx.core.utils.InstructionRemover;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -51,10 +50,6 @@ public class ModVisitor extends AbstractVisitor { ...@@ -51,10 +50,6 @@ public class ModVisitor extends AbstractVisitor {
removeStep(mth, remover); removeStep(mth, remover);
checkArgsNames(mth); checkArgsNames(mth);
for (BlockNode block : mth.getBasicBlocks()) {
processExceptionHandler(mth, block);
}
} }
private static void replaceStep(MethodNode mth, InstructionRemover remover) { private static void replaceStep(MethodNode mth, InstructionRemover remover) {
...@@ -110,6 +105,10 @@ public class ModVisitor extends AbstractVisitor { ...@@ -110,6 +105,10 @@ public class ModVisitor extends AbstractVisitor {
} }
break; break;
case MOVE_EXCEPTION:
processMoveException(mth, block, insn, remover);
break;
default: default:
break; break;
} }
...@@ -284,42 +283,14 @@ public class ModVisitor extends AbstractVisitor { ...@@ -284,42 +283,14 @@ public class ModVisitor extends AbstractVisitor {
} }
} }
private static void processExceptionHandler(MethodNode mth, BlockNode block) { private static void processMoveException(MethodNode mth, BlockNode block, InsnNode insn,
ExcHandlerAttr handlerAttr = block.get(AType.EXC_HANDLER); InstructionRemover remover) {
if (handlerAttr == null) { ExcHandlerAttr excHandlerAttr = block.get(AType.EXC_HANDLER);
if (excHandlerAttr == null) {
return; return;
} }
ExceptionHandler excHandler = handlerAttr.getHandler(); ExceptionHandler excHandler = excHandlerAttr.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());
}
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, // result arg used both in this insn and exception handler,
RegisterArg resArg = insn.getResult(); RegisterArg resArg = insn.getResult();
ArgType type = excHandler.isCatchAll() ? ArgType.THROWABLE : excHandler.getCatchType().getType(); ArgType type = excHandler.isCatchAll() ? ArgType.THROWABLE : excHandler.getCatchType().getType();
...@@ -330,7 +301,7 @@ public class ModVisitor extends AbstractVisitor { ...@@ -330,7 +301,7 @@ public class ModVisitor extends AbstractVisitor {
SSAVar sVar = insn.getResult().getSVar(); SSAVar sVar = insn.getResult().getSVar();
if (sVar.getUseCount() == 0) { if (sVar.getUseCount() == 0) {
excHandler.setArg(new NamedArg(name, type)); excHandler.setArg(new NamedArg(name, type));
InstructionRemover.remove(mth, block, 0); remover.add(insn);
} else if (sVar.isUsedInPhi()) { } else if (sVar.isUsedInPhi()) {
// exception var moved to external variable => replace with 'move' insn // exception var moved to external variable => replace with 'move' insn
InsnNode moveInsn = new InsnNode(InsnType.MOVE, 1); InsnNode moveInsn = new InsnNode(InsnType.MOVE, 1);
...@@ -341,15 +312,6 @@ public class ModVisitor extends AbstractVisitor { ...@@ -341,15 +312,6 @@ public class ModVisitor extends AbstractVisitor {
replaceInsn(block, 0, moveInsn); replaceInsn(block, 0, moveInsn);
} }
} }
}
int totalSize = 0;
for (BlockNode excBlock : excHandler.getBlocks()) {
totalSize += excBlock.getInstructions().size();
}
if (totalSize == 0 && noExitNode && reThrow) {
handlerAttr.getTryBlock().removeHandler(mth, excHandler);
}
}
/** /**
* Replace insn by index i in block, * Replace insn by index i in block,
......
package jadx.core.dex.visitors; package jadx.core.dex.visitors.blocksmaker;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnArg;
...@@ -14,14 +13,16 @@ import jadx.core.dex.trycatch.CatchAttr; ...@@ -14,14 +13,16 @@ import jadx.core.dex.trycatch.CatchAttr;
import jadx.core.dex.trycatch.ExcHandlerAttr; import jadx.core.dex.trycatch.ExcHandlerAttr;
import jadx.core.dex.trycatch.ExceptionHandler; import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.TryCatchBlock; import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.utils.BlockUtils; import jadx.core.utils.BlockUtils;
import jadx.core.utils.InstructionRemover; 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()) { if (mth.isNoCode()) {
return; return;
} }
...@@ -30,7 +31,6 @@ public class BlockProcessingHelper { ...@@ -30,7 +31,6 @@ public class BlockProcessingHelper {
} }
for (BlockNode block : mth.getBasicBlocks()) { for (BlockNode block : mth.getBasicBlocks()) {
block.updateCleanSuccessors(); block.updateCleanSuccessors();
initBlocksInIfNodes(block);
} }
for (BlockNode block : mth.getBasicBlocks()) { for (BlockNode block : mth.getBasicBlocks()) {
processExceptionHandlers(mth, block); processExceptionHandlers(mth, block);
...@@ -38,6 +38,16 @@ public class BlockProcessingHelper { ...@@ -38,6 +38,16 @@ public class BlockProcessingHelper {
for (BlockNode block : mth.getBasicBlocks()) { for (BlockNode block : mth.getBasicBlocks()) {
processTryCatchBlocks(mth, block); 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 { ...@@ -88,24 +98,35 @@ public class BlockProcessingHelper {
// if 'throw' in exception handler block have 'catch' - merge these catch blocks // if 'throw' in exception handler block have 'catch' - merge these catch blocks
for (InsnNode insn : excBlock.getInstructions()) { for (InsnNode insn : excBlock.getInstructions()) {
if (insn.getType() == InsnType.THROW) {
CatchAttr catchAttr = insn.get(AType.CATCH_BLOCK); CatchAttr catchAttr = insn.get(AType.CATCH_BLOCK);
if (catchAttr != null) { if (catchAttr == null) {
continue;
}
if (insn.getType() == InsnType.THROW
|| onlyAllHandler(catchAttr.getTryBlock())) {
TryCatchBlock handlerBlock = handlerAttr.getTryBlock(); TryCatchBlock handlerBlock = handlerAttr.getTryBlock();
TryCatchBlock catchBlock = catchAttr.getTryBlock(); TryCatchBlock catchBlock = catchAttr.getTryBlock();
if (handlerBlock != catchBlock) { // TODO: why it can be?
handlerBlock.merge(mth, catchBlock); handlerBlock.merge(mth, catchBlock);
catchBlock.removeInsn(insn);
} }
} }
} }
} }
} }
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) { 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; CatchAttr commonCatchAttr = null;
for (InsnNode insn : block.getInstructions()) { for (InsnNode insn : block.getInstructions()) {
CatchAttr catchAttr = insn.get(AType.CATCH_BLOCK); CatchAttr catchAttr = insn.get(AType.CATCH_BLOCK);
...@@ -138,17 +159,4 @@ public class BlockProcessingHelper { ...@@ -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.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.IgnoreEdgeAttr;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
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.dex.visitors.AbstractVisitor;
import jadx.core.dex.visitors.blocksmaker.helpers.BlocksPair;
import jadx.core.dex.visitors.blocksmaker.helpers.BlocksRemoveInfo;
import jadx.core.dex.visitors.ssa.LiveVarAnalysis;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.InstructionRemover;
import jadx.core.utils.exceptions.JadxRuntimeException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static jadx.core.dex.visitors.blocksmaker.BlockSplitter.connect;
import static jadx.core.dex.visitors.blocksmaker.BlockSplitter.insertBlockBetween;
import static jadx.core.dex.visitors.blocksmaker.BlockSplitter.removeConnection;
public class BlockFinallyExtract extends AbstractVisitor {
private static final Logger LOG = LoggerFactory.getLogger(BlockFinallyExtract.class);
@Override
public void visit(MethodNode mth) {
if (mth.isNoCode() || mth.isNoExceptionHandlers()) {
return;
}
boolean reloadBlocks = false;
List<BlockNode> basicBlocks = mth.getBasicBlocks();
for (int i = 0; i < basicBlocks.size(); i++) {
BlockNode block = basicBlocks.get(i);
if (processExceptionHandler(mth, block)) {
reloadBlocks = true;
}
}
if (reloadBlocks) {
mergeReturnBlocks(mth);
BlockProcessor.rerun(mth);
}
}
private static boolean processExceptionHandler(MethodNode mth, BlockNode block) {
ExcHandlerAttr handlerAttr = block.get(AType.EXC_HANDLER);
if (handlerAttr == null) {
return false;
}
ExceptionHandler excHandler = handlerAttr.getHandler();
// check if handler has exit edge to block not from this handler
boolean noExitNode = true;
boolean reThrowRemoved = false;
for (BlockNode excBlock : excHandler.getBlocks()) {
if (noExitNode) {
noExitNode = excHandler.getBlocks().containsAll(excBlock.getCleanSuccessors());
}
List<InsnNode> insns = excBlock.getInstructions();
int size = insns.size();
if (excHandler.isCatchAll()
&& size != 0
&& insns.get(size - 1).getType() == InsnType.THROW) {
reThrowRemoved = true;
InstructionRemover.remove(mth, excBlock, size - 1);
}
}
if (reThrowRemoved && noExitNode
&& extractFinally(mth, block, excHandler)) {
return true;
}
int totalSize = countInstructions(excHandler);
if (totalSize == 0 && reThrowRemoved && noExitNode) {
handlerAttr.getTryBlock().removeHandler(mth, excHandler);
}
return false;
}
/**
* Search and remove common code from 'catch' and 'handlers'.
*/
private static boolean extractFinally(MethodNode mth, BlockNode handlerBlock, ExceptionHandler handler) {
int count = handler.getBlocks().size();
BitSet bs = new BitSet(count);
List<BlockNode> blocks = new ArrayList<BlockNode>(count);
for (BlockNode block : handler.getBlocks()) {
List<InsnNode> insns = block.getInstructions();
if (!insns.isEmpty()) {
if (insns.get(0).getType() != InsnType.MOVE_EXCEPTION) {
blocks.add(block);
}
bs.set(block.getId());
}
}
if (blocks.isEmpty()) {
// nothing to do
return false;
}
List<BlocksRemoveInfo> removes = new LinkedList<BlocksRemoveInfo>();
Set<BlockNode> splitters = new HashSet<BlockNode>();
// remove 'finally' from handlers
TryCatchBlock tryBlock = handler.getTryBlock();
if (tryBlock.getHandlersCount() > 1) {
for (ExceptionHandler otherHandler : tryBlock.getHandlers()) {
if (otherHandler == handler) {
continue;
}
for (BlockNode hb : otherHandler.getBlocks()) {
BlocksRemoveInfo removeInfo = removeInsns(mth, hb, blocks, bs);
if (removeInfo != null) {
removes.add(removeInfo);
break;
}
}
}
if (removes.size() != tryBlock.getHandlersCount() - 1) {
return false;
}
}
for (ExceptionHandler otherHandler : tryBlock.getHandlers()) {
SplitterBlockAttr splitterAttr = otherHandler.getHandlerBlock().get(AType.SPLITTER_BLOCK);
if (splitterAttr != null) {
BlockNode splBlock = splitterAttr.getBlock();
if (!splBlock.getCleanSuccessors().isEmpty()) {
splitters.add(splBlock);
}
}
}
// remove 'finally' from 'try' blocks (dominated by splitter block)
boolean removed = false;
for (BlockNode splitter : splitters) {
BlockNode start = splitter.getCleanSuccessors().get(0);
List<BlockNode> list = BlockUtils.collectBlocksDominatedBy(splitter, start);
for (BlockNode block : list) {
if (bs.get(block.getId())) {
continue;
}
BlocksRemoveInfo removeInfo = removeInsns(mth, block, blocks, bs);
if (removeInfo != null) {
removes.add(removeInfo);
removed = true;
break;
}
}
}
if (!removed) {
return false;
}
// 'finally' extract confirmed
for (BlocksRemoveInfo removeInfo : removes) {
if (!applyRemove(mth, removeInfo)) {
return false;
}
}
handler.setFinally(true);
// remove 'move-exception' instruction
if (BlockUtils.checkLastInsnType(handlerBlock, InsnType.MOVE_EXCEPTION)) {
InstructionRemover.remove(mth, handlerBlock, handlerBlock.getInstructions().size() - 1);
handlerBlock.add(AFlag.SKIP);
}
return true;
}
private static BlocksRemoveInfo removeInsns(MethodNode mth, BlockNode remBlock, List<BlockNode> blocks, BitSet bs) {
if (blocks.isEmpty()) {
return null;
}
BlockNode startBlock = blocks.get(0);
BlocksRemoveInfo removeInfo = checkFromFirstBlock(remBlock, startBlock, bs);
if (removeInfo == null) {
return null;
}
if (removeInfo.getOuts().size() != 1) {
LOG.debug("Unexpected finally block outs count: {}", removeInfo.getOuts());
return null;
}
return removeInfo;
}
private static BlocksRemoveInfo checkFromFirstBlock(BlockNode remBlock, BlockNode startBlock, BitSet bs) {
BlocksRemoveInfo removeInfo = isStartBlock(remBlock, startBlock);
if (removeInfo == null) {
return null;
}
if (!checkBlocksTree(remBlock, startBlock, removeInfo, bs)) {
return null;
}
return removeInfo;
}
/**
* 'Finally' instructions can start in the middle of the first block.
*/
private static BlocksRemoveInfo isStartBlock(BlockNode remBlock, BlockNode startBlock) {
List<InsnNode> remInsns = remBlock.getInstructions();
List<InsnNode> startInsns = startBlock.getInstructions();
if (remInsns.size() < startInsns.size()) {
return null;
}
// first - fast check
int delta = remInsns.size() - startInsns.size();
if (!checkInsns(remInsns, startInsns, delta, null)) {
return null;
}
BlocksRemoveInfo removeInfo = new BlocksRemoveInfo(new BlocksPair(remBlock, startBlock));
removeInfo.setStartSplitIndex(delta);
// second - run checks again for collect registers mapping
if (!checkInsns(remInsns, startInsns, delta, removeInfo)) {
return null;
}
return removeInfo;
}
private static boolean checkInsns(List<InsnNode> remInsns, List<InsnNode> startInsns, int delta,
@Nullable BlocksRemoveInfo removeInfo) {
for (int i = startInsns.size() - 1; i >= 0; i--) {
InsnNode startInsn = startInsns.get(i);
InsnNode remInsn = remInsns.get(delta + i);
if (!sameInsns(remInsn, startInsn, removeInfo)) {
return false;
}
}
return true;
}
private static boolean checkBlocksTree(BlockNode remBlock, BlockNode startBlock, BlocksRemoveInfo removeInfo,
BitSet bs) {
// skip check on start block
if (!removeInfo.getProcessed().isEmpty()
&& !sameBlocks(remBlock, startBlock, removeInfo)) {
return false;
}
removeInfo.getProcessed().add(new BlocksPair(remBlock, startBlock));
List<BlockNode> baseCS = startBlock.getCleanSuccessors();
List<BlockNode> remCS = remBlock.getCleanSuccessors();
if (baseCS.size() != remCS.size()) {
removeInfo.getOuts().add(new BlocksPair(remBlock, startBlock));
return true;
}
for (int i = 0; i < baseCS.size(); i++) {
BlockNode sBlock = baseCS.get(i);
BlockNode rBlock = remCS.get(i);
if (bs.get(sBlock.getId())) {
if (!checkBlocksTree(rBlock, sBlock, removeInfo, bs)) {
return false;
}
} else {
removeInfo.getOuts().add(new BlocksPair(rBlock, sBlock));
}
}
return true;
}
private static boolean sameBlocks(BlockNode remBlock, BlockNode startBlock, BlocksRemoveInfo removeInfo) {
List<InsnNode> first = remBlock.getInstructions();
List<InsnNode> second = startBlock.getInstructions();
if (first.size() != second.size()) {
return false;
}
int size = first.size();
for (int i = 0; i < size; i++) {
if (!sameInsns(first.get(i), second.get(i), removeInfo)) {
return false;
}
}
return true;
}
private static boolean sameInsns(InsnNode remInsn, InsnNode fInsn, BlocksRemoveInfo removeInfo) {
if (remInsn.getType() != fInsn.getType()
|| remInsn.getArgsCount() != fInsn.getArgsCount()) {
return false;
}
for (int i = 0; i < remInsn.getArgsCount(); i++) {
InsnArg remArg = remInsn.getArg(i);
InsnArg fArg = fInsn.getArg(i);
if (remArg.isRegister() != fArg.isRegister()) {
return false;
}
if (removeInfo != null && fArg.isRegister()) {
RegisterArg remReg = (RegisterArg) remArg;
RegisterArg fReg = (RegisterArg) fArg;
if (remReg.getRegNum() != fReg.getRegNum()) {
RegisterArg mapReg = removeInfo.getRegMap().get(remArg);
if (mapReg == null) {
removeInfo.getRegMap().put(remReg, fReg);
} else if (!mapReg.equalRegisterAndType(fReg)) {
return false;
}
}
}
}
return true;
}
private static boolean applyRemove(MethodNode mth, BlocksRemoveInfo removeInfo) {
BlockNode remBlock = removeInfo.getStart().getFirst();
BlockNode startBlock = removeInfo.getStart().getSecond();
if (remBlock.contains(AFlag.REMOVE)) {
// already processed
return true;
}
if (remBlock.getPredecessors().size() != 1) {
LOG.warn("Finally extract failed: remBlock pred: {}, {}", remBlock, remBlock.getPredecessors());
return false;
}
BlockNode remBlockPred = remBlock.getPredecessors().get(0);
int splitIndex = removeInfo.getStartSplitIndex();
if (splitIndex > 0) {
// split start block (remBlock)
BlockNode newBlock = insertBlockBetween(mth, remBlockPred, remBlock);
for (int i = 0; i < splitIndex; i++) {
InsnNode insnNode = remBlock.getInstructions().get(i);
insnNode.add(AFlag.SKIP);
newBlock.getInstructions().add(insnNode);
}
Iterator<InsnNode> it = remBlock.getInstructions().iterator();
while (it.hasNext()) {
InsnNode insnNode = it.next();
if (insnNode.contains(AFlag.SKIP)) {
it.remove();
}
}
for (InsnNode insnNode : newBlock.getInstructions()) {
insnNode.remove(AFlag.SKIP);
}
remBlockPred = newBlock;
}
BlocksPair out = removeInfo.getOuts().iterator().next();
BlockNode rOut = out.getFirst();
BlockNode sOut = out.getSecond();
// redirect out edges
List<BlockNode> filtPreds = filterPredecessors(sOut);
if (filtPreds.size() > 1) {
BlockNode pred = sOut.getPredecessors().get(0);
BlockNode newPred = BlockSplitter.insertBlockBetween(mth, pred, sOut);
for (BlockNode predBlock : new ArrayList<BlockNode>(sOut.getPredecessors())) {
if (predBlock != newPred) {
removeConnection(predBlock, sOut);
connect(predBlock, newPred);
}
}
rOut.getPredecessors().clear();
addIgnoredEdge(newPred, rOut);
connect(newPred, rOut);
} else if (filtPreds.size() == 1) {
BlockNode pred = filtPreds.get(0);
BlockNode repl = removeInfo.getBySecond(pred);
if (repl == null) {
throw new JadxRuntimeException("Block not found by " + pred
+ ", in " + removeInfo + ", method: " + mth);
}
removeConnection(pred, rOut);
addIgnoredEdge(repl, rOut);
connect(repl, rOut);
} else {
throw new JadxRuntimeException("Finally extract failed, unexpected preds: " + filtPreds
+ " for " + sOut + ", method: " + mth);
}
// redirect input edges
for (BlockNode pred : new ArrayList<BlockNode>(remBlock.getPredecessors())) {
removeConnection(pred, remBlock);
connect(pred, startBlock);
addIgnoredEdge(pred, startBlock);
connect(pred, rOut);
}
// generate 'move' instruction for mapped register pairs
if (!removeInfo.getRegMap().isEmpty()) {
// TODO: very expensive operation
LiveVarAnalysis la = new LiveVarAnalysis(mth);
la.runAnalysis();
for (Map.Entry<RegisterArg, RegisterArg> entry : removeInfo.getRegMap().entrySet()) {
RegisterArg from = entry.getKey();
if (la.isLive(remBlockPred.getId(), from.getRegNum())) {
RegisterArg to = entry.getValue();
InsnNode move = new InsnNode(InsnType.MOVE, 1);
move.setResult(to);
move.addArg(from);
remBlockPred.getInstructions().add(move);
}
}
}
// mark blocks for remove
markForRemove(remBlock);
for (BlocksPair pair : removeInfo.getProcessed()) {
markForRemove(pair.getFirst());
BlockNode second = pair.getSecond();
second.updateCleanSuccessors();
}
return true;
}
/**
* Unbind block for removing.
*/
private static void markForRemove(BlockNode block) {
for (BlockNode p : block.getPredecessors()) {
p.getSuccessors().remove(block);
}
for (BlockNode s : block.getSuccessors()) {
s.getPredecessors().remove(block);
}
block.getPredecessors().clear();
block.getSuccessors().clear();
block.add(AFlag.REMOVE);
}
private static void addIgnoredEdge(BlockNode from, BlockNode toBlock) {
IgnoreEdgeAttr edgeAttr = from.get(AType.IGNORE_EDGE);
if (edgeAttr == null) {
edgeAttr = new IgnoreEdgeAttr();
from.addAttr(edgeAttr);
}
edgeAttr.getBlocks().add(toBlock);
}
private static List<BlockNode> filterPredecessors(BlockNode block) {
List<BlockNode> predecessors = block.getPredecessors();
List<BlockNode> list = new ArrayList<BlockNode>(predecessors.size());
for (BlockNode pred : predecessors) {
IgnoreEdgeAttr edgeAttr = pred.get(AType.IGNORE_EDGE);
if (edgeAttr == null || !edgeAttr.contains(block)) {
list.add(pred);
}
}
return list;
}
private static int countInstructions(ExceptionHandler excHandler) {
int totalSize = 0;
for (BlockNode excBlock : excHandler.getBlocks()) {
List<InsnNode> list = excBlock.getInstructions();
if (!list.isEmpty() && list.get(0).getType() == InsnType.MOVE_EXCEPTION) {
// don't count 'move-exception' it will be removed later
totalSize--;
}
totalSize += list.size();
}
return totalSize;
}
/**
* Merge return block with same predecessor.
*/
private static void mergeReturnBlocks(MethodNode mth) {
List<BlockNode> exitBlocks = mth.getExitBlocks();
BlockNode pred = getFinallyOutBlock(exitBlocks);
if (pred == null) {
return;
}
List<BlockNode> merge = new LinkedList<BlockNode>();
for (BlockNode blockNode : pred.getSuccessors()) {
if (blockNode.contains(AFlag.RETURN)) {
merge.add(blockNode);
}
}
if (merge.size() < 2) {
return;
}
// select 'original' return block
BlockNode origReturnBlock = null;
for (BlockNode ret : merge) {
if (ret.contains(AFlag.ORIG_RETURN)) {
origReturnBlock = ret;
break;
}
}
if (origReturnBlock == null) {
return;
}
IgnoreEdgeAttr edgeAttr = pred.get(AType.IGNORE_EDGE);
for (BlockNode mb : merge) {
if (mb == origReturnBlock) {
continue;
}
for (BlockNode remPred : mb.getPredecessors()) {
connect(remPred, origReturnBlock);
}
markForRemove(mb);
edgeAttr.getBlocks().remove(mb);
}
}
private static BlockNode getFinallyOutBlock(List<BlockNode> exitBlocks) {
for (BlockNode exitBlock : exitBlocks) {
for (BlockNode exitPred : exitBlock.getPredecessors()) {
IgnoreEdgeAttr edgeAttr = exitPred.get(AType.IGNORE_EDGE);
if (edgeAttr != null && edgeAttr.contains(exitBlock)) {
return exitPred;
}
}
}
return null;
}
}
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; package jadx.core.dex.visitors.blocksmaker;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.JumpInfo;
import jadx.core.dex.attributes.nodes.LoopInfo; import jadx.core.dex.attributes.nodes.LoopInfo;
import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.RegisterArg;
...@@ -12,172 +10,37 @@ import jadx.core.dex.nodes.BlockNode; ...@@ -12,172 +10,37 @@ import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.Edge; import jadx.core.dex.nodes.Edge;
import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.trycatch.CatchAttr; import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.SplitterBlockAttr;
import jadx.core.utils.BlockUtils; import jadx.core.utils.BlockUtils;
import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxRuntimeException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.BitSet; import java.util.BitSet;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import static jadx.core.utils.EmptyBitSet.EMPTY; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BlockMakerVisitor extends AbstractVisitor { import static jadx.core.dex.visitors.blocksmaker.BlockSplitter.connect;
import static jadx.core.dex.visitors.blocksmaker.BlockSplitter.removeConnection;
import static jadx.core.utils.EmptyBitSet.EMPTY;
// leave these instructions alone in block node public class BlockProcessor extends AbstractVisitor {
private static final Set<InsnType> SEPARATE_INSNS = EnumSet.of( private static final Logger LOG = LoggerFactory.getLogger(BlockProcessor.class);
InsnType.RETURN,
InsnType.IF,
InsnType.SWITCH,
InsnType.MONITOR_ENTER,
InsnType.MONITOR_EXIT
);
@Override @Override
public void visit(MethodNode mth) { public void visit(MethodNode mth) {
if (mth.isNoCode()) { if (mth.isNoCode()) {
return; return;
} }
mth.checkInstructions();
mth.initBasicBlocks();
splitBasicBlocks(mth);
processBlocksTree(mth); processBlocksTree(mth);
BlockProcessingHelper.visit(mth);
mth.finishBasicBlocks();
}
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);
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);
} }
private static void setupConnections(MethodNode mth, Map<Integer, BlockNode> blocksMap) { public static void rerun(MethodNode mth) {
for (BlockNode block : mth.getBasicBlocks()) { removeBlocks(mth);
for (InsnNode insn : block.getInstructions()) { clearBlocksState(mth);
List<JumpInfo> jumps = insn.getAll(AType.JUMP); processBlocksTree(mth);
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) {
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 void processBlocksTree(MethodNode mth) { private static void processBlocksTree(MethodNode mth) {
...@@ -201,32 +64,6 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -201,32 +64,6 @@ public class BlockMakerVisitor extends AbstractVisitor {
processNestedLoops(mth); processNestedLoops(mth);
} }
private static BlockNode getBlock(int offset, Map<Integer, BlockNode> blocksMap) {
BlockNode block = blocksMap.get(offset);
assert block != null;
return block;
}
private 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);
}
}
private static void removeConnection(BlockNode from, BlockNode to) {
from.getSuccessors().remove(to);
to.getPredecessors().remove(from);
}
private static BlockNode startNewBlock(MethodNode mth, int offset) {
BlockNode block = new BlockNode(mth.getBasicBlocks().size(), offset);
mth.getBasicBlocks().add(block);
return block;
}
private static void computeDominators(MethodNode mth) { private static void computeDominators(MethodNode mth) {
List<BlockNode> basicBlocks = mth.getBasicBlocks(); List<BlockNode> basicBlocks = mth.getBasicBlocks();
int nBlocks = basicBlocks.size(); int nBlocks = basicBlocks.size();
...@@ -308,14 +145,18 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -308,14 +145,18 @@ public class BlockMakerVisitor extends AbstractVisitor {
} }
private static void computeBlockDF(MethodNode mth, BlockNode block) { private static void computeBlockDF(MethodNode mth, BlockNode block) {
if (block.getDomFrontier() != null) {
return;
}
for (BlockNode c : block.getDominatesOn()) { for (BlockNode c : block.getDominatesOn()) {
computeBlockDF(mth, c); computeBlockDF(mth, c);
} }
List<BlockNode> blocks = mth.getBasicBlocks();
BitSet domFrontier = null; BitSet domFrontier = null;
for (BlockNode s : block.getSuccessors()) { for (BlockNode s : block.getSuccessors()) {
if (s.getIDom() != block) { if (s.getIDom() != block) {
if (domFrontier == null) { if (domFrontier == null) {
domFrontier = new BitSet(); domFrontier = new BitSet(blocks.size());
} }
domFrontier.set(s.getId()); domFrontier.set(s.getId());
} }
...@@ -323,9 +164,9 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -323,9 +164,9 @@ public class BlockMakerVisitor extends AbstractVisitor {
for (BlockNode c : block.getDominatesOn()) { for (BlockNode c : block.getDominatesOn()) {
BitSet frontier = c.getDomFrontier(); BitSet frontier = c.getDomFrontier();
for (int p = frontier.nextSetBit(0); p >= 0; p = frontier.nextSetBit(p + 1)) { for (int p = frontier.nextSetBit(0); p >= 0; p = frontier.nextSetBit(p + 1)) {
if (mth.getBasicBlocks().get(p).getIDom() != block) { if (blocks.get(p).getIDom() != block) {
if (domFrontier == null) { if (domFrontier == null) {
domFrontier = new BitSet(); domFrontier = new BitSet(blocks.size());
} }
domFrontier.set(p); domFrontier.set(p);
} }
...@@ -418,7 +259,7 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -418,7 +259,7 @@ public class BlockMakerVisitor extends AbstractVisitor {
} }
if (oneHeader) { if (oneHeader) {
// several back edges connected to one loop header => make additional block // several back edges connected to one loop header => make additional block
BlockNode newLoopHeader = startNewBlock(mth, block.getStartOffset()); BlockNode newLoopHeader = BlockSplitter.startNewBlock(mth, block.getStartOffset());
newLoopHeader.add(AFlag.SYNTHETIC); newLoopHeader.add(AFlag.SYNTHETIC);
connect(newLoopHeader, block); connect(newLoopHeader, block);
for (LoopInfo la : loops) { for (LoopInfo la : loops) {
...@@ -438,7 +279,7 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -438,7 +279,7 @@ public class BlockMakerVisitor extends AbstractVisitor {
for (Edge edge : edges) { for (Edge edge : edges) {
BlockNode target = edge.getTarget(); BlockNode target = edge.getTarget();
if (!target.contains(AFlag.SYNTHETIC)) { if (!target.contains(AFlag.SYNTHETIC)) {
insertBlockBetween(mth, edge.getSource(), target); BlockSplitter.insertBlockBetween(mth, edge.getSource(), target);
change = true; change = true;
} }
} }
...@@ -453,7 +294,7 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -453,7 +294,7 @@ public class BlockMakerVisitor extends AbstractVisitor {
List<BlockNode> nodes = new ArrayList<BlockNode>(loopEnd.getPredecessors()); List<BlockNode> nodes = new ArrayList<BlockNode>(loopEnd.getPredecessors());
for (BlockNode pred : nodes) { for (BlockNode pred : nodes) {
if (!pred.contains(AFlag.SYNTHETIC)) { if (!pred.contains(AFlag.SYNTHETIC)) {
insertBlockBetween(mth, pred, loopEnd); BlockSplitter.insertBlockBetween(mth, pred, loopEnd);
change = true; change = true;
} }
} }
...@@ -466,15 +307,6 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -466,15 +307,6 @@ public class BlockMakerVisitor extends AbstractVisitor {
return splitReturn(mth); return splitReturn(mth);
} }
private 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;
}
/** /**
* Splice return block if several predecessors presents * Splice return block if several predecessors presents
*/ */
...@@ -493,11 +325,12 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -493,11 +325,12 @@ public class BlockMakerVisitor extends AbstractVisitor {
} }
boolean first = true; boolean first = true;
for (BlockNode pred : preds) { for (BlockNode pred : preds) {
BlockNode newRetBlock = startNewBlock(mth, exitBlock.getStartOffset()); BlockNode newRetBlock = BlockSplitter.startNewBlock(mth, exitBlock.getStartOffset());
newRetBlock.add(AFlag.SYNTHETIC); newRetBlock.add(AFlag.SYNTHETIC);
InsnNode newRetInsn; InsnNode newRetInsn;
if (first) { if (first) {
newRetInsn = returnInsn; newRetInsn = returnInsn;
newRetBlock.add(AFlag.ORIG_RETURN);
first = false; first = false;
} else { } else {
newRetInsn = duplicateReturnInsn(returnInsn); newRetInsn = duplicateReturnInsn(returnInsn);
...@@ -548,6 +381,21 @@ public class BlockMakerVisitor extends AbstractVisitor { ...@@ -548,6 +381,21 @@ public class BlockMakerVisitor extends AbstractVisitor {
return insn; return insn;
} }
private static void removeBlocks(MethodNode mth) {
Iterator<BlockNode> it = mth.getBasicBlocks().iterator();
while (it.hasNext()) {
BlockNode block = it.next();
if (block.contains(AFlag.REMOVE)) {
if (!block.getPredecessors().isEmpty()
|| !block.getSuccessors().isEmpty()) {
LOG.error("Block {} not deleted, method: {}", block, mth);
} else {
it.remove();
}
}
}
}
private static void clearBlocksState(MethodNode mth) { private static void clearBlocksState(MethodNode mth) {
for (BlockNode block : mth.getBasicBlocks()) { for (BlockNode block : mth.getBasicBlocks()) {
block.remove(AType.LOOP); block.remove(AType.LOOP);
......
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; ...@@ -4,6 +4,7 @@ import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.IBlock;
import jadx.core.dex.nodes.IContainer;
import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.IRegion;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.regions.loops.LoopRegion; import jadx.core.dex.regions.loops.LoopRegion;
...@@ -11,9 +12,8 @@ import jadx.core.dex.visitors.AbstractVisitor; ...@@ -11,9 +12,8 @@ import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.utils.ErrorsCounter; import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxException;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
import org.slf4j.Logger; import org.slf4j.Logger;
...@@ -30,9 +30,11 @@ public class CheckRegions extends AbstractVisitor { ...@@ -30,9 +30,11 @@ public class CheckRegions extends AbstractVisitor {
return; return;
} }
// printRegion(mth, mth.getRegion(), "|");
// check if all blocks included in regions // check if all blocks included in regions
final Set<BlockNode> blocksInRegions = new HashSet<BlockNode>(); final Set<BlockNode> blocksInRegions = new HashSet<BlockNode>();
DepthRegionTraversal.traverseAll(mth, new AbstractRegionVisitor() { DepthRegionTraversal.traverse(mth, new AbstractRegionVisitor() {
@Override @Override
public void processBlock(MethodNode mth, IBlock container) { public void processBlock(MethodNode mth, IBlock container) {
if (!(container instanceof BlockNode)) { if (!(container instanceof BlockNode)) {
...@@ -49,7 +51,7 @@ public class CheckRegions extends AbstractVisitor { ...@@ -49,7 +51,7 @@ public class CheckRegions extends AbstractVisitor {
// TODO // TODO
// mth.add(AFlag.INCONSISTENT_CODE); // mth.add(AFlag.INCONSISTENT_CODE);
LOG.debug(" Duplicated block: {} in {}", block, mth); LOG.debug(" Duplicated block: {} in {}", block, mth);
// printRegionsWithBlock(mth, block); printRegionsWithBlock(mth, block);
} }
} }
}); });
...@@ -79,7 +81,7 @@ public class CheckRegions extends AbstractVisitor { ...@@ -79,7 +81,7 @@ public class CheckRegions extends AbstractVisitor {
} }
private static void printRegionsWithBlock(MethodNode mth, final BlockNode block) { 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() { DepthRegionTraversal.traverseAll(mth, new TracedRegionVisitor() {
@Override @Override
public void processBlockTraced(MethodNode mth, IBlock container, IRegion currentRegion) { public void processBlockTraced(MethodNode mth, IBlock container, IRegion currentRegion) {
...@@ -90,4 +92,15 @@ public class CheckRegions extends AbstractVisitor { ...@@ -90,4 +92,15 @@ public class CheckRegions extends AbstractVisitor {
}); });
LOG.debug(" Found block: {} in regions: {}", block, regions); 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; ...@@ -22,6 +22,7 @@ import jadx.core.dex.regions.conditions.IfRegion;
import jadx.core.dex.regions.loops.LoopRegion; import jadx.core.dex.regions.loops.LoopRegion;
import jadx.core.dex.trycatch.ExcHandlerAttr; import jadx.core.dex.trycatch.ExcHandlerAttr;
import jadx.core.dex.trycatch.ExceptionHandler; import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.SplitterBlockAttr;
import jadx.core.dex.trycatch.TryCatchBlock; import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.utils.BlockUtils; import jadx.core.utils.BlockUtils;
import jadx.core.utils.ErrorsCounter; import jadx.core.utils.ErrorsCounter;
...@@ -316,7 +317,8 @@ public class RegionMaker { ...@@ -316,7 +317,8 @@ public class RegionMaker {
Region body = makeRegion(loopStart, stack); Region body = makeRegion(loopStart, stack);
BlockNode loopEnd = loop.getEnd(); BlockNode loopEnd = loop.getEnd();
if (!RegionUtils.isRegionContainsBlock(body, loopEnd) if (!RegionUtils.isRegionContainsBlock(body, loopEnd)
&& !loopEnd.contains(AType.EXC_HANDLER)) { && !loopEnd.contains(AType.EXC_HANDLER)
&& !inExceptionHandlerBlocks(loopEnd)) {
body.getSubBlocks().add(loopEnd); body.getSubBlocks().add(loopEnd);
} }
loopRegion.setBody(body); loopRegion.setBody(body);
...@@ -330,6 +332,18 @@ public class RegionMaker { ...@@ -330,6 +332,18 @@ public class RegionMaker {
return loopExit; 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) { private boolean canInsertBreak(BlockNode exit) {
if (exit.contains(AFlag.RETURN) if (exit.contains(AFlag.RETURN)
|| BlockUtils.checkLastInsnType(exit, InsnType.BREAK)) { || BlockUtils.checkLastInsnType(exit, InsnType.BREAK)) {
...@@ -747,23 +761,33 @@ public class RegionMaker { ...@@ -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) { private void processExcHandler(ExceptionHandler handler, Set<BlockNode> exits) {
BlockNode start = handler.getHandlerBlock(); BlockNode start = handler.getHandlerBlock();
if (start == null) { if (start == null) {
return; 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); RegionStack stack = new RegionStack(mth);
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); stack.addExits(exits);
}
BlockNode exit = BlockUtils.traverseWhileDominates(start, start); BitSet domFrontier = dom.getDomFrontier();
if (exit != null && RegionUtils.isRegionContainsBlock(mth.getRegion(), exit)) { 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); stack.addExit(exit);
} }
}
handler.setHandlerRegion(makeRegion(start, stack)); handler.setHandlerRegion(makeRegion(start, stack));
ExcHandlerAttr excHandlerAttr = start.get(AType.EXC_HANDLER); ExcHandlerAttr excHandlerAttr = start.get(AType.EXC_HANDLER);
......
...@@ -105,18 +105,19 @@ public class SSATransform extends AbstractVisitor { ...@@ -105,18 +105,19 @@ public class SSATransform extends AbstractVisitor {
for (InsnNode insn : block.getInstructions()) { for (InsnNode insn : block.getInstructions()) {
if (insn.getType() != InsnType.PHI) { if (insn.getType() != InsnType.PHI) {
for (InsnArg arg : insn.getArguments()) { for (InsnArg arg : insn.getArguments()) {
if (arg.isRegister()) { if (!arg.isRegister()) {
continue;
}
RegisterArg reg = (RegisterArg) arg; RegisterArg reg = (RegisterArg) arg;
int regNum = reg.getRegNum(); int regNum = reg.getRegNum();
SSAVar var = vars[regNum]; SSAVar var = vars[regNum];
if (var == null) { if (var == null) {
var = mth.makeNewSVar(regNum, vers, null); throw new JadxRuntimeException("Not initialized variable reg: " + regNum
vars[regNum] = var; + ", insn: " + insn + ", block:" + block + ", method: " + mth);
} }
var.use(reg); var.use(reg);
} }
} }
}
RegisterArg result = insn.getResult(); RegisterArg result = insn.getResult();
if (result != null) { if (result != null) {
int regNum = result.getRegNum(); int regNum = result.getRegNum();
...@@ -125,7 +126,9 @@ public class SSATransform extends AbstractVisitor { ...@@ -125,7 +126,9 @@ public class SSATransform extends AbstractVisitor {
} }
for (BlockNode s : block.getSuccessors()) { for (BlockNode s : block.getSuccessors()) {
PhiListAttr phiList = s.get(AType.PHI_LIST); PhiListAttr phiList = s.get(AType.PHI_LIST);
if (phiList != null) { if (phiList == null) {
continue;
}
int j = s.getPredecessors().indexOf(block); int j = s.getPredecessors().indexOf(block);
if (j == -1) { if (j == -1) {
throw new JadxRuntimeException("Can't find predecessor for " + block + " " + s); throw new JadxRuntimeException("Can't find predecessor for " + block + " " + s);
...@@ -137,14 +140,12 @@ public class SSATransform extends AbstractVisitor { ...@@ -137,14 +140,12 @@ public class SSATransform extends AbstractVisitor {
int regNum = phiInsn.getResult().getRegNum(); int regNum = phiInsn.getResult().getRegNum();
SSAVar var = vars[regNum]; SSAVar var = vars[regNum];
if (var == null) { if (var == null) {
var = mth.makeNewSVar(regNum, vers, null); continue;
vars[regNum] = var;
} }
var.use(phiInsn.getArg(j)); var.use(phiInsn.getArg(j));
var.setUsedInPhi(phiInsn); var.setUsedInPhi(phiInsn);
} }
} }
}
for (BlockNode domOn : block.getDominatesOn()) { for (BlockNode domOn : block.getDominatesOn()) {
renameVar(mth, vars, vers, domOn); renameVar(mth, vars, vers, domOn);
} }
...@@ -231,7 +232,10 @@ public class SSATransform extends AbstractVisitor { ...@@ -231,7 +232,10 @@ public class SSATransform extends AbstractVisitor {
for (PhiInsn phiInsn : insnToRemove) { for (PhiInsn phiInsn : insnToRemove) {
if (list.remove(phiInsn)) { if (list.remove(phiInsn)) {
for (InsnArg arg : phiInsn.getArguments()) { 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); InstructionRemover.remove(mth, block, phiInsn);
} }
......
...@@ -23,6 +23,8 @@ import java.util.LinkedList; ...@@ -23,6 +23,8 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.jetbrains.annotations.Nullable;
public class BlockUtils { public class BlockUtils {
private BlockUtils() { private BlockUtils() {
...@@ -112,12 +114,17 @@ public class BlockUtils { ...@@ -112,12 +114,17 @@ public class BlockUtils {
} }
public static boolean checkLastInsnType(BlockNode block, InsnType expectedType) { 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(); List<InsnNode> insns = block.getInstructions();
if (insns.isEmpty()) { if (insns.isEmpty()) {
return false; return null;
} }
InsnNode insn = insns.get(insns.size() - 1); return insns.get(insns.size() - 1);
return insn.getType() == expectedType;
} }
public static BlockNode getBlockByInsn(MethodNode mth, InsnNode insn) { public static BlockNode getBlockByInsn(MethodNode mth, InsnNode insn) {
...@@ -135,7 +142,7 @@ public class BlockUtils { ...@@ -135,7 +142,7 @@ public class BlockUtils {
return null; return null;
} }
private static BlockNode searchBlockWithPhi(MethodNode mth, PhiInsn insn) { public static BlockNode searchBlockWithPhi(MethodNode mth, PhiInsn insn) {
for (BlockNode block : mth.getBasicBlocks()) { for (BlockNode block : mth.getBasicBlocks()) {
PhiListAttr phiListAttr = block.get(AType.PHI_LIST); PhiListAttr phiListAttr = block.get(AType.PHI_LIST);
if (phiListAttr != null) { if (phiListAttr != null) {
...@@ -225,7 +232,11 @@ public class BlockUtils { ...@@ -225,7 +232,11 @@ public class BlockUtils {
} }
public static List<BlockNode> bitSetToBlocks(MethodNode mth, BitSet bs) { 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)) { for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
BlockNode block = mth.getBasicBlocks().get(i); BlockNode block = mth.getBasicBlocks().get(i);
blocks.add(block); blocks.add(block);
......
...@@ -183,10 +183,6 @@ public class RegionUtils { ...@@ -183,10 +183,6 @@ public class RegionUtils {
return true; return true;
} }
} }
if (tb.getFinalRegion() != null
&& isRegionContainsRegion(tb.getFinalRegion(), region)) {
return true;
}
} }
if (isRegionContainsRegion(b, region)) { if (isRegionContainsRegion(b, region)) {
return true; 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 { ...@@ -38,5 +38,8 @@ public class TestSynchronized extends IntegrationTest {
assertThat(code, containsString("synchronized (this.o) {")); assertThat(code, containsString("synchronized (this.o) {"));
assertThat(code, not(containsString(indent(3) + ";"))); 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; ...@@ -6,27 +6,47 @@ import jadx.tests.api.IntegrationTest;
import org.junit.Test; import org.junit.Test;
import static org.hamcrest.CoreMatchers.containsString; 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.assertThat;
import static org.junit.Assert.assertTrue;
public class TestTryCatch3 extends IntegrationTest { public class TestTryCatch3 extends IntegrationTest {
public static class TestCls { public static class TestCls {
private final static Object obj = new Object(); private int f = 0;
private boolean mDiscovering;
private boolean test(Object obj) { private boolean test(Object obj) {
this.mDiscovering = false; boolean res;
try { try {
exc(obj); res = exc(obj);
} catch (Exception e) { } catch (Exception e) {
e.toString(); res = false;
} finally { } 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 { ...@@ -38,5 +58,16 @@ public class TestTryCatch3 extends IntegrationTest {
assertThat(code, containsString("try {")); assertThat(code, containsString("try {"));
assertThat(code, containsString("exc(obj);")); assertThat(code, containsString("exc(obj);"));
assertThat(code, containsString("} catch (Exception e) {")); 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