This commit is contained in:
Claudio Maggioni 2023-10-11 13:50:56 +02:00
parent f4252692e6
commit c3151c951f
51 changed files with 842 additions and 165 deletions

View file

@ -17,13 +17,13 @@ Please complete this checklist (turn [ ] into [X]) before you submit:
- [x] I completed the above Submission Info
- [x] I built the project in IntelliJ (Build > Build Project)
- [ ] I implemented the ControlFlowGraphBuilder
- [ ] I implemented the ControlFlowGraphRenderer
- [ ] I wrote the source code myself and did not look at the source code of my class mates
- [ ] I ran all the JUnit tests and verified that they all pass
- [ ] I manually checked that my implementation is correct by doing this:
- [ ] I studied the test-input/ExampleClass.java source code
- [ ] I ran App to produce the dot files (in test-output/)
- [x] I implemented the ControlFlowGraphBuilder
- [x] I implemented the ControlFlowGraphRenderer
- [x] I wrote the source code myself and did not look at the source code of my class mates
- [x] I ran all the JUnit tests and verified that they all pass
- [x] I manually checked that my implementation is correct by doing this:
- [x] I studied the test-input/ExampleClass.java source code
- [x] I ran App to produce the dot files (in test-output/)
- [ ] I ran dot to turn the dot files in test-output into a PDF
- [ ] I manually verified that the PDF contains one page per method of ExampleClass
- [ ] I manually verified that those CFGs correspond to the methods' source code

6
compile-tests.sh Executable file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -eou pipefail
cd -- "$( dirname -- "${BASH_SOURCE[0]}" )"
cat test-output/*.dot | gvpack -u | dot -Tpdf > test-output/all.pdf

View file

@ -1,24 +1,139 @@
package ch.usi.inf.sp.cfg;
import ch.usi.inf.sp.bytecode.Disassembler;
import ch.usi.inf.sp.cfg.builder.JumpSource;
import ch.usi.inf.sp.cfg.builder.LookupSwitchInstructionNodeInfo;
import ch.usi.inf.sp.cfg.builder.MultiMap;
import ch.usi.inf.sp.cfg.builder.SwitchInstructionNodeInfo;
import ch.usi.inf.sp.cfg.builder.TableSwitchInstructionNodeInfo;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import java.util.*;
import ch.usi.inf.sp.bytecode.Disassembler;
import ch.usi.inf.sp.cfg.builder.CFGBuilder;
import ch.usi.inf.sp.cfg.builder.Edge;
import ch.usi.inf.sp.cfg.builder.MultiMap;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LookupSwitchInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
public final class ControlFlowGraphBuilder {
private static final String DEFAULT_LABEL = "default";
private static final String TRUE_LABEL = "T";
private final MultiMap<LabelNode, JumpSource> labelToIncomingEdges = new MultiMap<>();
private final List<LabelNode> labels = new ArrayList<>();
private final Map<AbstractInsnNode, BasicBlock> insnToBlock = new HashMap<>();
private final ControlFlowGraph graph = new ControlFlowGraph();
private final MethodNode method;
private BasicBlock currentBasicBlock;
public class ControlFlowGraphBuilder {
private ControlFlowGraphBuilder(MethodNode method) {
this.method = method;
setNewBasicBlock(0);
graph.addEntryEdge(currentBasicBlock);
}
public static ControlFlowGraph createControlFlowGraph(final MethodNode method) {
return new CFGBuilder(method).create();
return new ControlFlowGraphBuilder(method).create();
}
private void setNewBasicBlock(int id) {
currentBasicBlock = new BasicBlock(id);
graph.addNode(currentBasicBlock);
}
public ControlFlowGraph create() {
// get the list of all instructions in that method
final InsnList instructions = method.instructions;
for (int i = 0; i < instructions.size(); i++) {
final AbstractInsnNode instruction = instructions.get(i);
switch (instruction.getType()) {
case AbstractInsnNode.JUMP_INSN ->
labelToIncomingEdges.put(((JumpInsnNode) instruction).label, new JumpSource(instruction, TRUE_LABEL));
case AbstractInsnNode.LOOKUPSWITCH_INSN ->
captureSwitchEdges(new LookupSwitchInstructionNodeInfo((LookupSwitchInsnNode) instruction));
case AbstractInsnNode.TABLESWITCH_INSN ->
captureSwitchEdges(new TableSwitchInstructionNodeInfo((TableSwitchInsnNode) instruction));
}
}
for (int i = 0; i < instructions.size(); i++) {
final AbstractInsnNode instruction = instructions.get(i);
currentBasicBlock.appendInstruction(Disassembler.disassembleInstruction(instruction, i, instructions));
if (isReturnInstruction(instruction)) {
graph.addExitEdge(currentBasicBlock);
}
insnToBlock.put(instruction, currentBasicBlock);
if (isInsnSignificantLabel(instruction)) {
labels.add((LabelNode) instruction);
}
if (isEndOfBlock(instruction)) {
final BasicBlock previousBasicBlock = currentBasicBlock;
setNewBasicBlock(i + 1);
// GOTO and SWITCH instructions do not have a fallthrough edge, otherwise add one
if (instruction.getOpcode() != Opcodes.GOTO
&& instruction.getType() != AbstractInsnNode.LOOKUPSWITCH_INSN
&& instruction.getType() != AbstractInsnNode.TABLESWITCH_INSN) {
graph.addFallthroughEdge(previousBasicBlock, currentBasicBlock);
}
}
}
for (final LabelNode label : labels) {
final BasicBlock toBB = Objects.requireNonNull(insnToBlock.get(label));
for (final JumpSource jumpSource : labelToIncomingEdges.getAll(label)) {
final BasicBlock fromBB = jumpSource.block(insnToBlock);
final ControlFlowEdge edge = jumpSource.edge();
graph.addEdge(edge);
graph.connect(fromBB, edge, toBB);
}
}
return graph;
}
private void captureSwitchEdges(final SwitchInstructionNodeInfo info) {
for (int j = 0; j < info.getCaseCount(); j++) {
final LabelNode label = info.getLabelForCase(j);
final String value = info.getKeyForCase(j);
labelToIncomingEdges.put(label, new JumpSource(info.getNode(), value));
}
if (info.getDefaultCase() != null) {
labelToIncomingEdges.put(info.getDefaultCase(), new JumpSource(info.getNode(), DEFAULT_LABEL));
}
}
private boolean isInsnSignificantLabel(final AbstractInsnNode node) {
return node instanceof LabelNode && labelToIncomingEdges.containsKey((LabelNode) node);
}
private boolean isReturnInstruction(final AbstractInsnNode instruction) {
final int opcode = instruction.getOpcode();
return opcode == Opcodes.RETURN ||
opcode == Opcodes.ARETURN ||
opcode == Opcodes.LRETURN ||
opcode == Opcodes.IRETURN ||
opcode == Opcodes.FRETURN;
}
private boolean isEndOfBlock(final AbstractInsnNode instruction) {
final AbstractInsnNode nextInsn = instruction.getNext();
if (nextInsn == null) {
return false; // cannot start another bb at the end of the method with 0 instructions
}
final int type = instruction.getType();
if (type == AbstractInsnNode.JUMP_INSN ||
type == AbstractInsnNode.LOOKUPSWITCH_INSN ||
type == AbstractInsnNode.TABLESWITCH_INSN) {
return true; // if we're branching or jumping after this instruction, we NEED to cut the current bb short
}
// if the next instruction is a label some other bb may jump into then cut, otherwise continue
return isInsnSignificantLabel(nextInsn);
}
}

View file

@ -1,11 +1,66 @@
package ch.usi.inf.sp.cfg;
public class ControlFlowGraphRenderer {
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public static String renderControlFlowGraph(final String label, final ControlFlowGraph cfg) {
//TODO
return null;
public class ControlFlowGraphRenderer {
private static final String INDENTATION = " ";
private final StringBuilder code = new StringBuilder();
private int indentationLevel = 0;
private static String nodeIdentifier(final BasicBlock bb) {
if (bb.getInEdges().isEmpty()) {
return "e";
} else if (bb.getOutEdges().isEmpty()) {
return "x";
} else {
return "bb" + bb.getId();
}
}
private static String nodeStyle(final BasicBlock bb) {
if (bb.getInEdges().isEmpty()) {
return "[shape=circle,label=\"e\"]";
} else if (bb.getOutEdges().isEmpty()) {
return "[shape=circle,label=\"x\"]";
} else {
return "[label=\"" + bb.getId() + "|{" +
StreamSupport.stream(bb.getInstructions().spliterator(), false)
.collect(Collectors.joining("|"))
+ "}\"]";
}
}
public static String renderControlFlowGraph(final String label, final ControlFlowGraph cfg) {
return new ControlFlowGraphRenderer().render(label, cfg);
}
private void line(String line) {
code.append(INDENTATION.repeat(indentationLevel)).append(line).append('\n');
}
private String render(final String label, final ControlFlowGraph graph) {
line("digraph " + label + " {");
indentationLevel++;
line("label=\"" + label + "\";");
code.append('\n');
line("node [shape=record]");
for (final BasicBlock bb : graph.getNodes()) {
line(nodeIdentifier(bb) + " " + nodeStyle(bb));
}
code.append('\n');
for (var e : graph.getEdges()) {
final String l = e.getLabel();
final String suffix = l == null || l.isBlank() ? "" : (" [label=\"" + e.getLabel() + "\"]");
line(nodeIdentifier(e.getFrom()) + " -> " + nodeIdentifier(e.getTo()) + suffix);
}
indentationLevel--;
line("}");
return code.toString();
}
}

View file

@ -1,134 +0,0 @@
package ch.usi.inf.sp.cfg.builder;
import ch.usi.inf.sp.bytecode.Disassembler;
import ch.usi.inf.sp.cfg.BasicBlock;
import ch.usi.inf.sp.cfg.ControlFlowEdge;
import ch.usi.inf.sp.cfg.ControlFlowGraph;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import java.util.*;
public final class CFGBuilder {
private final MultiMap<LabelNode, ch.usi.inf.sp.cfg.builder.Edge> labelToIncomingEdges = new MultiMap<>();
private final List<LabelNode> labels = new ArrayList<>();
private final Map<AbstractInsnNode, BasicBlock> insnToBlock = new HashMap<>();
private final ControlFlowGraph graph = new ControlFlowGraph();
private final MethodNode method;
private BasicBlock currentBasicBlock;
public CFGBuilder(MethodNode method) {
this.method = method;
setNewBasicBlock(0);
graph.addEntryEdge(currentBasicBlock);
}
private void setNewBasicBlock(int id) {
currentBasicBlock = new BasicBlock(id);
graph.addNode(currentBasicBlock);
}
public ControlFlowGraph create() {
// get the list of all instructions in that method
final InsnList instructions = method.instructions;
for (int i = 0; i < instructions.size(); i++) {
final AbstractInsnNode instruction = instructions.get(i);
switch (instruction.getType()) {
case AbstractInsnNode.JUMP_INSN -> labelToIncomingEdges.put(
((JumpInsnNode) instruction).label,
new Edge(instruction, "T")
);
case AbstractInsnNode.LOOKUPSWITCH_INSN ->
captureSwitchEdges(new LookupSwitchInstructionNodeInfo((LookupSwitchInsnNode) instruction));
case AbstractInsnNode.TABLESWITCH_INSN ->
captureSwitchEdges(new TableSwitchInstructionNodeInfo((TableSwitchInsnNode) instruction));
}
}
for (int i = 0; i < instructions.size(); i++) {
final AbstractInsnNode instruction = instructions.get(i);
currentBasicBlock.appendInstruction(Disassembler.disassembleInstruction(instruction, i, instructions));
if (isReturnInstruction(instruction)) {
graph.addExitEdge(currentBasicBlock);
}
insnToBlock.put(instruction, currentBasicBlock);
if (isInsnSignificantLabel(instruction)) {
labels.add((LabelNode) instruction);
}
if (isEndOfBlock(instruction)) {
final int nextI = i + 1;
final BasicBlock previousBasicBlock = currentBasicBlock;
setNewBasicBlock(nextI);
// Handle "implicit" edges from this basic block to the next one, or the exit node
if (instructions.get(nextI).getOpcode() != Opcodes.GOTO) {
graph.addFallthroughEdge(previousBasicBlock, currentBasicBlock);
}
}
}
for (final LabelNode label : labels) {
final BasicBlock toBB = Objects.requireNonNull(insnToBlock.get(label));
for (final ch.usi.inf.sp.cfg.builder.Edge incomingEdge : labelToIncomingEdges.getAll(label)) {
final BasicBlock fromBB = Objects.requireNonNull(insnToBlock.get(incomingEdge.instruction()));
final ControlFlowEdge edge = new ControlFlowEdge(incomingEdge.condition());
graph.addEdge(edge);
graph.connect(fromBB, edge, toBB);
}
}
return graph;
}
private void captureSwitchEdges(final SwitchInstructionNodeInfo info) {
for (int j = 0; j < info.getCaseCount(); j++) {
final LabelNode label = info.getLabelForCase(j);
final String value = info.getKeyForCase(j);
labelToIncomingEdges.put(label, new Edge(info.getNode(), value));
}
if (info.getDefaultCase() != null) {
labelToIncomingEdges.put(info.getDefaultCase(), new Edge(info.getNode(), "default"));
}
}
private boolean isInsnSignificantLabel(final AbstractInsnNode node) {
return node instanceof LabelNode && labelToIncomingEdges.containsKey((LabelNode) node);
}
private boolean isReturnInstruction(final AbstractInsnNode instruction) {
final int opcode = instruction.getOpcode();
return opcode == Opcodes.RETURN ||
opcode == Opcodes.ARETURN ||
opcode == Opcodes.LRETURN ||
opcode == Opcodes.IRETURN ||
opcode == Opcodes.FRETURN;
}
private boolean isEndOfBlock(final AbstractInsnNode instruction) {
final AbstractInsnNode nextInsn = instruction.getNext();
if (nextInsn == null) {
return false; // cannot start another bb at the end of the method with 0 instructions
}
final int type = instruction.getType();
if (type == AbstractInsnNode.JUMP_INSN ||
type == AbstractInsnNode.LOOKUPSWITCH_INSN ||
type == AbstractInsnNode.TABLESWITCH_INSN) {
return true; // if we're branching or jumping after this instruction, we NEED to cut the current bb short
}
// if the next instruction is a label some other bb may jump into then cut, otherwise continue
return isInsnSignificantLabel(nextInsn);
}
}

View file

@ -1,6 +0,0 @@
package ch.usi.inf.sp.cfg.builder;
import org.objectweb.asm.tree.AbstractInsnNode;
public record Edge(AbstractInsnNode instruction, String condition) {
}

View file

@ -0,0 +1,19 @@
package ch.usi.inf.sp.cfg.builder;
import ch.usi.inf.sp.cfg.BasicBlock;
import ch.usi.inf.sp.cfg.ControlFlowEdge;
import org.objectweb.asm.tree.AbstractInsnNode;
import java.util.Map;
import java.util.Objects;
public record JumpSource(AbstractInsnNode instruction, String condition) {
public BasicBlock block(Map<AbstractInsnNode, BasicBlock> insnToBlock) {
return Objects.requireNonNull(insnToBlock.get(this.instruction));
}
public ControlFlowEdge edge() {
return new ControlFlowEdge(this.condition);
}
}

View file

@ -0,0 +1,11 @@
digraph <init> {
label="<init>";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ALOAD 0|3: INVOKESPECIAL java/lang/Object.<init> ()V|4: RETURN }"]
e -> bb0
bb0 -> x
}

BIN
test-output/all.pdf Normal file

Binary file not shown.

View file

@ -0,0 +1,11 @@
digraph alloc2Of3dArrayMethod {
label="alloc2Of3dArrayMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_2 |3: ICONST_3 |4: MULTIANEWARRAY [[[I 2|5: ARETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph alloc2dArrayMethod {
label="alloc2dArrayMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_2 |3: ICONST_3 |4: MULTIANEWARRAY [[I 2|5: ARETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph allocAndInit2dArrayMethod {
label="allocAndInit2dArrayMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_1 |3: ANEWARRAY [I|4: DUP |5: ICONST_0 |6: ICONST_1 |7: NEWARRAY T_INT|8: DUP |9: ICONST_0 |10: ICONST_1 |11: IASTORE |12: AASTORE |13: ARETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph allocAndInitIntArrayMethod {
label="allocAndInitIntArrayMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_2 |3: NEWARRAY T_INT|4: DUP |5: ICONST_0 |6: ICONST_1 |7: IASTORE |8: DUP |9: ICONST_1 |10: ICONST_2 |11: IASTORE |12: ARETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph allocAndInitObjectArrayMethod {
label="allocAndInitObjectArrayMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_2 |3: ANEWARRAY java/lang/Object|4: DUP |5: ICONST_0 |6: LDC 1|7: AASTORE |8: DUP |9: ICONST_1 |10: LDC 2|11: AASTORE |12: ARETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph allocIncomplete2dArrayMethod {
label="allocIncomplete2dArrayMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_2 |3: ANEWARRAY [I|4: ARETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph allocIntArrayMethod {
label="allocIntArrayMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_3 |3: NEWARRAY T_INT|4: ARETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph allocObjectArrayMethod {
label="allocObjectArrayMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_3 |3: ANEWARRAY java/lang/Object|4: ARETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph allocObjectMethod {
label="allocObjectMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: NEW java/lang/Object|3: DUP |4: INVOKESPECIAL java/lang/Object.<init> ()V|5: ARETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph arrayLengthMethod {
label="arrayLengthMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ALOAD 1|3: ARRAYLENGTH |4: IRETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph arrayReadMethod {
label="arrayReadMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ALOAD 1|3: ICONST_0 |4: AALOAD |5: ARETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph arrayWriteMethod {
label="arrayWriteMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ALOAD 1|3: ICONST_0 |4: ALOAD 2|5: AASTORE |6: // label|7: // line number information|8: RETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,18 @@
digraph condMethod {
label="condMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ILOAD 1|3: ILOAD 2|4: IF_ICMPLE 7}"]
bb5 [label="5|{5: ILOAD 1|6: GOTO 10}"]
bb7 [label="7|{7: // label|8: // stack frame map|9: ILOAD 2}"]
bb10 [label="10|{10: // label|11: // stack frame map|12: IRETURN }"]
e -> bb0
bb0 -> bb5
bb7 -> bb10
bb10 -> x
bb0 -> bb7 [label="T"]
bb5 -> bb10 [label="T"]
}

View file

@ -0,0 +1,16 @@
digraph doWhileMethod {
label="doWhileMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_0 |3: ISTORE 2}"]
bb4 [label="4|{4: // label|5: // line number information|6: // stack frame map|7: ILOAD 2|8: ILOAD 1|9: IADD |10: ISTORE 2|11: // label|12: // line number information|13: IINC 1 -1|14: // label|15: // line number information|16: ILOAD 1|17: IFGT 4}"]
bb18 [label="18|{18: // label|19: // line number information|20: ILOAD 2|21: IRETURN }"]
e -> bb0
bb0 -> bb4
bb4 -> bb18
bb18 -> x
bb4 -> bb4 [label="T"]
}

View file

@ -0,0 +1,11 @@
digraph doWhileTrue {
label="doWhileTrue";
node [shape=record]
e [shape=circle,label="e"]
e [shape=circle,label="e"]
bb0 [label="0|{0: // label|1: // line number information|2: // stack frame map|3: IINC 1 1|4: // label|5: // line number information|6: GOTO 0}"]
e -> bb0
bb0 -> bb0 [label="T"]
}

View file

@ -0,0 +1,11 @@
digraph emptyMethod {
label="emptyMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: RETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph fieldReadMethod {
label="fieldReadMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ALOAD 0|3: GETFIELD ExampleClass.field Ljava/lang/String;|4: ARETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph fieldWriteMethod {
label="fieldWriteMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ALOAD 0|3: ALOAD 1|4: PUTFIELD ExampleClass.field Ljava/lang/String;|5: // label|6: // line number information|7: RETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,18 @@
digraph forEachArrayMethod {
label="forEachArrayMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_0 |3: ISTORE 2|4: // label|5: // line number information|6: ALOAD 1|7: ASTORE 3|8: ALOAD 3|9: ARRAYLENGTH |10: ISTORE 4|11: ICONST_0 |12: ISTORE 5}"]
bb13 [label="13|{13: // label|14: // stack frame map|15: ILOAD 5|16: ILOAD 4|17: IF_ICMPGE 29}"]
bb18 [label="18|{18: ALOAD 3|19: ILOAD 5|20: AALOAD |21: ASTORE 6|22: // label|23: // line number information|24: IINC 2 1|25: // label|26: // line number information|27: IINC 5 1|28: GOTO 13}"]
bb29 [label="29|{29: // label|30: // line number information|31: // stack frame map|32: ILOAD 2|33: IRETURN }"]
e -> bb0
bb0 -> bb13
bb13 -> bb18
bb29 -> x
bb18 -> bb13 [label="T"]
bb13 -> bb29 [label="T"]
}

View file

@ -0,0 +1,18 @@
digraph forEachCollectionMethod {
label="forEachCollectionMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_0 |3: ISTORE 2|4: // label|5: // line number information|6: ALOAD 1|7: INVOKEINTERFACE java/util/Set.iterator ()Ljava/util/Iterator;|8: ASTORE 3}"]
bb9 [label="9|{9: // label|10: // stack frame map|11: ALOAD 3|12: INVOKEINTERFACE java/util/Iterator.hasNext ()Z|13: IFEQ 24}"]
bb14 [label="14|{14: ALOAD 3|15: INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object;|16: CHECKCAST java/lang/String|17: ASTORE 4|18: // label|19: // line number information|20: IINC 2 1|21: // label|22: // line number information|23: GOTO 9}"]
bb24 [label="24|{24: // label|25: // line number information|26: // stack frame map|27: ILOAD 2|28: IRETURN }"]
e -> bb0
bb0 -> bb9
bb9 -> bb14
bb24 -> x
bb14 -> bb9 [label="T"]
bb9 -> bb24 [label="T"]
}

13
test-output/forEver.dot Normal file
View file

@ -0,0 +1,13 @@
digraph forEver {
label="forEver";
node [shape=record]
e [shape=circle,label="e"]
e [shape=circle,label="e"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_0 |3: ISTORE 2}"]
bb4 [label="4|{4: // label|5: // stack frame map|6: IINC 2 1|7: GOTO 4}"]
e -> bb0
bb0 -> bb4
bb4 -> bb4 [label="T"]
}

18
test-output/forMethod.dot Normal file
View file

@ -0,0 +1,18 @@
digraph forMethod {
label="forMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_0 |3: ISTORE 2|4: // label|5: // line number information|6: ICONST_0 |7: ISTORE 3}"]
bb8 [label="8|{8: // label|9: // stack frame map|10: ILOAD 3|11: ILOAD 1|12: IF_ICMPGE 23}"]
bb13 [label="13|{13: // label|14: // line number information|15: ILOAD 2|16: ILOAD 3|17: IADD |18: ISTORE 2|19: // label|20: // line number information|21: IINC 1 1|22: GOTO 8}"]
bb23 [label="23|{23: // label|24: // line number information|25: // stack frame map|26: ILOAD 2|27: IRETURN }"]
e -> bb0
bb0 -> bb8
bb8 -> bb13
bb23 -> x
bb13 -> bb8 [label="T"]
bb8 -> bb23 [label="T"]
}

View file

@ -0,0 +1,23 @@
digraph forWithBreakMethod {
label="forWithBreakMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_0 |3: ISTORE 2|4: // label|5: // line number information|6: ICONST_0 |7: ISTORE 3}"]
bb8 [label="8|{8: // label|9: // stack frame map|10: ILOAD 3|11: ILOAD 1|12: IF_ICMPGE 32}"]
bb13 [label="13|{13: // label|14: // line number information|15: ILOAD 3|16: BIPUSH 10|17: IF_ICMPNE 21}"]
bb18 [label="18|{18: // label|19: // line number information|20: GOTO 32}"]
bb21 [label="21|{21: // label|22: // line number information|23: // stack frame map|24: ILOAD 2|25: ILOAD 3|26: IADD |27: ISTORE 2|28: // label|29: // line number information|30: IINC 3 1|31: GOTO 8}"]
bb32 [label="32|{32: // label|33: // line number information|34: // stack frame map|35: ILOAD 2|36: IRETURN }"]
e -> bb0
bb0 -> bb8
bb8 -> bb13
bb13 -> bb18
bb32 -> x
bb21 -> bb8 [label="T"]
bb13 -> bb21 [label="T"]
bb8 -> bb32 [label="T"]
bb18 -> bb32 [label="T"]
}

View file

@ -0,0 +1,25 @@
digraph forWithContinueMethod {
label="forWithContinueMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_0 |3: ISTORE 2|4: // label|5: // line number information|6: ICONST_0 |7: ISTORE 3}"]
bb8 [label="8|{8: // label|9: // stack frame map|10: ILOAD 3|11: ILOAD 1|12: IF_ICMPGE 33}"]
bb13 [label="13|{13: // label|14: // line number information|15: ILOAD 3|16: BIPUSH 10|17: IF_ICMPNE 21}"]
bb18 [label="18|{18: // label|19: // line number information|20: GOTO 28}"]
bb21 [label="21|{21: // label|22: // line number information|23: // stack frame map|24: ILOAD 2|25: ILOAD 3|26: IADD |27: ISTORE 2}"]
bb28 [label="28|{28: // label|29: // line number information|30: // stack frame map|31: IINC 3 1|32: GOTO 8}"]
bb33 [label="33|{33: // label|34: // line number information|35: // stack frame map|36: ILOAD 2|37: IRETURN }"]
e -> bb0
bb0 -> bb8
bb8 -> bb13
bb13 -> bb18
bb21 -> bb28
bb33 -> x
bb28 -> bb8 [label="T"]
bb13 -> bb21 [label="T"]
bb18 -> bb28 [label="T"]
bb8 -> bb33 [label="T"]
}

View file

@ -0,0 +1,18 @@
digraph ifElseMethod {
label="ifElseMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_0 |3: ISTORE 2|4: // label|5: // line number information|6: ILOAD 1|7: IFLE 13}"]
bb8 [label="8|{8: // label|9: // line number information|10: ICONST_0 |11: ISTORE 2|12: GOTO 18}"]
bb13 [label="13|{13: // label|14: // line number information|15: // stack frame map|16: ILOAD 1|17: ISTORE 2}"]
bb18 [label="18|{18: // label|19: // line number information|20: // stack frame map|21: ILOAD 2|22: IRETURN }"]
e -> bb0
bb0 -> bb8
bb13 -> bb18
bb18 -> x
bb0 -> bb13 [label="T"]
bb8 -> bb18 [label="T"]
}

16
test-output/ifMethod.dot Normal file
View file

@ -0,0 +1,16 @@
digraph ifMethod {
label="ifMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_0 |3: ISTORE 2|4: // label|5: // line number information|6: ILOAD 1|7: IFGE 12}"]
bb8 [label="8|{8: // label|9: // line number information|10: ICONST_1 |11: ISTORE 2}"]
bb12 [label="12|{12: // label|13: // line number information|14: // stack frame map|15: ILOAD 2|16: IRETURN }"]
e -> bb0
bb0 -> bb8
bb8 -> bb12
bb12 -> x
bb0 -> bb12 [label="T"]
}

View file

@ -0,0 +1,11 @@
digraph instanceCallMethod {
label="instanceCallMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ALOAD 1|3: INVOKEVIRTUAL ExampleClass.instanceCallTarget ()V|4: // label|5: // line number information|6: ICONST_2 |7: IRETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph instanceCallTarget {
label="instanceCallTarget";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: RETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph interfaceCallMethod {
label="interfaceCallMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ALOAD 1|3: INVOKEINTERFACE ExampleClass$Interface.interfaceCallTarget ()V|4: // label|5: // line number information|6: ICONST_2 |7: IRETURN }"]
e -> bb0
bb0 -> x
}

25
test-output/nestedFor.dot Normal file
View file

@ -0,0 +1,25 @@
digraph nestedFor {
label="nestedFor";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_0 |3: ISTORE 2|4: // label|5: // line number information|6: ICONST_0 |7: ISTORE 3}"]
bb8 [label="8|{8: // label|9: // stack frame map|10: ILOAD 3|11: ILOAD 1|12: IF_ICMPGE 37}"]
bb13 [label="13|{13: // label|14: // line number information|15: ICONST_0 |16: ISTORE 4}"]
bb17 [label="17|{17: // label|18: // stack frame map|19: ILOAD 4|20: ILOAD 3|21: IF_ICMPGE 32}"]
bb22 [label="22|{22: // label|23: // line number information|24: ILOAD 2|25: ILOAD 4|26: IADD |27: ISTORE 2|28: // label|29: // line number information|30: IINC 4 1|31: GOTO 17}"]
bb32 [label="32|{32: // label|33: // line number information|34: // stack frame map|35: IINC 3 1|36: GOTO 8}"]
bb37 [label="37|{37: // label|38: // line number information|39: // stack frame map|40: ILOAD 2|41: IRETURN }"]
e -> bb0
bb0 -> bb8
bb8 -> bb13
bb13 -> bb17
bb17 -> bb22
bb37 -> x
bb32 -> bb8 [label="T"]
bb22 -> bb17 [label="T"]
bb17 -> bb32 [label="T"]
bb8 -> bb37 [label="T"]
}

View file

@ -0,0 +1,31 @@
digraph nonShortCircuitMethod {
label="nonShortCircuitMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ILOAD 1|3: ILOAD 2|4: IF_ICMPLE 7}"]
bb5 [label="5|{5: ICONST_1 |6: GOTO 10}"]
bb7 [label="7|{7: // label|8: // stack frame map|9: ICONST_0 }"]
bb10 [label="10|{10: // label|11: // stack frame map|12: ILOAD 1|13: ILOAD 3|14: IF_ICMPGE 17}"]
bb15 [label="15|{15: ICONST_1 |16: GOTO 20}"]
bb17 [label="17|{17: // label|18: // stack frame map|19: ICONST_0 }"]
bb20 [label="20|{20: // label|21: // stack frame map|22: IAND |23: IFEQ 28}"]
bb24 [label="24|{24: // label|25: // line number information|26: ICONST_1 |27: IRETURN }"]
bb28 [label="28|{28: // label|29: // line number information|30: // stack frame map|31: ICONST_0 |32: IRETURN }"]
e -> bb0
bb0 -> bb5
bb7 -> bb10
bb10 -> bb15
bb17 -> bb20
bb20 -> bb24
bb24 -> x
bb24 -> bb28
bb28 -> x
bb0 -> bb7 [label="T"]
bb5 -> bb10 [label="T"]
bb10 -> bb17 [label="T"]
bb15 -> bb20 [label="T"]
bb20 -> bb28 [label="T"]
}

View file

@ -0,0 +1,11 @@
digraph privateInstanceCallMethod {
label="privateInstanceCallMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ALOAD 1|3: INVOKESPECIAL ExampleClass.privateInstanceCallTarget ()V|4: // label|5: // line number information|6: ICONST_2 |7: IRETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph privateInstanceCallTarget {
label="privateInstanceCallTarget";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: RETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,20 @@
digraph shortCircuitMethod {
label="shortCircuitMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ILOAD 1|3: ILOAD 2|4: IF_ICMPLE 12}"]
bb5 [label="5|{5: ILOAD 1|6: ILOAD 3|7: IF_ICMPGE 12}"]
bb8 [label="8|{8: // label|9: // line number information|10: ICONST_1 |11: IRETURN }"]
bb12 [label="12|{12: // label|13: // line number information|14: // stack frame map|15: ICONST_0 |16: IRETURN }"]
e -> bb0
bb0 -> bb5
bb5 -> bb8
bb8 -> x
bb8 -> bb12
bb12 -> x
bb0 -> bb12 [label="T"]
bb5 -> bb12 [label="T"]
}

View file

@ -0,0 +1,11 @@
digraph staticCallMethod {
label="staticCallMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: INVOKESTATIC ExampleClass.staticCallTarget ()V|3: // label|4: // line number information|5: ICONST_2 |6: IRETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph staticCallTarget {
label="staticCallTarget";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: RETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph staticFieldReadMethod {
label="staticFieldReadMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: GETSTATIC ExampleClass.staticField Ljava/lang/String;|3: ARETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,11 @@
digraph staticFieldWriteMethod {
label="staticFieldWriteMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ALOAD 1|3: PUTSTATIC ExampleClass.staticField Ljava/lang/String;|4: // label|5: // line number information|6: RETURN }"]
e -> bb0
bb0 -> x
}

View file

@ -0,0 +1,24 @@
digraph switchMethod {
label="switchMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_0 |3: ISTORE 2|4: // label|5: // line number information|6: ILOAD 1|7: TABLESWITCH 0: 8, 1: 14, 2: 20, default: 26}"]
bb8 [label="8|{8: // label|9: // line number information|10: // stack frame map|11: ICONST_0 |12: ISTORE 2|13: GOTO 31}"]
bb14 [label="14|{14: // label|15: // line number information|16: // stack frame map|17: ICONST_1 |18: ISTORE 2|19: GOTO 31}"]
bb20 [label="20|{20: // label|21: // line number information|22: // stack frame map|23: ICONST_2 |24: ISTORE 2|25: GOTO 31}"]
bb26 [label="26|{26: // label|27: // line number information|28: // stack frame map|29: ICONST_M1 |30: ISTORE 2}"]
bb31 [label="31|{31: // label|32: // line number information|33: // stack frame map|34: ILOAD 2|35: IRETURN }"]
e -> bb0
bb26 -> bb31
bb31 -> x
bb0 -> bb8 [label="0"]
bb0 -> bb14 [label="1"]
bb0 -> bb20 [label="2"]
bb0 -> bb26 [label="default"]
bb8 -> bb31 [label="T"]
bb14 -> bb31 [label="T"]
bb20 -> bb31 [label="T"]
}

View file

@ -0,0 +1,24 @@
digraph switchMethod2 {
label="switchMethod2";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_0 |3: ISTORE 2|4: // label|5: // line number information|6: ILOAD 1|7: LOOKUPSWITCH 0: 8, 1000: 14, 2000: 20, default: 26}"]
bb8 [label="8|{8: // label|9: // line number information|10: // stack frame map|11: ICONST_0 |12: ISTORE 2|13: GOTO 31}"]
bb14 [label="14|{14: // label|15: // line number information|16: // stack frame map|17: ICONST_1 |18: ISTORE 2|19: GOTO 31}"]
bb20 [label="20|{20: // label|21: // line number information|22: // stack frame map|23: ICONST_2 |24: ISTORE 2|25: GOTO 31}"]
bb26 [label="26|{26: // label|27: // line number information|28: // stack frame map|29: ICONST_M1 |30: ISTORE 2}"]
bb31 [label="31|{31: // label|32: // line number information|33: // stack frame map|34: ILOAD 2|35: IRETURN }"]
e -> bb0
bb26 -> bb31
bb31 -> x
bb0 -> bb8 [label="0"]
bb0 -> bb14 [label="1000"]
bb0 -> bb20 [label="2000"]
bb0 -> bb26 [label="default"]
bb8 -> bb31 [label="T"]
bb14 -> bb31 [label="T"]
bb20 -> bb31 [label="T"]
}

View file

@ -0,0 +1,18 @@
digraph whileMethod {
label="whileMethod";
node [shape=record]
e [shape=circle,label="e"]
x [shape=circle,label="x"]
bb0 [label="0|{0: // label|1: // line number information|2: ICONST_0 |3: ISTORE 2}"]
bb4 [label="4|{4: // label|5: // line number information|6: // stack frame map|7: ILOAD 1|8: IFLE 19}"]
bb9 [label="9|{9: // label|10: // line number information|11: ILOAD 2|12: ILOAD 1|13: IADD |14: ISTORE 2|15: // label|16: // line number information|17: IINC 1 -1|18: GOTO 4}"]
bb19 [label="19|{19: // label|20: // line number information|21: // stack frame map|22: ILOAD 2|23: IRETURN }"]
e -> bb0
bb0 -> bb4
bb4 -> bb9
bb19 -> x
bb9 -> bb4 [label="T"]
bb4 -> bb19 [label="T"]
}

View file

@ -0,0 +1,11 @@
digraph whileTrueMethod {
label="whileTrueMethod";
node [shape=record]
e [shape=circle,label="e"]
e [shape=circle,label="e"]
bb0 [label="0|{0: // label|1: // line number information|2: // stack frame map|3: IINC 1 1|4: GOTO 0}"]
e -> bb0
bb0 -> bb0 [label="T"]
}