core: refactor DotGraphVisitor
This commit is contained in:
@@ -62,7 +62,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
private List<BlockNode> exitBlocks;
|
||||
|
||||
private Region region;
|
||||
private List<ExceptionHandler> exceptionHandlers;
|
||||
private List<ExceptionHandler> exceptionHandlers = Collections.emptyList();
|
||||
private List<LoopAttr> loops = Collections.emptyList();
|
||||
|
||||
public MethodNode(ClassNode classNode, Method mthData) {
|
||||
@@ -134,9 +134,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
}
|
||||
blocks = null;
|
||||
exitBlocks = null;
|
||||
if (exceptionHandlers != null) {
|
||||
exceptionHandlers.clear();
|
||||
}
|
||||
exceptionHandlers.clear();
|
||||
noCode = true;
|
||||
}
|
||||
|
||||
@@ -440,7 +438,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
}
|
||||
|
||||
public ExceptionHandler addExceptionHandler(ExceptionHandler handler) {
|
||||
if (exceptionHandlers == null) {
|
||||
if (exceptionHandlers.isEmpty()) {
|
||||
exceptionHandlers = new ArrayList<ExceptionHandler>(2);
|
||||
} else {
|
||||
for (ExceptionHandler h : exceptionHandlers) {
|
||||
@@ -454,7 +452,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
}
|
||||
|
||||
public List<ExceptionHandler> getExceptionHandlers() {
|
||||
return exceptionHandlers;
|
||||
return Collections.unmodifiableList(exceptionHandlers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,8 @@ package jadx.core.dex.visitors;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.codegen.MethodGen;
|
||||
import jadx.core.dex.attributes.IAttributeNode;
|
||||
import jadx.core.dex.instructions.IfNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
@@ -10,9 +12,13 @@ import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.RegionUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class DotGraphVisitor extends AbstractVisitor {
|
||||
|
||||
@@ -23,6 +29,9 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
private final boolean useRegions;
|
||||
private final boolean rawInsn;
|
||||
|
||||
private CodeWriter dot;
|
||||
private CodeWriter conn;
|
||||
|
||||
public DotGraphVisitor(File outDir, boolean useRegions, boolean rawInsn) {
|
||||
this.dir = outDir;
|
||||
this.useRegions = useRegions;
|
||||
@@ -38,45 +47,39 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
if (mth.isNoCode()) {
|
||||
return;
|
||||
}
|
||||
CodeWriter dot = new CodeWriter();
|
||||
CodeWriter conn = new CodeWriter();
|
||||
dot = new CodeWriter();
|
||||
conn = new CodeWriter();
|
||||
|
||||
dot.startLine("digraph \"CFG for"
|
||||
+ escape(mth.getParentClass().getFullName() + "." + mth.getMethodInfo().getShortId())
|
||||
+ "\" {");
|
||||
dot.startLine("digraph \"CFG for");
|
||||
dot.add(escape(mth.getParentClass().getFullName() + "." + mth.getMethodInfo().getShortId()));
|
||||
dot.add("\" {");
|
||||
|
||||
if (useRegions) {
|
||||
if (mth.getRegion() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
processRegion(mth, mth.getRegion(), dot, conn);
|
||||
if (mth.getExceptionHandlers() != null) {
|
||||
for (ExceptionHandler h : mth.getExceptionHandlers()) {
|
||||
if (h.getHandlerRegion() != null) {
|
||||
processRegion(mth, h.getHandlerRegion(), dot, conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
processMethodRegion(mth);
|
||||
} else {
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
processBlock(mth, block, dot, conn);
|
||||
processBlock(mth, block);
|
||||
}
|
||||
}
|
||||
|
||||
String attrs = attributesString(mth);
|
||||
|
||||
dot.startLine("MethodNode[shape=record,label=\"{"
|
||||
+ escape(mth.getAccessFlags().makeString())
|
||||
+ escape(mth.getReturnType() + " "
|
||||
dot.startLine("MethodNode[shape=record,label=\"{");
|
||||
dot.add(escape(mth.getAccessFlags().makeString()));
|
||||
dot.add(escape(mth.getReturnType() + " "
|
||||
+ mth.getParentClass().getFullName() + "." + mth.getName()
|
||||
+ "(" + Utils.listToString(mth.getArguments(true)) + ") ")
|
||||
+ (attrs.length() == 0 ? "" : " | " + attrs)
|
||||
+ "}\"];");
|
||||
+ "(" + Utils.listToString(mth.getArguments(true)) + ") "));
|
||||
|
||||
dot.startLine("MethodNode -> " + makeName(mth.getEnterBlock()) + ";");
|
||||
String attrs = attributesString(mth);
|
||||
if (attrs.length() != 0) {
|
||||
dot.add(" | ").add(attrs);
|
||||
}
|
||||
dot.add("}\"];");
|
||||
|
||||
dot.add(conn);
|
||||
dot.startLine("MethodNode -> ").add(makeName(mth.getEnterBlock())).add(';');
|
||||
|
||||
dot.add(conn.toString());
|
||||
|
||||
dot.startLine('}');
|
||||
dot.startLine();
|
||||
@@ -88,27 +91,55 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
dot.save(dir, mth.getParentClass().getClassInfo().getFullPath() + "_graphs", fileName);
|
||||
}
|
||||
|
||||
private void processRegion(MethodNode mth, IContainer region, CodeWriter dot, CodeWriter conn) {
|
||||
private void processMethodRegion(MethodNode mth) {
|
||||
processRegion(mth, mth.getRegion());
|
||||
for (ExceptionHandler h : mth.getExceptionHandlers()) {
|
||||
if (h.getHandlerRegion() != null) {
|
||||
processRegion(mth, h.getHandlerRegion());
|
||||
}
|
||||
}
|
||||
Set<BlockNode> regionsBlocks = new HashSet<BlockNode>(mth.getBasicBlocks().size());
|
||||
RegionUtils.getAllRegionBlocks(mth.getRegion(), regionsBlocks);
|
||||
for (ExceptionHandler handler : mth.getExceptionHandlers()) {
|
||||
IContainer handlerRegion = handler.getHandlerRegion();
|
||||
if (handlerRegion != null) {
|
||||
RegionUtils.getAllRegionBlocks(handlerRegion, regionsBlocks);
|
||||
}
|
||||
}
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
if (!regionsBlocks.contains(block)) {
|
||||
processBlock(mth, block, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processRegion(MethodNode mth, IContainer region) {
|
||||
if (region instanceof IRegion) {
|
||||
IRegion r = (IRegion) region;
|
||||
String attrs = attributesString(r);
|
||||
dot.startLine("subgraph " + makeName(region) + " {");
|
||||
dot.startLine("label = \"" + r
|
||||
+ (attrs.length() == 0 ? "" : " | " + attrs)
|
||||
+ "\";");
|
||||
dot.startLine("label = \"").add(r);
|
||||
String attrs = attributesString(r);
|
||||
if (attrs.length() != 0) {
|
||||
dot.add(" | ").add(attrs);
|
||||
}
|
||||
dot.add("\";");
|
||||
dot.startLine("node [shape=record,color=blue];");
|
||||
|
||||
for (IContainer c : r.getSubBlocks()) {
|
||||
processRegion(mth, c, dot, conn);
|
||||
processRegion(mth, c);
|
||||
}
|
||||
|
||||
dot.startLine('}');
|
||||
} else if (region instanceof BlockNode) {
|
||||
processBlock(mth, (BlockNode) region, dot, conn);
|
||||
processBlock(mth, (BlockNode) region);
|
||||
}
|
||||
}
|
||||
|
||||
private void processBlock(MethodNode mth, BlockNode block, CodeWriter dot, CodeWriter conn) {
|
||||
private void processBlock(MethodNode mth, BlockNode block) {
|
||||
processBlock(mth, block, false);
|
||||
}
|
||||
|
||||
private void processBlock(MethodNode mth, BlockNode block, boolean error) {
|
||||
String attrs = attributesString(block);
|
||||
if (PRINT_REGISTERS_STATES) {
|
||||
if (block.getStartState() != null) {
|
||||
@@ -119,21 +150,34 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
attrs += escape("RE: " + block.getEndState()) + NL;
|
||||
}
|
||||
}
|
||||
|
||||
String insns = insertInsns(mth, block);
|
||||
|
||||
dot.startLine(makeName(block) + " [shape=record,label=\"{"
|
||||
+ block.getId() + "\\:\\ "
|
||||
+ InsnUtils.formatOffset(block.getStartOffset())
|
||||
+ (attrs.length() == 0 ? "" : "|" + attrs)
|
||||
+ (insns.length() == 0 ? "" : "|" + insns)
|
||||
+ "}\"];");
|
||||
|
||||
for (BlockNode next : block.getSuccessors()) {
|
||||
conn.startLine(makeName(block) + " -> " + makeName(next) + ";");
|
||||
dot.startLine(makeName(block));
|
||||
dot.add(" [shape=record,");
|
||||
if (error) {
|
||||
dot.add("color=red,");
|
||||
}
|
||||
for (BlockNode next : block.getDominatesOn()) {
|
||||
conn.startLine(makeName(block) + " -> " + makeName(next) + "[style=dotted];");
|
||||
dot.add("label=\"{");
|
||||
dot.add(block.getId()).add("\\:\\ ");
|
||||
dot.add(InsnUtils.formatOffset(block.getStartOffset()));
|
||||
if (attrs.length() != 0) {
|
||||
dot.add('|').add(attrs);
|
||||
}
|
||||
String insns = insertInsns(mth, block);
|
||||
if (insns.length() != 0) {
|
||||
dot.add('|').add(insns);
|
||||
}
|
||||
dot.add("}\"];");
|
||||
|
||||
BlockNode falsePath = null;
|
||||
List<InsnNode> list = block.getInstructions();
|
||||
if (!list.isEmpty() && list.get(0).getType() == InsnType.IF) {
|
||||
falsePath = ((IfNode) list.get(0)).getElseBlock();
|
||||
}
|
||||
for (BlockNode next : block.getSuccessors()) {
|
||||
conn.startLine(makeName(block)).add(" -> ").add(makeName(next));
|
||||
if (next == falsePath) {
|
||||
conn.add("[style=dotted]");
|
||||
}
|
||||
conn.add(';');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,8 @@ public class DepthRegionTraverser {
|
||||
public static void traverseAll(MethodNode mth, IRegionVisitor visitor) {
|
||||
traverse(mth, visitor, mth.getRegion());
|
||||
|
||||
if (mth.getExceptionHandlers() != null) {
|
||||
for (ExceptionHandler h : mth.getExceptionHandlers()) {
|
||||
traverse(mth, visitor, h.getHandlerRegion());
|
||||
}
|
||||
for (ExceptionHandler h : mth.getExceptionHandlers()) {
|
||||
traverse(mth, visitor, h.getHandlerRegion());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor {
|
||||
private final Map<BlockNode, TryCatchBlock> tryBlocksMap = new HashMap<BlockNode, TryCatchBlock>(2);
|
||||
|
||||
public ProcessTryCatchRegions(MethodNode mth) {
|
||||
if (mth.isNoCode() || mth.getExceptionHandlers() == null) {
|
||||
if (mth.isNoCode() || mth.getExceptionHandlers().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public class RegionMakerVisitor extends AbstractVisitor {
|
||||
// fill region structure
|
||||
mth.setRegion(rm.makeRegion(mth.getEnterBlock(), state));
|
||||
|
||||
if (mth.getExceptionHandlers() != null) {
|
||||
if (!mth.getExceptionHandlers().isEmpty()) {
|
||||
state = new RegionStack(mth);
|
||||
for (ExceptionHandler handler : mth.getExceptionHandlers()) {
|
||||
rm.processExcHandler(handler, state);
|
||||
|
||||
@@ -11,6 +11,7 @@ import jadx.core.dex.trycatch.TryCatchBlock;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class RegionUtils {
|
||||
|
||||
@@ -47,7 +48,7 @@ public class RegionUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void getAllRegionBlocks(IContainer container, List<BlockNode> blocks) {
|
||||
public static void getAllRegionBlocks(IContainer container, Set<BlockNode> blocks) {
|
||||
if (container instanceof BlockNode) {
|
||||
blocks.add((BlockNode) container);
|
||||
} else if (container instanceof IRegion) {
|
||||
@@ -108,7 +109,8 @@ public class RegionUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if {@code region} contains in {@code container}.<br>
|
||||
* Check if {@code region} contains in {@code container}.
|
||||
* <br>
|
||||
* For simple region (not from exception handlers) search in parents
|
||||
* otherwise run recursive search because exception handlers can have several parents
|
||||
*/
|
||||
@@ -124,9 +126,8 @@ public class RegionUtils {
|
||||
if (parent == null) {
|
||||
if (region.getAttributes().contains(AttributeType.EXC_HANDLER)) {
|
||||
return isRegionContainsExcHandlerRegion(container, region);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
region = parent;
|
||||
parent = region.getParent();
|
||||
|
||||
Reference in New Issue
Block a user