Commit b73cb406 authored by Skylot's avatar Skylot

core: refactor DotGraphVisitor

parent ca448fc4
......@@ -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;
}
}
dot.startLine(makeName(block));
dot.add(" [shape=record,");
if (error) {
dot.add("color=red,");
}
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("}\"];");
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) + ";");
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.getDominatesOn()) {
conn.startLine(makeName(block) + " -> " + makeName(next) + "[style=dotted];");
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();
......
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