diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java index 16552c8ef..c3daf3567 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java @@ -62,7 +62,7 @@ public class MethodNode extends LineAttrNode implements ILoadable { private List exitBlocks; private Region region; - private List exceptionHandlers; + private List exceptionHandlers = Collections.emptyList(); private List 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(2); } else { for (ExceptionHandler h : exceptionHandlers) { @@ -454,7 +452,7 @@ public class MethodNode extends LineAttrNode implements ILoadable { } public List getExceptionHandlers() { - return exceptionHandlers; + return Collections.unmodifiableList(exceptionHandlers); } /** diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java index 00bfa319c..d1dbee386 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java @@ -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 regionsBlocks = new HashSet(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 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(';'); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/DepthRegionTraverser.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/DepthRegionTraverser.java index da51c2723..2d773a4d3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/DepthRegionTraverser.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/DepthRegionTraverser.java @@ -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()); } } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java index a3d71c4e9..4e202be1f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java @@ -40,7 +40,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor { private final Map tryBlocksMap = new HashMap(2); public ProcessTryCatchRegions(MethodNode mth) { - if (mth.isNoCode() || mth.getExceptionHandlers() == null) { + if (mth.isNoCode() || mth.getExceptionHandlers().isEmpty()) { return; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java index a8010183b..a91aaf4c4 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java @@ -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); diff --git a/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java b/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java index 4c812e476..5d365dc3c 100644 --- a/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java @@ -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 blocks) { + public static void getAllRegionBlocks(IContainer container, Set 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}.
+ * Check if {@code region} contains in {@code container}. + *
* 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();