dtoNames){
UnitsInfoRecorder.registerSpecifiedDtoSchema(ExtractJvmClass.extractAsSchema(dtoNames));
}
+ public static DynamosaControlDependenceSnapshot getControlDependenceSnapshot(int fromIndex){
+ return GraphPool.exportSnapshotFromIndex(fromIndex);
+ }
+
+ public static void setDynamosaGraphsEnabled(boolean enableGraphs) {
+ DynamosaConfig.setEnableGraphs(enableGraphs);
+ }
+
+ public static void setWriteCfgEnabled(boolean writeCfg) {
+ DynamosaConfig.setWriteCfgEnabled(writeCfg);
+ }
+
}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/Instrumentator.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/Instrumentator.java
index 2853537d93..889d75bf02 100644
--- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/Instrumentator.java
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/Instrumentator.java
@@ -7,6 +7,8 @@
import org.evomaster.client.java.instrumentation.shared.ClassName;
import org.evomaster.client.java.instrumentation.staticstate.UnitsInfoRecorder;
import org.evomaster.client.java.utils.SimpleLogger;
+import org.evomaster.client.java.instrumentation.dynamosa.DynamosaConfig;
+import org.evomaster.client.java.instrumentation.dynamosa.visitor.CFGClassVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
@@ -65,11 +67,15 @@ public byte[] transformBytes(ClassLoader classLoader, ClassName className, Class
ClassNode cn = new ClassNode();
reader.accept(cn, readFlags);
- if(canInstrumentForCoverage(className)){
+ boolean canCollectCoverage = canInstrumentForCoverage(className);
+ if(canCollectCoverage){
+ if (DynamosaConfig.isGraphsEnabled()){
+ cv = new CFGClassVisitor(classLoader, cv);
+ }
cv = new CoverageClassVisitor(cv, className);
} else {
cv = new ThirdPartyClassVisitor(cv, className);
- }
+ }
try {
cn.accept(cv);
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedLabel.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedLabel.java
new file mode 100755
index 0000000000..fdfa1cc9cd
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedLabel.java
@@ -0,0 +1,57 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.tree.LabelNode;
+
+/**
+ * Annotated labels are used to identify instrumented code
+ */
+public class AnnotatedLabel extends Label {
+
+ private boolean isStart = false;
+
+ private boolean ignore = false;
+
+ private boolean ignoreFalse = false;
+
+ private LabelNode parent = null;
+
+ public AnnotatedLabel(boolean ignore, boolean start) {
+ this.ignore = ignore;
+ this.isStart = start;
+ }
+
+ public AnnotatedLabel(boolean ignore, boolean start, LabelNode parent) {
+ this.ignore = ignore;
+ this.isStart = start;
+ this.parent = parent;
+ }
+
+ public boolean isStartTag() {
+ return isStart;
+ }
+
+ public boolean shouldIgnore() {
+ return ignore;
+ }
+
+ public void setIgnoreFalse(boolean value) {
+ ignoreFalse = value;
+ }
+
+ public boolean shouldIgnoreFalse() {
+ return ignoreFalse;
+ }
+
+ public LabelNode getParent() {
+ return parent;
+ }
+
+ public void setParent(LabelNode parent) {
+ this.parent = parent;
+ }
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedMethodNode.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedMethodNode.java
new file mode 100755
index 0000000000..e04dde9f3d
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedMethodNode.java
@@ -0,0 +1,52 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * AnnotatedMethodNode wraps ASM's MethodNode to support Dynamosa-specific metadata.
+ */
+public class AnnotatedMethodNode extends MethodNode {
+
+ /**
+ * Constructor for AnnotatedMethodNode.
+ *
+ * @param access a int.
+ * @param name a {@link java.lang.String} object.
+ * @param desc a {@link java.lang.String} object.
+ * @param signature a {@link java.lang.String} object.
+ * @param exceptions an array of {@link java.lang.String} objects.
+ */
+ public AnnotatedMethodNode(int access, String name, String desc, String signature,
+ String[] exceptions) {
+ super(Opcodes.ASM9, access, name, desc, signature, exceptions);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Returns the LabelNode corresponding to the given Label. Creates a new
+ * LabelNode if necessary. The default implementation of this method uses
+ * the {@link Label#info} field to store associations between labels and
+ * label nodes.
+ */
+ @Override
+ protected LabelNode getLabelNode(final Label l) {
+ if (l instanceof AnnotatedLabel) {
+ AnnotatedLabel al = (AnnotatedLabel) l;
+ al.setParent(new LabelNode(al));
+ return al.getParent();
+ } else {
+ if (!(l.info instanceof LabelNode)) {
+ l.info = new LabelNode(l);
+ }
+ return (LabelNode) l.info;
+ }
+ }
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/DynamosaConfig.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/DynamosaConfig.java
new file mode 100644
index 0000000000..1010449e9c
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/DynamosaConfig.java
@@ -0,0 +1,44 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa;
+
+/**
+ * Runtime configuration for DYNAMOSA-related instrumentation features.
+ * Populated via the agent control channel.
+ */
+public class DynamosaConfig {
+
+ private static volatile boolean enableGraphs = false;
+ private static volatile boolean writeCfg = false;
+
+
+ public static boolean isGraphsEnabled() {
+ System.out.println("enableGraphs: " + enableGraphs);
+ return enableGraphs;
+ }
+
+ public static void setEnableGraphs(boolean value) {
+ System.out.println("----------------------------------------------");
+ System.out.println("Setting enableGraphs to " + value);
+ System.out.println("----------------------------------------------");
+ enableGraphs = value;
+ }
+
+ public static boolean isWriteCfgEnabled() {
+ System.out.println("----------------------------------------------");
+ System.out.println("writeCfg: " + writeCfg);
+ System.out.println("----------------------------------------------");
+ return writeCfg;
+ }
+
+ public static void setWriteCfgEnabled(boolean value) {
+ System.out.println("----------------------------------------------");
+ System.out.println("Setting writeCfg to " + value);
+ System.out.println("----------------------------------------------");
+ writeCfg = value;
+ }
+}
+
+
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoMasterGraph.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoMasterGraph.java
new file mode 100755
index 0000000000..92be370ade
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoMasterGraph.java
@@ -0,0 +1,815 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs;
+
+import org.jgrapht.DirectedGraph;
+import org.jgrapht.alg.DijkstraShortestPath;
+import org.jgrapht.ext.*;
+import org.jgrapht.graph.DefaultDirectedGraph;
+import org.jgrapht.graph.DefaultEdge;
+import org.evomaster.client.java.utils.SimpleLogger;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.*;
+
+import static java.util.stream.Collectors.toCollection;
+
+public abstract class EvoMasterGraph {
+
+ private static int evoSuiteGraphs = 0;
+ protected int graphId;
+
+ protected DirectedGraph graph;
+ protected Class edgeClass;
+
+ ComponentAttributeProvider vertexAttributeProvider = null;
+ ComponentAttributeProvider edgeAttributeProvider = null;
+
+ protected EvoMasterGraph(Class edgeClass) {
+
+ graph = new DefaultDirectedGraph<>(edgeClass);
+ this.edgeClass = edgeClass;
+
+ setId();
+ }
+
+ protected EvoMasterGraph(DirectedGraph graph, Class edgeClass) {
+ if (graph == null || edgeClass == null)
+ throw new IllegalArgumentException("null given");
+
+ this.graph = graph;
+ this.edgeClass = edgeClass;
+
+ setId();
+ }
+
+ private void setId() {
+ evoSuiteGraphs++;
+ graphId = evoSuiteGraphs;
+ }
+
+ // retrieving nodes and edges
+
+ /**
+ * getEdgeSource
+ *
+ * @param e a E object.
+ * @return a V object.
+ */
+ public V getEdgeSource(E e) {
+ if (!containsEdge(e))
+ throw new IllegalArgumentException("edge not in graph");
+
+ return graph.getEdgeSource(e);
+ }
+
+ /**
+ * getEdgeTarget
+ *
+ * @param e a E object.
+ * @return a V object.
+ */
+ public V getEdgeTarget(E e) {
+ if (!containsEdge(e))
+ throw new IllegalArgumentException("edge not in graph");
+
+ return graph.getEdgeTarget(e);
+ }
+
+ /**
+ * outgoingEdgesOf
+ *
+ * @param node a V object.
+ * @return a {@link java.util.Set} object.
+ */
+ public Set outgoingEdgesOf(V node) {
+ if (!containsVertex(node)) // should this just return null?
+ throw new IllegalArgumentException(
+ "node not contained in this graph");
+ // TODO hash set? can't be sure E implements hash correctly
+ return new LinkedHashSet<>(graph.outgoingEdgesOf(node));
+ }
+
+ /**
+ * incomingEdgesOf
+ *
+ * @param node a V object.
+ * @return a {@link java.util.Set} object.
+ */
+ public Set incomingEdgesOf(V node) {
+ if (!containsVertex(node)) // should this just return null?
+ throw new IllegalArgumentException("node not contained in this graph ");
+ // TODO hash set? can't be sure E implements hash correctly
+ return new LinkedHashSet<>(graph.incomingEdgesOf(node));
+ }
+
+ /**
+ * getChildren
+ *
+ * @param node a V object.
+ * @return a {@link java.util.Set} object.
+ */
+ public Set getChildren(V node) {
+ if (!containsVertex(node)) {
+ SimpleLogger.warn("getChildren call requests a node not contained in the current graph. Node: " + node);
+ return null;
+ }
+ //TODO check why in project 57_hft-bomberman class client.gui.StartFrame this happens
+ // throw new IllegalArgumentException(
+ // "node not contained in this graph");
+ // TODO hash set? can't be sure V implements hash correctly
+ Set r = outgoingEdgesOf(node).stream()
+ .map(this::getEdgeTarget)
+ .collect(toCollection(LinkedHashSet::new));
+
+ // sanity check
+ if (r.size() != outDegreeOf(node))
+ throw new IllegalStateException(
+ "expect children count and size of set of all children of a graphs node to be equal");
+
+ return r;
+ }
+
+ /**
+ * getParents
+ *
+ * @param node a V object.
+ * @return a {@link java.util.Set} object.
+ */
+ public Set getParents(V node) {
+ if (!containsVertex(node)) // should this just return null?
+ throw new IllegalArgumentException(
+ "node not contained in this graph");
+ // TODO hash set? can't be sure V implements hash correctly
+ Set r = incomingEdgesOf(node).stream()
+ .map(this::getEdgeSource)
+ .collect(toCollection(LinkedHashSet::new));
+
+ // sanity check
+ if (r.size() != inDegreeOf(node))
+ throw new IllegalStateException(
+ "expect parent count and size of set of all parents of a graphs node to be equal");
+
+ return r;
+ }
+
+ /**
+ * vertexSet
+ *
+ * @return a {@link java.util.Set} object.
+ */
+ public Set vertexSet() {
+ // TODO hash set? can't be sure V implements hash correctly
+ return new LinkedHashSet<>(graph.vertexSet());
+ /*
+ * Set r = new HashSet();
+ *
+ * for (V v : graph.vertexSet()) r.add(v);
+ *
+ * return r;
+ */
+ }
+
+ /**
+ * edgeSet
+ *
+ * @return a {@link java.util.Set} object.
+ */
+ public Set edgeSet() {
+ // TODO hash set? can't be sure E implements hash correctly
+ return new LinkedHashSet<>(graph.edgeSet());
+
+ /*
+ * Set r = new HashSet();
+ *
+ * for (E e : graph.edgeSet()) r.add(e);
+ *
+ * return r;
+ */
+ }
+
+ /**
+ * If the given node is contained within this graph and has exactly one
+ * child v this method will return v. Otherwise it will return null
+ *
+ * @param node a V object.
+ * @return a V object.
+ */
+ public V getSingleChild(V node) {
+ if (node == null)
+ return null;
+ if (!graph.containsVertex(node))
+ return null;
+ if (outDegreeOf(node) != 1)
+ return null;
+
+ for (V r : getChildren(node))
+ return r;
+ // should be unreachable
+ return null;
+ }
+
+ // building the graph
+
+ protected void addVertices(EvoMasterGraph other) {
+
+ addVertices(other.vertexSet());
+ }
+
+ /**
+ * addVertices
+ *
+ * @param vs a {@link java.util.Collection} object.
+ */
+ protected void addVertices(Collection vs) {
+ if (vs == null)
+ throw new IllegalArgumentException("null given");
+ for (V v : vs)
+ if (!addVertex(v))
+ throw new IllegalArgumentException(
+ "unable to add all nodes in given collection: "
+ + v.toString());
+
+ }
+
+ /**
+ * addVertex
+ *
+ * @param v a V object.
+ * @return a boolean.
+ */
+ protected boolean addVertex(V v) {
+ return graph.addVertex(v);
+ }
+
+ /**
+ * addEdge
+ *
+ * @param src a V object.
+ * @param target a V object.
+ * @return a E object.
+ */
+ protected E addEdge(V src, V target) {
+
+ return graph.addEdge(src, target);
+ }
+
+ /**
+ * addEdge
+ *
+ * @param src a V object.
+ * @param target a V object.
+ * @param e a E object.
+ * @return a boolean.
+ */
+ protected boolean addEdge(V src, V target, E e) {
+
+ return graph.addEdge(src, target, e);
+ }
+
+ /**
+ * Redirects all edges going into node from to the node newStart and all
+ * edges going out of node from to the node newEnd.
+ *
+ * All three edges have to be present in the graph prior to a call to this
+ * method.
+ *
+ * @param from a V object.
+ * @param newStart a V object.
+ * @param newEnd a V object.
+ * @return a boolean.
+ */
+ protected boolean redirectEdges(V from, V newStart, V newEnd) {
+ if (!(containsVertex(from) && containsVertex(newStart) && containsVertex(newEnd)))
+ throw new IllegalArgumentException(
+ "expect all given nodes to be present in this graph");
+
+ if (!redirectIncomingEdges(from, newStart))
+ return false;
+
+ return redirectOutgoingEdges(from, newEnd);
+
+ }
+
+ /**
+ * Redirects all incoming edges to oldNode to node newNode by calling
+ * redirectEdgeTarget for each incoming edge of oldNode
+ *
+ * @param oldNode a V object.
+ * @param newNode a V object.
+ * @return a boolean.
+ */
+ protected boolean redirectIncomingEdges(V oldNode, V newNode) {
+ return incomingEdgesOf(oldNode).stream()
+ .allMatch(incomingEdge -> redirectEdgeTarget(incomingEdge, newNode));
+ }
+
+ /**
+ * Redirects all outgoing edges to oldNode to node newNode by calling
+ * redirectEdgeSource for each outgoing edge of oldNode
+ *
+ * @param oldNode a V object.
+ * @param newNode a V object.
+ * @return a boolean.
+ */
+ protected boolean redirectOutgoingEdges(V oldNode, V newNode) {
+ return outgoingEdgesOf(oldNode).stream()
+ .allMatch(outgoingEdge -> redirectEdgeSource(outgoingEdge, newNode));
+ }
+
+ /**
+ * Redirects the edge target of the given edge to the given node by removing
+ * the given edge from the graph and reinserting it from the original source
+ * node to the given node
+ *
+ * @param edge a E object.
+ * @param node a V object.
+ * @return a boolean.
+ */
+ protected boolean redirectEdgeTarget(E edge, V node) {
+ if (!(containsVertex(node) && containsEdge(edge)))
+ throw new IllegalArgumentException(
+ "edge and node must be present in this graph");
+
+ V edgeSource = graph.getEdgeSource(edge);
+ if (!graph.removeEdge(edge))
+ return false;
+
+ return addEdge(edgeSource, node, edge);
+ }
+
+ /**
+ * Redirects the edge source of the given edge to the given node by removing
+ * the given edge from the graph and reinserting it from the given node to
+ * the original target node
+ *
+ * @param edge a E object.
+ * @param node a V object.
+ * @return a boolean.
+ */
+ protected boolean redirectEdgeSource(E edge, V node) {
+ if (!(containsVertex(node) && containsEdge(edge)))
+ throw new IllegalArgumentException(
+ "edge and node must be present in this graph");
+
+ V edgeTarget = graph.getEdgeTarget(edge);
+ if (!graph.removeEdge(edge))
+ return false;
+
+ return addEdge(node, edgeTarget, edge);
+ }
+
+ // different counts
+
+ /**
+ *
vertexCount
+ *
+ * @return a int.
+ */
+ public int vertexCount() {
+ return graph.vertexSet().size();
+ }
+
+ /**
+ * edgeCount
+ *
+ * @return a int.
+ */
+ public int edgeCount() {
+ return graph.edgeSet().size();
+ }
+
+ /**
+ * outDegreeOf
+ *
+ * @param node a V object.
+ * @return a int.
+ */
+ public int outDegreeOf(V node) { // TODO rename to sth. like childCount()
+ if (node == null || !containsVertex(node))
+ return -1;
+
+ return graph.outDegreeOf(node);
+ }
+
+ /**
+ * inDegreeOf
+ *
+ * @param node a V object.
+ * @return a int.
+ */
+ public int inDegreeOf(V node) { // TODO rename sth. like parentCount()
+ if (node == null || !containsVertex(node))
+ return -1;
+
+ return graph.inDegreeOf(node);
+ }
+
+ // some queries
+
+ /**
+ * getEdge
+ *
+ * @param v1 a V object.
+ * @param v2 a V object.
+ * @return a E object.
+ */
+ public E getEdge(V v1, V v2) {
+ return graph.getEdge(v1, v2);
+ }
+
+ /**
+ * containsVertex
+ *
+ * @param v a V object.
+ * @return a boolean.
+ */
+ public boolean containsVertex(V v) {
+ // documentation says containsVertex() returns false on when given null
+ return graph.containsVertex(v);
+ }
+
+ /**
+ * containsEdge
+ *
+ * @param v1 a V object.
+ * @param v2 a V object.
+ * @return a boolean.
+ */
+ public boolean containsEdge(V v1, V v2) {
+ return graph.containsEdge(v1, v2);
+ }
+
+ /**
+ * containsEdge
+ *
+ * @param e a E object.
+ * @return a boolean.
+ */
+ public boolean containsEdge(E e) {
+ return graph.containsEdge(e); // TODO this seems to be buggy, at least
+ // for ControlFlowEdges
+ }
+
+ /**
+ * isEmpty
+ *
+ * @return a boolean.
+ */
+ public boolean isEmpty() {
+ return graph.vertexSet().isEmpty();
+ }
+
+ /**
+ * Checks whether each vertex inside this graph is reachable from some other
+ * vertex
+ *
+ * @return a boolean.
+ */
+ public boolean isConnected() {
+ if (vertexCount() < 2)
+ return true;
+
+ V start = getRandomVertex();
+ Set connectedToStart = determineConnectedVertices(start);
+
+ return connectedToStart.size() == vertexSet().size();
+ }
+
+ /**
+ * determineEntryPoints
+ *
+ * @return Set containing all nodes with in degree 0
+ */
+ public Set determineEntryPoints() {
+ Set r = new LinkedHashSet<>();
+
+ for (V instruction : vertexSet())
+ if (inDegreeOf(instruction) == 0) {
+ r.add(instruction);
+ }
+
+ return r;
+ }
+
+ /**
+ * determineExitPoints
+ *
+ * @return Set containing all nodes with out degree 0
+ */
+ public Set determineExitPoints() {
+ Set r = new LinkedHashSet<>();
+
+ for (V instruction : vertexSet())
+ if (outDegreeOf(instruction) == 0)
+ r.add(instruction);
+
+ return r;
+ }
+
+ /**
+ * Follows all edges adjacent to the given vertex v ignoring edge directions
+ * and returns a set containing all vertices visited that way
+ *
+ * @param v a V object.
+ * @return a {@link java.util.Set} object.
+ */
+ public Set determineConnectedVertices(V v) {
+
+ Set visited = new LinkedHashSet<>();
+ Queue queue = new LinkedList<>();
+
+ queue.add(v);
+ while (!queue.isEmpty()) {
+ V current = queue.poll();
+ if (visited.contains(current))
+ continue;
+ visited.add(current);
+
+ queue.addAll(getParents(current));
+ queue.addAll(getChildren(current));
+ }
+
+ return visited;
+ }
+
+ /**
+ * Returns true iff whether the given node is not null, in this graph and
+ * has exactly n parents and m children.
+ *
+ * @param node a V object.
+ * @param n a int.
+ * @param m a int.
+ * @return a boolean.
+ */
+ public boolean hasNPartentsMChildren(V node, int n, int m) {
+ if (node == null || !containsVertex(node))
+ return false;
+
+ return inDegreeOf(node) == n && outDegreeOf(node) == m;
+ }
+
+ /**
+ * Returns a Set of all nodes within this graph that neither have incoming
+ * nor outgoing edges.
+ *
+ * @return a {@link java.util.Set} object.
+ */
+ public Set getIsolatedNodes() {
+ Set r = new LinkedHashSet<>();
+ for (V node : graph.vertexSet())
+ if (inDegreeOf(node) == 0 && outDegreeOf(node) == 0)
+ r.add(node);
+ return r;
+ }
+
+ /**
+ * Returns a Set containing every node in this graph that has no outgoing
+ * edges.
+ *
+ * @return a {@link java.util.Set} object.
+ */
+ public Set getNodesWithoutChildren() {
+ Set r = new LinkedHashSet<>();
+ for (V node : graph.vertexSet())
+ if (outDegreeOf(node) == 0)
+ r.add(node);
+ return r;
+ }
+
+ // utilities
+
+ /**
+ * getRandomVertex
+ *
+ * @return a V object.
+ */
+ public V getRandomVertex() {
+ // TODO that's not really random
+ for (V v : graph.vertexSet())
+ return v;
+
+ return null;
+ }
+
+ /**
+ * getDistance
+ *
+ * @param v1 a V object.
+ * @param v2 a V object.
+ * @return a int.
+ */
+ public int getDistance(V v1, V v2) {
+ DijkstraShortestPath d = new DijkstraShortestPath<>(graph, v1, v2);
+ return (int) Math.round(d.getPathLength());
+ }
+
+ /**
+ * isDirectSuccessor
+ *
+ * @param v1 a V object.
+ * @param v2 a V object.
+ * @return a boolean.
+ */
+ public boolean isDirectSuccessor(V v1, V v2) {
+
+ return (containsEdge(v1, v2) && inDegreeOf(v2) == 1);
+ }
+
+ // TODO make like determineEntry/ExitPoints
+
+ /**
+ * determineBranches
+ *
+ * @return a {@link java.util.Set} object.
+ */
+ public Set determineBranches() {
+ return graph.vertexSet().stream()
+ .filter(instruction -> outDegreeOf(instruction) > 1)
+ .collect(toCollection(LinkedHashSet::new));
+ }
+
+ /**
+ * determineJoins
+ *
+ * @return a {@link java.util.Set} object.
+ */
+ public Set determineJoins() {
+ return vertexSet().stream()
+ .filter(instruction -> inDegreeOf(instruction) > 1)
+ .collect(toCollection(LinkedHashSet::new));
+ }
+
+ // building up the reverse graph
+
+ /**
+ * Returns a reverted version of this graph in a jGraph
+ *
+ * That is a graph containing exactly the same nodes as this one but for
+ * each edge from v1 to v2 in this graph the resulting graph will contain an
+ * edge from v2 to v1 - or in other words the reverted edge
+ *
+ * This is used to revert CFGs in order to determine control dependencies
+ * for example
+ *
+ * @return a {@link org.jgrapht.graph.DefaultDirectedGraph} object.
+ */
+ protected DefaultDirectedGraph computeReverseJGraph() {
+
+ DefaultDirectedGraph r = new DefaultDirectedGraph<>(edgeClass);
+
+ for (V v : vertexSet())
+ if (!r.addVertex(v))
+ throw new IllegalStateException(
+ "internal error while adding vertices");
+
+ for (E e : edgeSet()) {
+ V src = getEdgeSource(e);
+ V target = getEdgeTarget(e);
+ if (r.addEdge(target, src) == null)
+ throw new IllegalStateException(
+ "internal error while adding reverse edges");
+ }
+
+ return r;
+ }
+
+ // visualizing the graph TODO clean up!
+
+ /**
+ * toDot
+ */
+ public void toDot() {
+
+ createGraphDirectory();
+
+ String dotFileName = getGraphDirectory() + toFileString(getName())
+ + ".dot";
+ toDot(dotFileName);
+ createToPNGScript(dotFileName);
+ }
+
+ private String getGraphDirectory() {
+ return "evosuite-graphs/" + dotSubFolder();
+ }
+
+ /**
+ * Subclasses can overwrite this method in order to separate their .dot and
+ * .png export to a special folder.
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ protected String dotSubFolder() {
+ return "";
+ }
+
+ /**
+ * toFileString
+ *
+ * @param name a {@link java.lang.String} object.
+ * @return a {@link java.lang.String} object.
+ */
+ protected String toFileString(String name) {
+
+ return name.replaceAll("\\(", "_").replaceAll("\\)", "_")
+ .replaceAll(";", "_").replaceAll("/", "_").replaceAll("<", "_")
+ .replaceAll(">", "_");
+ }
+
+ private void createGraphDirectory() {
+
+ File graphDir = new File(getGraphDirectory());
+
+ if (!graphDir.exists() && !graphDir.mkdirs())
+ throw new IllegalStateException("unable to create directory "
+ + getGraphDirectory());
+ }
+
+ private void createToPNGScript(String filename) {
+ File dotFile = new File(filename);
+
+ // dot -Tpng RawCFG11_exe2_III_I.dot > file.png
+ assert (dotFile.exists() && !dotFile.isDirectory());
+
+ try {
+ String[] cmd = {"dot", "-Tpng",
+ "-o" + dotFile.getAbsolutePath() + ".png",
+ dotFile.getAbsolutePath()};
+ Runtime.getRuntime().exec(cmd);
+
+ } catch (IOException e) {
+ SimpleLogger.error("Problem while generating a graph for a dotFile", e);
+ }
+ }
+
+ /**
+ * getName
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getName() {
+ return "EvoMasterGraph_" + graphId;
+ }
+
+ /**
+ * registerVertexAttributeProvider
+ *
+ * @param vertexAttributeProvider a {@link org.jgrapht.ext.ComponentAttributeProvider} object.
+ */
+ public void registerVertexAttributeProvider(
+ ComponentAttributeProvider vertexAttributeProvider) {
+ this.vertexAttributeProvider = vertexAttributeProvider;
+ }
+
+ /**
+ * registerEdgeAttributeProvider
+ *
+ * @param edgeAttributeProvider a {@link org.jgrapht.ext.ComponentAttributeProvider} object.
+ */
+ public void registerEdgeAttributeProvider(
+ ComponentAttributeProvider edgeAttributeProvider) {
+ this.edgeAttributeProvider = edgeAttributeProvider;
+ }
+
+ private void toDot(String filename) {
+
+ // TODO check if graphviz/dot is actually available on the current
+ // machine
+
+ try {
+
+ FileWriter fstream = new FileWriter(filename);
+ BufferedWriter out = new BufferedWriter(fstream);
+ if (!graph.vertexSet().isEmpty()) {
+ // FrameVertexNameProvider nameprovider = new
+ // FrameVertexNameProvider(mn.instructions);
+ // DOTExporter exporter = new
+ // DOTExporter();
+ // DOTExporter exporter = new
+ // DOTExporter(new IntegerNameProvider(),
+ // nameprovider, new IntegerEdgeNameProvider());
+ // DOTExporter exporter = new
+ // DOTExporter(new LineNumberProvider(),
+ // new LineNumberProvider(), new IntegerEdgeNameProvider());
+ DOTExporter exporter = new DOTExporter<>(
+ new IntegerNameProvider<>(),
+ new StringNameProvider<>(),
+ new StringEdgeNameProvider<>(),
+ vertexAttributeProvider, edgeAttributeProvider);
+
+ // new IntegerEdgeNameProvider());
+ exporter.export(out, graph);
+
+ SimpleLogger.info("exportet " + getName());
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/GraphPool.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/GraphPool.java
new file mode 100755
index 0000000000..a0bc407a18
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/GraphPool.java
@@ -0,0 +1,452 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs;
+
+import org.evomaster.client.java.instrumentation.ClassesToExclude;
+import com.google.common.annotations.VisibleForTesting;
+import org.evomaster.client.java.instrumentation.dynamosa.DynamosaConfig;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg.ControlDependenceGraph;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlDependency;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch;
+import org.evomaster.client.java.instrumentation.external.DynamosaControlDependenceSnapshot;
+import org.evomaster.client.java.instrumentation.shared.ClassName;
+import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto;
+import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto.BranchObjectiveDto;
+import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto.DependencyEdgeDto;
+import org.evomaster.client.java.instrumentation.staticstate.ObjectiveRecorder;
+import org.evomaster.client.java.utils.SimpleLogger;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Gives access to all Graphs computed during CUT analysis such as CFGs created
+ * by the CFGGenerator and BytcodeAnalyzer in the CFGMethodVisitor
+ *
+ * For each CUT and each of their methods a Raw- and an ActualControlFlowGraph
+ * instance are stored within this pool. Additionally a ControlDependenceGraph
+ * is computed and stored for each such method.
+ *
+ * This pool stores per-method CFGs and CDGs computed during analysis.
+ *
+ */
+public class GraphPool {
+
+ private static final Map instanceMap = new HashMap<>();
+ private static final List exportedCdgs = new ArrayList<>();
+ private static final Object exportLock = new Object();
+
+ private final ClassLoader classLoader;
+
+ /**
+ * Private constructor
+ */
+ private GraphPool(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ private static boolean isWriteCfgEnabled() {
+ return DynamosaConfig.isWriteCfgEnabled();
+ }
+
+ public static GraphPool getInstance(ClassLoader classLoader) {
+ if (!instanceMap.containsKey(classLoader)) {
+ instanceMap.put(classLoader, new GraphPool(classLoader));
+ }
+
+ return instanceMap.get(classLoader);
+ }
+
+ @VisibleForTesting
+ public static void resetForTesting(ClassLoader classLoader) {
+ instanceMap.remove(classLoader);
+ synchronized (exportLock) {
+ exportedCdgs.clear();
+ }
+ }
+
+ /**
+ * Complete control flow graph, contains each bytecode instruction, each
+ * label and line number node Think of the direct Known Subclasses of
+ * http://
+ * asm.ow2.org/asm33/javadoc/user/org/objectweb/asm/tree/AbstractInsnNode
+ * .html for a complete list of the nodes in this cfg
+ *
+ * Maps from classNames to methodNames to corresponding RawCFGs
+ */
+ private final Map> rawCFGs = new HashMap<>();
+
+ /**
+ * Minimized control flow graph. This graph only contains the first and last
+ * node (usually a LABEL and IRETURN), nodes which create branches (all
+ * jumps/switches except GOTO) and nodes which were mutated.
+ *
+ * Maps from classNames to methodNames to corresponding ActualCFGs
+ */
+ private final Map> actualCFGs = new HashMap<>();
+
+ /**
+ * Control Dependence Graphs for each method.
+ *
+ * Maps from classNames to methodNames to corresponding CDGs
+ */
+ private final Map> controlDependencies = new HashMap<>();
+
+ // retrieve graphs
+
+ /**
+ * Returns the {@link RawControlFlowGraph} of the specified method. To this end, one has to
+ * provide
+ *
+ * - the fully qualified name of the class containing the desired method, and
+ * - a string consisting of the method name concatenated with the corresponding
+ * method descriptor.
+ *
+ *
+ * @param className the fully qualified name of the containing class
+ * @param methodName concatenation of method name and descriptor
+ * @return the raw control flow graph
+ */
+ public RawControlFlowGraph getRawCFG(String className, String methodName) {
+
+ if (rawCFGs.get(className) == null) {
+ SimpleLogger.warn("Class unknown: " + className);
+ SimpleLogger.warn(rawCFGs.keySet().toString());
+ return null;
+ }
+
+ return rawCFGs.get(className).get(methodName);
+ }
+
+ /**
+ *
+ * getActualCFG
+ *
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph} object.
+ */
+ public ActualControlFlowGraph getActualCFG(String className, String methodName) {
+
+ if (actualCFGs.get(className) == null)
+ return null;
+
+ return actualCFGs.get(className).get(methodName);
+ }
+
+ /**
+ *
+ * getCDG
+ *
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg.ControlDependenceGraph} object.
+ */
+ public ControlDependenceGraph getCDG(String className, String methodName) {
+
+ if (controlDependencies.get(className) == null)
+ return null;
+
+ return controlDependencies.get(className).get(methodName);
+ }
+
+ public static DynamosaControlDependenceSnapshot exportSnapshotFromIndex(int fromIndex) {
+ synchronized (exportLock) {
+ int start = Math.max(0, Math.min(fromIndex, exportedCdgs.size()));
+ List slice = new ArrayList<>(exportedCdgs.subList(start, exportedCdgs.size()));
+ return new DynamosaControlDependenceSnapshot(slice, exportedCdgs.size());
+ }
+ }
+
+ // register graphs
+
+ /**
+ *
+ * registerRawCFG
+ *
+ *
+ * @param cfg a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph} object.
+ */
+ public void registerRawCFG(RawControlFlowGraph cfg) {
+ String className = cfg.getClassName();
+ String methodName = cfg.getMethodName();
+
+ if (className == null || methodName == null)
+ throw new IllegalStateException(
+ "expect class and method name of CFGs to be set before entering the GraphPool");
+
+ if (!rawCFGs.containsKey(className)) {
+ rawCFGs.put(className, new HashMap<>());
+ }
+ Map methods = rawCFGs.get(className);
+ SimpleLogger.debug("Added complete CFG for class " + className + " and method "
+ + methodName);
+ methods.put(methodName, cfg);
+
+ if (isWriteCfgEnabled())
+ cfg.toDot();
+ }
+
+ /**
+ *
+ * registerActualCFG
+ *
+ *
+ * @param cfg a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph}
+ * object.
+ */
+ public void registerActualCFG(ActualControlFlowGraph cfg) {
+ String className = cfg.getClassName();
+ String methodName = cfg.getMethodName();
+
+ if (className == null || methodName == null)
+ throw new IllegalStateException(
+ "expect class and method name of CFGs to be set before entering the GraphPool");
+
+ if (!actualCFGs.containsKey(className)) {
+ actualCFGs.put(className, new HashMap<>());
+ // diameters.put(className, new HashMap());
+ }
+ Map methods = actualCFGs.get(className);
+ SimpleLogger.debug("Added CFG for class " + className + " and method " + methodName);
+ methods.put(methodName, cfg);
+
+ if (isWriteCfgEnabled())
+ cfg.toDot();
+
+ if (shouldInstrument(cfg.getClassName(), cfg.getMethodName())) {
+ createAndRegisterControlDependence(cfg);
+ }
+ }
+
+ private boolean shouldInstrument(String className, String methodName) {
+ return ClassesToExclude.checkIfCanInstrument(classLoader, new ClassName(className));
+ }
+
+ private void createAndRegisterControlDependence(ActualControlFlowGraph cfg) {
+
+ ControlDependenceGraph cd = new ControlDependenceGraph(cfg);
+
+ String className = cd.getClassName();
+ String methodName = cd.getMethodName();
+
+ if (className == null || methodName == null)
+ throw new IllegalStateException(
+ "expect class and method name of CFGs to be set before entering the GraphPool");
+
+ if (!controlDependencies.containsKey(className))
+ controlDependencies.put(className,
+ new HashMap<>());
+ Map cds = controlDependencies.get(className);
+
+ cds.put(methodName, cd);
+ if (isWriteCfgEnabled())
+ cd.toDot();
+
+ ControlDependenceGraphDto dto = buildControlDependenceDto(className, methodName, cd);
+ if (dto != null) {
+ appendExportLog(dto);
+ }
+ }
+
+ /**
+ *
+ * clear
+ *
+ */
+ public void clear() {
+ rawCFGs.clear();
+ actualCFGs.clear();
+ controlDependencies.clear();
+ }
+
+ /**
+ *
+ * clear
+ *
+ *
+ * @param className a {@link java.lang.String} object.
+ */
+ public void clear(String className) {
+ rawCFGs.remove(className);
+ actualCFGs.remove(className);
+ controlDependencies.remove(className);
+ }
+
+ /**
+ *
+ * clear
+ *
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ */
+ public void clear(String className, String methodName) {
+ if (rawCFGs.containsKey(className))
+ rawCFGs.get(className).remove(methodName);
+ if (actualCFGs.containsKey(className))
+ actualCFGs.get(className).remove(methodName);
+ if (controlDependencies.containsKey(className))
+ controlDependencies.get(className).remove(methodName);
+ }
+
+ public static void clearAll(String className) {
+ instanceMap.values().forEach(pool -> pool.clear(className));
+ }
+
+ public static void clearAll(String className, String methodName) {
+ instanceMap.values().forEach(pool -> pool.clear(className, methodName));
+ }
+
+ public static void clearAll() {
+ instanceMap.clear();
+ synchronized (exportLock) {
+ exportedCdgs.clear();
+ }
+ }
+
+ public static void refreshAllCdgs() {
+ synchronized (exportLock) {
+ exportedCdgs.clear();
+ }
+ instanceMap.values().forEach(GraphPool::refreshCdgs);
+ }
+
+ private void refreshCdgs() {
+ for (String className : controlDependencies.keySet()) {
+ for (String methodName : controlDependencies.get(className).keySet()) {
+ ControlDependenceGraph cdg = controlDependencies.get(className).get(methodName);
+ ControlDependenceGraphDto dto = buildControlDependenceDto(className, methodName, cdg);
+ if (dto != null) {
+ appendExportLog(dto);
+ }
+ }
+ }
+ }
+
+ private static void appendExportLog(ControlDependenceGraphDto dto) {
+ if (dto == null) {
+ return;
+ }
+ synchronized (exportLock) {
+ exportedCdgs.add(dto);
+ }
+ }
+
+ private ControlDependenceGraphDto buildControlDependenceDto(String className,
+ String methodName,
+ ControlDependenceGraph cdg) {
+ if (cdg == null) {
+ return null;
+ }
+ ActualControlFlowGraph cfg = getActualCFG(className, methodName);
+ if (cfg == null) {
+ SimpleLogger.warn("Cannot export CDG for " + className + "#" + methodName + " as ActualCFG is missing");
+ return null;
+ }
+
+ Map objectiveMap = new LinkedHashMap<>();
+ Set rootObjectives = new LinkedHashSet<>();
+ Map> adjacency = new HashMap<>();
+
+ // Iterate over all branches in the ActualCFG.
+ for (BytecodeInstruction branchInstruction : cfg.getBranches()) {
+ // Branches are stored in the BranchPool as they are instrumented.
+ // Here we retrieve the Branch object from the BranchPool, by its BytecodeInstruction.
+ // Branch also stores the descriptive identifiers for the "true" and "false" outcomes.
+ Branch branch = branchInstruction.toBranch();
+ if (branch == null) {
+ continue;
+ }
+
+
+ List branchObjectives = collectObjectiveIds(branch, objectiveMap);
+ if (branchObjectives.isEmpty()) {
+ continue;
+ }
+
+ Set dependencies = branchInstruction.getControlDependencies();
+ if (dependencies == null || dependencies.isEmpty()) {
+ rootObjectives.addAll(branchObjectives);
+ continue;
+ }
+
+ for (ControlDependency dependency : dependencies) {
+ Branch parent = dependency.getBranch();
+ if (parent == null) {
+ continue;
+ }
+ Integer parentObjective = resolveParentObjectiveId(parent, dependency.getBranchExpressionValue(), objectiveMap);
+ if (parentObjective == null) {
+ continue;
+ }
+
+ adjacency.computeIfAbsent(parentObjective, key -> new LinkedHashSet<>())
+ .addAll(branchObjectives);
+ }
+ }
+
+ if (objectiveMap.isEmpty() && adjacency.isEmpty()) {
+ return null;
+ }
+
+ List edges = new ArrayList<>();
+ adjacency.forEach((parent, childrenIds) -> {
+ for (Integer child : childrenIds) {
+ edges.add(new DependencyEdgeDto(parent, child));
+ }
+ });
+
+ ControlDependenceGraphDto dto = new ControlDependenceGraphDto();
+ dto.setClassName(className);
+ dto.setMethodName(methodName);
+ dto.setObjectives(new ArrayList<>(objectiveMap.values()));
+ dto.setRootObjectiveIds(new ArrayList<>(rootObjectives));
+ dto.setEdges(edges);
+ return dto;
+ }
+
+ private List collectObjectiveIds(Branch branch,
+ Map objectiveMap) {
+ List ids = new ArrayList<>(2);
+ if (branch.getThenObjectiveId() != null) {
+ ids.add(registerObjective(branch.getThenObjectiveId(), objectiveMap));
+ }
+ if (branch.getElseObjectiveId() != null) {
+ ids.add(registerObjective(branch.getElseObjectiveId(), objectiveMap));
+ }
+ return ids;
+ }
+
+ private Integer resolveParentObjectiveId(Branch branch,
+ boolean branchExpressionValue,
+ Map objectiveMap) {
+ String descriptiveId = branchExpressionValue
+ ? branch.getThenObjectiveId()
+ : branch.getElseObjectiveId();
+ if (descriptiveId == null) {
+ return null;
+ }
+ return registerObjective(descriptiveId, objectiveMap);
+ }
+
+ private Integer registerObjective(String descriptiveId,
+ Map objectiveMap) {
+ int id = ObjectiveRecorder.getMappedId(descriptiveId);
+ objectiveMap.computeIfAbsent(id, key -> new BranchObjectiveDto(id, descriptiveId));
+ return id;
+ }
+
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/ControlDependenceGraph.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/ControlDependenceGraph.java
new file mode 100755
index 0000000000..560fcbdb2a
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/ControlDependenceGraph.java
@@ -0,0 +1,219 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.EvoMasterGraph;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.*;
+import org.evomaster.client.java.utils.SimpleLogger;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class ControlDependenceGraph extends EvoMasterGraph {
+
+ private final ActualControlFlowGraph cfg;
+
+ private final String className;
+ private final String methodName;
+
+ /**
+ * Constructor for ControlDependenceGraph.
+ *
+ * @param cfg a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph} object.
+ */
+ public ControlDependenceGraph(ActualControlFlowGraph cfg) {
+ super(ControlFlowEdge.class);
+
+ this.cfg = cfg;
+ this.className = cfg.getClassName();
+ this.methodName = cfg.getMethodName();
+
+ computeGraph();
+ }
+
+
+ /**
+ * Returns a Set containing all Branches the given BasicBlock is control
+ * dependent on.
+ *
+ * This is for each incoming ControlFlowEdge of the given block within this
+ * CDG, the branch instruction of that edge will be added to the returned
+ * set.
+ *
+ * @param insBlock a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object.
+ * @return a {@link java.util.Set} object.
+ */
+ public Set getControlDependentBranches(BasicBlock insBlock) {
+ if (insBlock == null)
+ throw new IllegalArgumentException("null not accepted");
+ if (!containsVertex(insBlock))
+ throw new IllegalArgumentException("unknown block: " + insBlock.getName());
+
+ if (insBlock.hasControlDependenciesSet())
+ return insBlock.getControlDependencies();
+
+ Set r = retrieveControlDependencies(insBlock,
+ new LinkedHashSet<>());
+
+ return r;
+ }
+
+ private Set retrieveControlDependencies(BasicBlock insBlock,
+ Set handled) {
+
+ Set r = new LinkedHashSet<>();
+
+ for (ControlFlowEdge e : incomingEdgesOf(insBlock)) {
+ if (handled.contains(e))
+ continue;
+ handled.add(e);
+
+ ControlDependency cd = e.getControlDependency();
+ if (cd != null)
+ r.add(cd);
+ else {
+ BasicBlock in = getEdgeSource(e);
+ if (!in.equals(insBlock))
+ r.addAll(retrieveControlDependencies(in, handled));
+ }
+
+ }
+
+ return r;
+ }
+
+ // init
+
+ private void computeGraph() {
+
+ createGraphNodes();
+ computeControlDependence();
+ }
+
+ private void createGraphNodes() {
+ // copy CFG nodes
+ addVertices(cfg.vertexSet());
+
+ for (BasicBlock b : vertexSet())
+ if (b.isExitBlock() && !graph.removeVertex(b)) // TODO refactor
+ throw new IllegalStateException("internal error building up CDG");
+
+ }
+
+ private void computeControlDependence() {
+
+ ActualControlFlowGraph rcfg = cfg.computeReverseCFG();
+ DominatorTree dt = new DominatorTree<>(rcfg);
+
+ for (BasicBlock b : rcfg.vertexSet())
+ if (!b.isExitBlock()) {
+
+ SimpleLogger.debug("DFs for: " + b.getName());
+ for (BasicBlock cd : dt.getDominatingFrontiers(b)) {
+ ControlFlowEdge orig = cfg.getEdge(cd, b);
+
+ if (!cd.isEntryBlock() && orig == null) {
+ // in for loops for example it can happen that cd and b
+ // were not directly adjacent to each other in the CFG
+ // but rather there were some intermediate nodes between
+ // them and the needed information is inside one of the
+ // edges
+ // from cd to the first intermediate node. more
+ // precisely cd is expected to be a branch and to have 2
+ // outgoing edges, one for evaluating to true (jumping)
+ // and one for false. one of them can be followed and b
+ // will eventually be reached, the other one can not be
+ // followed in that way.
+
+ SimpleLogger.debug("cd: " + cd);
+ SimpleLogger.debug("b: " + b);
+
+ // TODO this is just for now! unsafe and probably not
+ // even correct!
+ Set candidates = cfg.outgoingEdgesOf(cd);
+ if (candidates.size() < 2)
+ throw new IllegalStateException("unexpected");
+
+ boolean leadToB = false;
+ boolean skip = false;
+
+ for (ControlFlowEdge e : candidates) {
+ if (!e.hasControlDependency()) {
+ skip = true;
+ break;
+ }
+
+ if (cfg.leadsToNode(e, b)) {
+ if (leadToB)
+ orig = null;
+ // throw new
+ // IllegalStateException("unexpected");
+ leadToB = true;
+
+ orig = e;
+ }
+ }
+ if (skip)
+ continue;
+ if (!leadToB)
+ throw new IllegalStateException("unexpected");
+ }
+
+ if (orig == null)
+ SimpleLogger.debug("orig still null!");
+
+ if (!addEdge(cd, b, new ControlFlowEdge(orig)))
+ throw new IllegalStateException(
+ "internal error while adding CD edge");
+
+ SimpleLogger.debug(" " + cd.getName());
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName() {
+ // return "CDG" + graphId + "_" + methodName;
+ return methodName + "_" + "CDG";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected String dotSubFolder() {
+ return toFileString(className) + "/CDG/";
+ }
+
+ /**
+ * Getter for the field className.
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getClassName() {
+ return className;
+ }
+
+ /**
+ * Getter for the field methodName.
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getMethodName() {
+ return methodName;
+ }
+
+ /**
+ * Exposes the underlying {@link ActualControlFlowGraph}
+ *
+ * @return the CFG used to build this CDG.
+ */
+ public ActualControlFlowGraph getCFG() {
+ return cfg;
+ }
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorNode.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorNode.java
new file mode 100755
index 0000000000..8d4ba72c45
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorNode.java
@@ -0,0 +1,99 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This class serves as a convenience data structure within cfg.DominatorTree
+ *
+ * For every node within a CFG for which the immediateDominators are to be
+ * computed this class holds auxiliary information needed during the computation
+ * inside the DominatorTree
+ *
+ * After that computation instances of this class hold the connection between
+ * CFG nodes and their immediateDominators
+ *
+ * Look at cfg.DominatorTree for more detailed information
+ *
+ */
+class DominatorNode {
+
+ final V node;
+ int n = 0;
+
+ // parent of node within spanning tree of DFS inside cfg.DominatorTree
+ DominatorNode parent;
+
+ // computed dominators
+ DominatorNode semiDominator;
+ DominatorNode immediateDominator;
+
+ // auxiliary field needed for dominator computation
+ Set> bucket = new HashSet<>();
+
+ // data structure needed to represented forest produced during cfg.DominatorTree computation
+ DominatorNode ancestor;
+ DominatorNode label;
+
+ DominatorNode(V node) {
+ this.node = node;
+
+ this.label = this;
+ }
+
+ void link(DominatorNode v) {
+ ancestor = v;
+ }
+
+ DominatorNode eval() {
+ if (ancestor == null)
+ return this;
+
+ compress();
+
+ return label;
+ }
+
+ void compress() {
+ if (ancestor == null)
+ throw new IllegalStateException("may only be called when ancestor is set");
+
+ if (ancestor.ancestor != null) {
+ ancestor.compress();
+ if (ancestor.label.semiDominator.n < label.semiDominator.n)
+ label = ancestor.label;
+
+ ancestor = ancestor.ancestor;
+ }
+ }
+
+ DominatorNode getFromBucket() {
+
+ for (DominatorNode r : bucket)
+ return r;
+
+ return null;
+ }
+
+ /**
+ * isRootNode
+ *
+ * @return a boolean.
+ */
+ public boolean isRootNode() {
+ // TODO not that nice :/
+ return n == 1;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "DTNode " + n + " - " + node;
+ }
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorTree.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorTree.java
new file mode 100755
index 0000000000..7e51026f58
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorTree.java
@@ -0,0 +1,294 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.EvoMasterGraph;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlFlowGraph;
+import org.jgrapht.graph.DefaultEdge;
+import org.evomaster.client.java.utils.SimpleLogger;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Given a CFG this class computes the immediateDominators and the
+ * dominatingFrontiers for each CFG vertex
+ *
+ * The current algorithm to determine the immediateDominators runs in time
+ * O(e*log n) where e is the number of control flow edges and n the number of
+ * CFG vertices and is taken from:
+ *
+ * "A Fast Algorithm for Finding Dominators in a Flowgraph" THOMAS LENGAUER and
+ * ROBERT ENDRE TARJAN 1979, Stanford University
+ *
+ * DOI: 10.1145/357062.357071
+ * http://portal.acm.org/citation.cfm?doid=357062.357071
+ *
+ *
+ * The algorithm for computing the dominatingFrontiers when given the
+ * immediateDominators is taken from
+ *
+ * "Efficiently Computing Static Single Assignment Form and the Control
+ * Dependence Graph" RON CYTRON, JEANNE FERRANTE, BARRY K. ROSEN, and MARK N.
+ * WEGMAN IBM Research Division and F. KENNETH ZADECK Brown University 1991
+ *
+ */
+public class DominatorTree extends EvoMasterGraph, DefaultEdge> {
+
+ private int nodeCount = 0;
+ private final ControlFlowGraph cfg;
+
+ private final Map> dominatorNodesMap = new LinkedHashMap<>();
+ private final Map> dominatorIDMap = new LinkedHashMap<>();
+ private final Map> dominatingFrontiers = new LinkedHashMap<>();
+
+ /**
+ * Will start the computation of all immediateDominators for the given CFG
+ * which can later be retrieved via getImmediateDominator()
+ *
+ * @param cfg a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlFlowGraph} object.
+ */
+ public DominatorTree(ControlFlowGraph cfg) {
+ super(DefaultEdge.class);
+
+ SimpleLogger.debug("Computing DominatorTree for " + cfg.getName());
+
+ this.cfg = cfg;
+
+ createDominatorNodes();
+
+ V root = cfg.determineEntryPoint(); // TODO change to getEntryPoint()
+ SimpleLogger.debug("determined root: " + root);
+ DominatorNode rootNode = getDominatorNodeFor(root);
+
+ depthFirstAnalyze(rootNode);
+
+ computeSemiDominators();
+ computeImmediateDominators(rootNode);
+
+ createDominatorTree();
+
+ computeDominatorFrontiers(rootNode);
+
+ // toDot();
+ }
+
+ private void createDominatorTree() {
+
+ // add dominator nodes
+ addVertices(dominatorIDMap.values());
+
+ SimpleLogger.debug("DTNodes: " + vertexCount());
+
+ // build up tree by adding for each node v an edge from v.iDom to v
+ for (DominatorNode v : vertexSet()) {
+ if (v.isRootNode())
+ continue;
+ if (addEdge(v.immediateDominator, v) == null)
+ throw new IllegalStateException(
+ "internal error while building dominator tree edges");
+
+ SimpleLogger.debug("added DTEdge from " + v.immediateDominator.n + " to " + v.n);
+ }
+
+ SimpleLogger.debug("DTEdges: " + edgeCount());
+
+ // sanity check
+ if (isEmpty())
+ throw new IllegalStateException("expect dominator trees to not be empty");
+ // check tree is connected
+ if (!isConnected())
+ throw new IllegalStateException("dominator tree expected to be connected");
+ }
+
+ private void computeDominatorFrontiers(DominatorNode currentNode) {
+
+ // TODO check assumption: exitPoints in original CFG are exitPoints in resulting DominatorTree
+
+ for (DominatorNode child : getChildren(currentNode))
+ computeDominatorFrontiers(child);
+
+ SimpleLogger.debug("computing dominatingFrontier for: " + currentNode.toString());
+
+ Set dominatingFrontier = dominatingFrontiers.get(currentNode);
+ if (dominatingFrontier == null)
+ dominatingFrontier = new HashSet<>();
+
+ // "local"
+ for (V child : cfg.getChildren(currentNode.node)) {
+ DominatorNode y = getDominatorNodeFor(child);
+ if (y.immediateDominator.n != currentNode.n) {
+ SimpleLogger.debug(" LOCAL adding to DFs: " + y.node);
+ dominatingFrontier.add(y.node);
+ }
+ }
+
+ // "up"
+ for (DominatorNode z : getChildren(currentNode))
+ for (V y : dominatingFrontiers.get(z.node))
+ if (getDominatorNodeFor(y).immediateDominator.n != currentNode.n) {
+ SimpleLogger.debug(" UP adding to DFs: " + y);
+ dominatingFrontier.add(y);
+ }
+
+ dominatingFrontiers.put(currentNode.node, dominatingFrontier);
+ }
+
+ /**
+ * Given a node of this objects CFG this method returns it's previously
+ * computed immediateDominator
+ *
+ * The immediateDominator iDom of a node v has the following properties:
+ *
+ * 1) iDom dominates v
+ *
+ * 2) every other dominator of v dominates iDom
+ *
+ * A node w dominates v or is a dominator of v if and only if every path
+ * from the CFG's entryPoint to v contains w
+ *
+ * @param v A node within this objects CFG for wich the immediateDominator
+ * is to be returned
+ * @return a V object.
+ */
+ public V getImmediateDominator(V v) {
+ if (v == null)
+ throw new IllegalArgumentException("null given");
+ DominatorNode domNode = dominatorNodesMap.get(v);
+ if (domNode == null)
+ throw new IllegalStateException("unknown vertice given");
+
+ if (domNode.immediateDominator == null) {
+ // sanity check: this is only allowed to happen if v is root of CFG
+ if (domNode.n != 1)
+ throw new IllegalStateException(
+ "expect known node without iDom to be root of CFG");
+
+ return null;
+ }
+
+ return domNode.immediateDominator.node;
+ }
+
+ /**
+ * Getter for the field dominatingFrontiers.
+ *
+ * @param v a V object.
+ * @return a {@link java.util.Set} object.
+ */
+ public Set getDominatingFrontiers(V v) {
+ if (v == null)
+ throw new IllegalStateException("null given");
+
+ return dominatingFrontiers.get(v);
+ }
+
+ // computation
+
+ private void createDominatorNodes() {
+
+ for (V v : cfg.vertexSet())
+ dominatorNodesMap.put(v, new DominatorNode<>(v));
+ }
+
+ private void depthFirstAnalyze(DominatorNode currentNode) {
+ // step 1
+
+ initialize(currentNode);
+
+ for (V w : cfg.getChildren(currentNode.node)) {
+ DominatorNode wNode = getDominatorNodeFor(w);
+ if (wNode.semiDominator == null) {
+ wNode.parent = currentNode;
+ depthFirstAnalyze(wNode);
+ }
+ }
+ }
+
+ private void initialize(DominatorNode currentNode) {
+
+ nodeCount++;
+ currentNode.n = nodeCount;
+ currentNode.semiDominator = currentNode;
+
+ SimpleLogger.debug("created " + currentNode + " for "
+ + currentNode.node.toString());
+
+ dominatorIDMap.put(nodeCount, currentNode);
+ }
+
+ private void computeSemiDominators() {
+
+ for (int i = nodeCount; i >= 2; i--) {
+ DominatorNode w = getDominatorNodeById(i);
+
+ // step 2
+ for (V current : cfg.getParents(w.node)) {
+ DominatorNode v = getDominatorNodeFor(current);
+ DominatorNode u = v.eval();
+
+ if (u.semiDominator.n < w.semiDominator.n)
+ w.semiDominator = u.semiDominator;
+ }
+
+ w.semiDominator.bucket.add(w);
+ w.link(w.parent);
+
+ // step 3
+ while (!w.parent.bucket.isEmpty()) {
+
+ DominatorNode v = w.parent.getFromBucket();
+ if (!w.parent.bucket.remove(v))
+ throw new IllegalStateException("internal error");
+
+ DominatorNode u = v.eval();
+ v.immediateDominator = (u.semiDominator.n < v.semiDominator.n ? u
+ : w.parent);
+ }
+ }
+ }
+
+ private void computeImmediateDominators(DominatorNode rootNode) {
+ // step 4
+ for (int i = 2; i <= nodeCount; i++) {
+ DominatorNode w = getDominatorNodeById(i);
+
+ if (w.immediateDominator != w.semiDominator)
+ w.immediateDominator = w.immediateDominator.immediateDominator;
+
+ // logger.debug("iDom for node "+i+" was: "+w.immediateDominator.n);
+ }
+
+ rootNode.immediateDominator = null;
+ }
+
+ private DominatorNode getDominatorNodeById(int id) {
+ DominatorNode r = dominatorIDMap.get(id);
+ if (r == null)
+ throw new IllegalArgumentException("id unknown to this tree");
+
+ return r;
+ }
+
+ private DominatorNode getDominatorNodeFor(V v) {
+ DominatorNode r = dominatorNodesMap.get(v);
+ if (r == null)
+ throw new IllegalStateException(
+ "expect dominatorNodesMap to contain domNodes for all Vs");
+
+ return r;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName() {
+ return "DominatorTree" + graphId;
+ }
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ActualControlFlowGraph.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ActualControlFlowGraph.java
new file mode 100755
index 0000000000..07ee96ad97
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ActualControlFlowGraph.java
@@ -0,0 +1,529 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg;
+
+import org.evomaster.client.java.utils.SimpleLogger;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class ActualControlFlowGraph extends ControlFlowGraph {
+
+ private RawControlFlowGraph rawGraph;
+
+ private BytecodeInstruction entryPoint;
+ private Set exitPoints;
+ private Set branches;
+ private Set branchTargets;
+ private Set joins;
+ private Set joinSources;
+
+ /**
+ *
+ * Constructor for ActualControlFlowGraph.
+ *
+ *
+ * @param rawGraph a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph} object.
+ */
+ public ActualControlFlowGraph(RawControlFlowGraph rawGraph) {
+ super(rawGraph.getClassName(), rawGraph.getMethodName(),
+ rawGraph.getMethodAccess());
+
+ this.rawGraph = rawGraph;
+
+ fillSets();
+ computeGraph();
+ }
+
+ /**
+ *
+ * Constructor for ActualControlFlowGraph.
+ *
+ *
+ * @param toRevert a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph}
+ * object.
+ */
+ protected ActualControlFlowGraph(ActualControlFlowGraph toRevert) {
+ super(toRevert.className, toRevert.methodName, toRevert.access,
+ toRevert.computeReverseJGraph());
+ }
+
+ /**
+ *
+ * computeReverseCFG
+ *
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph} object.
+ */
+ public ActualControlFlowGraph computeReverseCFG() {
+ // TODO: this must be possible to "pre implement" in EvoMasterGraph for
+ // all sub class of EvoMasterGraph
+ return new ActualControlFlowGraph(this);
+ }
+
+ // initialization
+
+ private void fillSets() {
+
+ setEntryPoint(rawGraph.determineEntryPoint());
+ setExitPoints(rawGraph.determineExitPoints());
+
+ setBranches(rawGraph.determineBranches());
+ setBranchTargets();
+ setJoins(rawGraph.determineJoins());
+ setJoinSources();
+ }
+
+ private void setEntryPoint(BytecodeInstruction entryPoint) {
+ if (entryPoint == null)
+ throw new IllegalArgumentException("null given");
+ if (!belongsToMethod(entryPoint))
+ throw new IllegalArgumentException(
+ "entry point does not belong to this CFGs method");
+ this.entryPoint = entryPoint;
+ }
+
+ private void setExitPoints(Set exitPoints) {
+ if (exitPoints == null)
+ throw new IllegalArgumentException("null given");
+
+ this.exitPoints = new HashSet<>();
+
+ for (BytecodeInstruction exitPoint : exitPoints) {
+ if (!belongsToMethod(exitPoint))
+ throw new IllegalArgumentException(
+ "exit point does not belong to this CFGs method");
+ if (!exitPoint.canBeExitPoint())
+ throw new IllegalArgumentException(
+ "unexpected exitPoint byteCode instruction type: "
+ + exitPoint.getInstructionType());
+
+ this.exitPoints.add(exitPoint);
+ }
+ }
+
+ private void setJoins(Set joins) {
+ if (joins == null)
+ throw new IllegalArgumentException("null given");
+
+ this.joins = new HashSet<>();
+
+ for (BytecodeInstruction join : joins) {
+ if (!belongsToMethod(join))
+ throw new IllegalArgumentException(
+ "join does not belong to this CFGs method");
+
+ this.joins.add(join);
+ }
+ }
+
+ private void setJoinSources() {
+ if (joins == null)
+ throw new IllegalStateException(
+ "expect joins to be set before setting of joinSources");
+ if (rawGraph == null)
+ throw new IllegalArgumentException("null given");
+
+ this.joinSources = new HashSet<>();
+
+ for (BytecodeInstruction join : joins)
+ for (ControlFlowEdge joinEdge : rawGraph.incomingEdgesOf(join))
+ joinSources.add(rawGraph.getEdgeSource(joinEdge));
+ }
+
+ private void setBranches(Set branches) {
+ if (branches == null)
+ throw new IllegalArgumentException("null given");
+
+ this.branches = new HashSet<>();
+
+ for (BytecodeInstruction branch : branches) {
+ if (!belongsToMethod(branch))
+ throw new IllegalArgumentException(
+ "branch does not belong to this CFGs method");
+
+ this.branches.add(branch);
+ }
+ }
+
+ private void setBranchTargets() {
+ if (branches == null)
+ throw new IllegalStateException(
+ "expect branches to be set before setting of branchTargets");
+ if (rawGraph == null)
+ throw new IllegalArgumentException("null given");
+
+ this.branchTargets = new HashSet<>();
+
+ for (BytecodeInstruction branch : branches)
+ for (ControlFlowEdge branchEdge : rawGraph.outgoingEdgesOf(branch))
+ branchTargets.add(rawGraph.getEdgeTarget(branchEdge));
+ }
+
+ private Set getInitiallyKnownInstructions() {
+ Set r = new HashSet<>();
+ r.add(entryPoint);
+ r.addAll(exitPoints);
+ r.addAll(branches);
+ r.addAll(branchTargets);
+ r.addAll(joins);
+ r.addAll(joinSources);
+
+ return r;
+ }
+
+ // compute actual CFG from RawControlFlowGraph
+
+ private void computeGraph() {
+
+ computeNodes();
+ computeEdges();
+
+ addAuxiliaryBlocks();
+ }
+
+ private void addAuxiliaryBlocks() {
+
+ // TODO clean up mess: exit-/entry- POINTs versus BLOCKs
+
+ EntryBlock entry = new EntryBlock(className, methodName);
+ ExitBlock exit = new ExitBlock(className, methodName);
+
+ addBlock(entry);
+ addBlock(exit);
+ addEdge(entry, exit);
+ addEdge(entry, this.entryPoint.getBasicBlock());
+ for (BytecodeInstruction exitPoint : this.exitPoints) {
+ addEdge(exitPoint.getBasicBlock(), exit);
+ }
+ }
+
+ private void computeNodes() {
+
+ Set nodes = getInitiallyKnownInstructions();
+
+ for (BytecodeInstruction node : nodes) {
+ if (knowsInstruction(node))
+ continue;
+
+ BasicBlock nodeBlock = rawGraph.determineBasicBlockFor(node);
+ addBlock(nodeBlock);
+ }
+
+ SimpleLogger.debug(vertexCount() + " BasicBlocks");
+ }
+
+ private void computeEdges() {
+
+ for (BasicBlock block : vertexSet()) {
+
+ computeIncomingEdgesFor(block);
+ computeOutgoingEdgesFor(block);
+ }
+
+ SimpleLogger.debug(edgeCount() + " ControlFlowEdges");
+ }
+
+ private void computeIncomingEdgesFor(BasicBlock block) {
+
+ if (isEntryPoint(block))
+ return;
+
+ BytecodeInstruction blockStart = block.getFirstInstruction();
+ Set rawIncomings = rawGraph.incomingEdgesOf(blockStart);
+ for (ControlFlowEdge rawIncoming : rawIncomings) {
+ BytecodeInstruction incomingStart = rawGraph.getEdgeSource(rawIncoming);
+ addRawEdge(incomingStart, block, rawIncoming);
+ }
+ }
+
+ private void computeOutgoingEdgesFor(BasicBlock block) {
+
+ if (isExitPoint(block))
+ return;
+
+ BytecodeInstruction blockEnd = block.getLastInstruction();
+
+ Set rawOutgoings = rawGraph.outgoingEdgesOf(blockEnd);
+ for (ControlFlowEdge rawOutgoing : rawOutgoings) {
+ BytecodeInstruction outgoingEnd = rawGraph.getEdgeTarget(rawOutgoing);
+ addRawEdge(block, outgoingEnd, rawOutgoing);
+ }
+ }
+
+ // internal graph handling
+
+ /**
+ *
+ * addBlock
+ *
+ *
+ * @param nodeBlock a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object.
+ */
+ protected void addBlock(BasicBlock nodeBlock) {
+ if (nodeBlock == null)
+ throw new IllegalArgumentException("null given");
+
+ SimpleLogger.debug("Adding block: " + nodeBlock.getName());
+
+ if (containsVertex(nodeBlock))
+ throw new IllegalArgumentException("block already added before");
+
+ if (!addVertex(nodeBlock))
+ throw new IllegalStateException(
+ "internal error while addind basic block to CFG");
+
+
+ if (!containsVertex(nodeBlock))
+ throw new IllegalStateException(
+ "expect graph to contain the given block on returning of addBlock()");
+
+ SimpleLogger.debug(".. succeeded. nodeCount: " + vertexCount());
+ }
+
+ /**
+ *
+ * addRawEdge
+ *
+ *
+ * @param src a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @param target a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object.
+ * @param origEdge a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlFlowEdge} object.
+ */
+ protected void addRawEdge(BytecodeInstruction src, BasicBlock target,
+ ControlFlowEdge origEdge) {
+ BasicBlock srcBlock = src.getBasicBlock();
+ if (srcBlock == null)
+ throw new IllegalStateException(
+ "when adding an edge to a CFG it is expected to know both the src- and the target-instruction");
+
+ addRawEdge(srcBlock, target, origEdge);
+ }
+
+ /**
+ *
+ * addRawEdge
+ *
+ *
+ * @param src a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object.
+ * @param target a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @param origEdge a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlFlowEdge} object.
+ */
+ protected void addRawEdge(BasicBlock src, BytecodeInstruction target,
+ ControlFlowEdge origEdge) {
+ BasicBlock targetBlock = target.getBasicBlock();
+ if (targetBlock == null)
+ throw new IllegalStateException(
+ "when adding an edge to a CFG it is expected to know both the src- and the target-instruction");
+
+ addRawEdge(src, targetBlock, origEdge);
+ }
+
+ /**
+ *
+ * addRawEdge
+ *
+ *
+ * @param src a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object.
+ * @param target a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object.
+ * @param origEdge a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlFlowEdge} object.
+ */
+ protected void addRawEdge(BasicBlock src, BasicBlock target, ControlFlowEdge origEdge) {
+ if (src == null || target == null)
+ throw new IllegalArgumentException("null given");
+
+ SimpleLogger.debug("Adding edge from " + src.getName() + " to " + target.getName());
+
+ if (containsEdge(src, target)) {
+ SimpleLogger.debug("edge already contained in CFG");
+ // sanity check
+ ControlFlowEdge current = getEdge(src, target);
+ if (current == null)
+ throw new IllegalStateException(
+ "expect getEdge() not to return null on parameters on which containsEdge() retruned true");
+ if (current.getBranchExpressionValue()
+ && !origEdge.getBranchExpressionValue())
+ throw new IllegalStateException(
+ "if this rawEdge was handled before i expect the old edge to have same branchExpressionValue set");
+ if (current.getBranchInstruction() == null) {
+ if (origEdge.getBranchInstruction() != null)
+ throw new IllegalStateException(
+ "if this rawEdge was handled before i expect the old edge to have same branchInstruction set");
+
+ } else if (origEdge.getBranchInstruction() == null
+ || !current.getBranchInstruction().equals(origEdge.getBranchInstruction()))
+ throw new IllegalStateException(
+ "if this rawEdge was handled before i expect the old edge to have same branchInstruction set");
+
+ return;
+ }
+
+ ControlFlowEdge e = new ControlFlowEdge(origEdge);
+ if (!super.addEdge(src, target, e))
+ throw new IllegalStateException("internal error while adding edge to CFG");
+
+ SimpleLogger.debug(".. succeeded, edgeCount: " + edgeCount());
+ }
+
+ // convenience methods to switch between BytecodeInstructons and BasicBlocks
+
+ /**
+ * If the given instruction is known to this graph, the BasicBlock holding
+ * that instruction is returned. Otherwise null will be returned.
+ *
+ * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object.
+ */
+ public BasicBlock getBlockOf(BytecodeInstruction instruction) {
+ if (instruction == null)
+ throw new IllegalArgumentException("null given");
+
+ if (instruction.hasBasicBlockSet())
+ return instruction.getBasicBlock();
+
+ for (BasicBlock block : vertexSet())
+ if (block.containsInstruction(instruction)) {
+ instruction.setBasicBlock(block);
+ return block;
+ }
+
+ SimpleLogger.debug("unknown instruction " + instruction);
+ return null;
+ }
+
+ /**
+ * Checks whether this graph knows the given instruction. That is there is a
+ * BasicBlock in this graph's vertexSet containing the given instruction.
+ *
+ * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @return a boolean.
+ */
+ public boolean knowsInstruction(BytecodeInstruction instruction) {
+ if (instruction == null)
+ throw new IllegalArgumentException("null given");
+
+ if (instruction.hasBasicBlockSet())
+ return containsVertex(instruction.getBasicBlock());
+
+ for (BasicBlock block : vertexSet())
+ if (block.containsInstruction(instruction))
+ return true;
+
+ return false;
+ }
+
+ /**
+ *
+ * getDistance
+ *
+ *
+ * @param v1 a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @param v2 a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @return a int.
+ */
+ /**
+ *
+ * isEntryPoint
+ *
+ *
+ * @param block a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object.
+ * @return a boolean.
+ */
+ private boolean isEntryPoint(BasicBlock block) {
+ if (block == null)
+ throw new IllegalArgumentException("null given");
+
+ return block.containsInstruction(entryPoint);
+ }
+
+ /**
+ *
+ * isExitPoint
+ *
+ *
+ * @param block a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object.
+ * @return a boolean.
+ */
+ private boolean isExitPoint(BasicBlock block) {
+ if (block == null)
+ throw new IllegalArgumentException("null given");
+
+ for (BytecodeInstruction exitPoint : exitPoints)
+ if (block.containsInstruction(exitPoint)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ *
+ * belongsToMethod
+ *
+ *
+ * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @return a boolean.
+ */
+ private boolean belongsToMethod(BytecodeInstruction instruction) {
+ if (instruction == null)
+ throw new IllegalArgumentException("null given");
+
+ if (!className.equals(instruction.getClassName()))
+ return false;
+ return methodName.equals(instruction.getMethodName());
+ }
+
+ // inherited from ControlFlowGraph
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean containsInstruction(BytecodeInstruction v) {
+ if (v == null)
+ return false;
+
+ for (BasicBlock block : vertexSet())
+ if (block.containsInstruction(v))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public BytecodeInstruction getInstruction(int instructionId) {
+
+ BytecodeInstruction searchedFor = BytecodeInstructionPool.getInstance(rawGraph.getClassLoader()).getInstruction(className,
+ methodName,
+ instructionId);
+
+ if (containsInstruction(searchedFor))
+ return searchedFor;
+
+ return null;
+ }
+
+ /**
+ *
+ * Getter for the field branches.
+ *
+ *
+ * @return a {@link java.util.Set} object.
+ */
+ public Set getBranches() {
+ return new HashSet<>(branches);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getCFGType() {
+ return "ACFG";
+ }
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BasicBlock.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BasicBlock.java
new file mode 100755
index 0000000000..b0251a5125
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BasicBlock.java
@@ -0,0 +1,402 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.GraphPool;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg.ControlDependenceGraph;
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * This class is used to represent basic blocks in the control flow graph.
+ *
+ * A basic block is a list of instructions for which the following holds:
+ *
+ * Whenever control flow reaches the first instruction of this blocks list,
+ * control flow will pass through all the instructions of this list successively
+ * and not pass another instruction of the underlying method in the mean time.
+ * The first element in this blocks list does not have a parent in the CFG that
+ * can be prepended to the list and the same would still hold true Finally the
+ * last element in this list does not have a child inside the CFG that could be
+ * appended to the list such that the above still holds true
+ *
+ * In other words: - the first/last element of this blocks list has either 0 or
+ * >=2 parents/children in the CFG - every other element in the list has exactly
+ * 1 parent and exactly 1 child in the raw CFG
+ *
+ *
+ * Taken from:
+ *
+ * "Efficiently Computing Static Single Assignment Form and the Control
+ * Dependence Graph" RON CYTRON, JEANNE FERRANTE, BARRY K. ROSEN, and MARK N.
+ * WEGMAN IBM Research Division and F. KENNETH ZADECK Brown University 1991
+ *
+ * @see org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph
+ */
+public class BasicBlock implements Serializable, Iterable {
+
+ private static final long serialVersionUID = -3465486470017841484L;
+
+ private static int blockCount = 0;
+
+ private int id = -1;
+ protected ClassLoader classLoader;
+ protected String className;
+ protected String methodName;
+
+ private Set controlDependencies;
+
+ protected boolean isAuxiliaryBlock = false;
+
+ private final List instructions = new ArrayList<>();
+
+ /**
+ *
+ * Constructor for BasicBlock.
+ *
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @param blockNodes a {@link java.util.List} object.
+ */
+ public BasicBlock(ClassLoader classLoader, String className, String methodName,
+ List blockNodes) {
+ if (className == null || methodName == null || blockNodes == null)
+ throw new IllegalArgumentException("null given");
+
+ this.className = className;
+ this.methodName = methodName;
+ this.classLoader = classLoader;
+
+ setId();
+ setInstructions(blockNodes);
+ }
+
+ /**
+ * Used by Entry- and ExitBlocks
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ */
+ protected BasicBlock(String className, String methodName) {
+ if (className == null || methodName == null)
+ throw new IllegalArgumentException("null given");
+
+ this.className = className;
+ this.methodName = methodName;
+ this.isAuxiliaryBlock = true;
+ }
+
+ // CDs
+
+ /**
+ * Returns the ControlDependenceGraph of this instructions method
+ *
+ * Convenience method. Redirects the call to GraphPool.getCDG()
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg.ControlDependenceGraph} object.
+ */
+ public ControlDependenceGraph getCDG() {
+
+ ControlDependenceGraph myCDG = GraphPool.getInstance(classLoader).getCDG(className,
+ methodName);
+ if (myCDG == null)
+ throw new IllegalStateException(
+ "expect GraphPool to know CDG for every method for which an instruction is known");
+
+ return myCDG;
+ }
+
+ /**
+ * Returns a cfg.Branch object for each branch this instruction is control
+ * dependent on as determined by the ControlDependenceGraph. If this
+ * instruction is only dependent on the root branch this method returns an
+ * empty set
+ *
+ * If this instruction is a Branch and it is dependent on itself - which can
+ * happen in loops for example - the returned set WILL contain this. If you
+ * do not need the full set in order to avoid loops, call
+ * getAllControlDependentBranches instead
+ *
+ * @return a {@link java.util.Set} object.
+ */
+ public Set getControlDependencies() {
+
+ if (controlDependencies == null)
+ controlDependencies = getCDG().getControlDependentBranches(this);
+
+ // return new HashSet(controlDependentBranches);
+ return controlDependencies;
+ }
+
+ /**
+ *
+ * hasControlDependenciesSet
+ *
+ *
+ * @return a boolean.
+ */
+ public boolean hasControlDependenciesSet() {
+ return controlDependencies != null;
+ }
+
+ // initialization
+
+ private void setInstructions(List blockNodes) {
+ for (BytecodeInstruction instruction : blockNodes) {
+ if (!appendInstruction(instruction))
+ throw new IllegalStateException(
+ "internal error while addind instruction to basic block list");
+ }
+ if (instructions.isEmpty())
+ throw new IllegalStateException(
+ "expect each basic block to contain at least one instruction");
+ }
+
+ private boolean appendInstruction(BytecodeInstruction instruction) {
+ if (instruction == null)
+ throw new IllegalArgumentException("null given");
+ if (!className.equals(instruction.getClassName()))
+ throw new IllegalArgumentException(
+ "expect elements of a basic block to be inside the same class");
+ if (!methodName.equals(instruction.getMethodName()))
+ throw new IllegalArgumentException(
+ "expect elements of a basic block to be inside the same class");
+ if (instruction.hasBasicBlockSet())
+ throw new IllegalArgumentException(
+ "expect to get instruction without BasicBlock already set");
+ if (instructions.contains(instruction))
+ throw new IllegalArgumentException(
+ "a basic block can not contain the same element twice");
+
+ instruction.setBasicBlock(this);
+
+ return instructions.add(instruction);
+ }
+
+ private void setId() {
+ blockCount++;
+ this.id = blockCount;
+ }
+
+ // retrieve information
+
+ /**
+ *
+ * containsInstruction
+ *
+ *
+ * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @return a boolean.
+ */
+ public boolean containsInstruction(BytecodeInstruction instruction) {
+ if (instruction == null)
+ throw new IllegalArgumentException("null given");
+
+ return instructions.contains(instruction);
+ }
+
+ /**
+ *
+ * getFirstInstruction
+ *
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ */
+ public BytecodeInstruction getFirstInstruction() {
+ if (instructions.isEmpty())
+ return null;
+ return instructions.get(0);
+ }
+
+ /**
+ *
+ * getLastInstruction
+ *
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ */
+ public BytecodeInstruction getLastInstruction() {
+ if (instructions.isEmpty())
+ return null;
+ return instructions.get(instructions.size() - 1);
+ }
+
+ /**
+ *
+ * getFirstLine
+ *
+ *
+ * @return a int.
+ */
+ public int getFirstLine() {
+ for (BytecodeInstruction ins : instructions)
+ if (ins.hasLineNumberSet())
+ return ins.getLineNumber();
+
+ return -1;
+ }
+
+ /**
+ *
+ * getLastLine
+ *
+ *
+ * @return a int.
+ */
+ public int getLastLine() {
+
+ int r = -1;
+
+ for (BytecodeInstruction ins : instructions)
+ if (ins.hasLineNumberSet())
+ r = ins.getLineNumber();
+
+ return r;
+ }
+
+ /**
+ *
+ * getName
+ *
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getName() {
+ return (isAuxiliaryBlock ? "aux" : "") + "BasicBlock " + id;
+ // +" - "+methodName;
+ }
+
+ /**
+ *
+ * Getter for the field className.
+ *
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getClassName() {
+ return className;
+ }
+
+ /**
+ *
+ * Getter for the field methodName.
+ *
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getMethodName() {
+ return methodName;
+ }
+
+ // inherited from Object
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+
+ String r = "BB" + id;
+
+ if (instructions.size() < 5)
+ for (BytecodeInstruction ins : instructions)
+ r = r.trim() + " " + ins.getInstructionType();
+ else
+ r += " " + getFirstInstruction().getInstructionType() + " ... "
+ + getLastInstruction().getInstructionType();
+
+ int startLine = getFirstLine();
+ int endLine = getLastLine();
+ r += " l" + (startLine == -1 ? "?" : startLine + "");
+ r += "-l" + (endLine == -1 ? "?" : endLine + "");
+
+ return r;
+ }
+
+
+ // sanity check
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((className == null) ? 0 : className.hashCode());
+ result = prime * result + id;
+ result = prime * result
+ + ((instructions == null) ? 0 : instructions.hashCode());
+ result = prime * result + (isAuxiliaryBlock ? 1231 : 1237);
+ result = prime * result
+ + ((methodName == null) ? 0 : methodName.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (!(obj instanceof BasicBlock))
+ return false;
+ BasicBlock other = (BasicBlock) obj;
+ if (id != other.id)
+ return false;
+ if (className == null) {
+ if (other.className != null)
+ return false;
+ } else if (!className.equals(other.className))
+ return false;
+ if (methodName == null) {
+ if (other.methodName != null)
+ return false;
+ } else if (!methodName.equals(other.methodName))
+ return false;
+ if (instructions == null) {
+ if (other.instructions != null)
+ return false;
+ } else if (!instructions.equals(other.instructions))
+ return false;
+ if (isEntryBlock() != other.isEntryBlock())
+ return false;
+ return isExitBlock() == other.isExitBlock();
+ }
+
+ /**
+ *
+ * isEntryBlock
+ *
+ *
+ * @return a boolean.
+ */
+ public boolean isEntryBlock() {
+ return false;
+ }
+
+ /**
+ *
+ * isExitBlock
+ *
+ *
+ * @return a boolean.
+ */
+ public boolean isExitBlock() {
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Iterable#iterator()
+ */
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterator iterator() {
+ return instructions.iterator();
+ }
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstruction.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstruction.java
new file mode 100755
index 0000000000..43cb8ff21b
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstruction.java
@@ -0,0 +1,778 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.BranchPool;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.GraphPool;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg.ControlDependenceGraph;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.*;
+import org.objectweb.asm.util.Printer;
+
+import java.io.Serializable;
+import java.util.Set;
+
+/**
+ * Internal representation of a BytecodeInstruction
+ */
+public class BytecodeInstruction implements Serializable,
+ Comparable {
+
+ private static final long serialVersionUID = 3630449183355518857L;
+
+ // from ASM library
+ protected AbstractInsnNode asmNode;
+
+ // identification of a byteCode instruction inside EvoSuite
+ protected ClassLoader classLoader;
+ protected String className;
+ protected String methodName;
+ protected int instructionId;
+ protected int bytecodeOffset;
+
+ // auxiliary information
+ private int lineNumber = -1;
+
+ // experiment: also searching through all CFG nodes in order to determine an
+ // instruction BasicBlock might be a little to expensive too just to safe
+ // space for one reference
+ private BasicBlock basicBlock;
+
+ /**
+ * Generates a ByteCodeInstruction instance that represents a byteCode
+ * instruction as indicated by the given ASMNode in the given method and
+ * class
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @param instructionId a int.
+ * @param bytecodeOffset a int.
+ * @param asmNode a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
+ */
+ public BytecodeInstruction(ClassLoader classLoader, String className,
+ String methodName, int instructionId, int bytecodeOffset, AbstractInsnNode asmNode) {
+
+ if (className == null || methodName == null || asmNode == null)
+ throw new IllegalArgumentException("null given");
+ if (instructionId < 0)
+ throw new IllegalArgumentException(
+ "expect instructionId to be positive, not " + instructionId);
+
+ this.instructionId = instructionId;
+ this.bytecodeOffset = bytecodeOffset;
+ this.asmNode = asmNode;
+
+ this.classLoader = classLoader;
+
+ setClassName(className);
+ setMethodName(methodName);
+ }
+
+ /**
+ * Can represent any byteCode instruction
+ *
+ * @param wrap a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ */
+ public BytecodeInstruction(BytecodeInstruction wrap) {
+
+ this(wrap.classLoader, wrap.className, wrap.methodName, wrap.instructionId,
+ wrap.bytecodeOffset, wrap.asmNode, wrap.lineNumber, wrap.basicBlock);
+ }
+
+ /**
+ *
+ * Constructor for BytecodeInstruction.
+ *
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @param instructionId a int.
+ * @param bytecodeOffset a int.
+ * @param asmNode a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
+ * @param lineNumber a int.
+ * @param basicBlock a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object.
+ */
+ public BytecodeInstruction(ClassLoader classLoader, String className,
+ String methodName, int instructionId, int bytecodeOffset, AbstractInsnNode asmNode,
+ int lineNumber, BasicBlock basicBlock) {
+
+ this(classLoader, className, methodName, instructionId, bytecodeOffset, asmNode,
+ lineNumber);
+
+ this.basicBlock = basicBlock;
+ }
+
+ /**
+ *
+ * Constructor for BytecodeInstruction.
+ *
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @param instructionId a int.
+ * @param bytecodeOffset a int.
+ * @param asmNode a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
+ * @param lineNumber a int.
+ */
+ public BytecodeInstruction(ClassLoader classLoader, String className,
+ String methodName, int instructionId, int bytecodeOffset, AbstractInsnNode asmNode,
+ int lineNumber) {
+
+ this(classLoader, className, methodName, instructionId, bytecodeOffset, asmNode);
+
+ if (lineNumber != -1)
+ setLineNumber(lineNumber);
+ }
+
+ // getter + setter
+
+ private void setMethodName(String methodName) {
+ if (methodName == null)
+ throw new IllegalArgumentException("null given");
+
+ this.methodName = methodName;
+ }
+
+ private void setClassName(String className) {
+ if (className == null)
+ throw new IllegalArgumentException("null given");
+
+ this.className = className;
+ }
+
+ // --- Field Management ---
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getInstructionId() {
+ return instructionId;
+ }
+
+ /**
+ *
+ * getBytecodeOffset
+ *
+ *
+ * @return a int.
+ */
+ public int getBytecodeOffset() {
+ return bytecodeOffset;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getMethodName() {
+ return methodName;
+ }
+
+ /**
+ *
+ * Getter for the field className.
+ *
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getClassName() {
+ return className;
+ }
+
+ /**
+ * Return's the BasicBlock that contain's this instruction in it's CFG.
+ *
+ * If no BasicBlock containing this instruction was created yet, null is
+ * returned.
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object.
+ */
+ public BasicBlock getBasicBlock() {
+ if (!hasBasicBlockSet())
+ retrieveBasicBlock();
+ return basicBlock;
+ }
+
+ private void retrieveBasicBlock() {
+
+ if (basicBlock == null)
+ basicBlock = getActualCFG().getBlockOf(this);
+ }
+
+ /**
+ * Once the CFG has been asked for this instruction's BasicBlock it sets
+ * this instance's internal basicBlock field.
+ *
+ * @param block a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object.
+ */
+ public void setBasicBlock(BasicBlock block) {
+ if (block == null)
+ throw new IllegalArgumentException("null given");
+
+ if (!block.getClassName().equals(getClassName())
+ || !block.getMethodName().equals(getMethodName()))
+ throw new IllegalArgumentException(
+ "expect block to be for the same method and class as this instruction");
+ if (this.basicBlock != null)
+ throw new IllegalArgumentException(
+ "basicBlock already set! not allowed to overwrite");
+
+ this.basicBlock = block;
+ }
+
+ /**
+ * Checks whether this instance's basicBlock has already been set by the CFG
+ * or
+ *
+ * @return a boolean.
+ */
+ public boolean hasBasicBlockSet() {
+ return basicBlock != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getLineNumber() {
+
+ if (lineNumber == -1 && isLineNumber())
+ retrieveLineNumber();
+
+ return lineNumber;
+ }
+
+ /**
+ *
+ * Setter for the field lineNumber.
+ *
+ *
+ * @param lineNumber a int.
+ */
+ public void setLineNumber(int lineNumber) {
+ if (lineNumber <= 0)
+ throw new IllegalArgumentException(
+ "expect lineNumber value to be positive");
+
+ if (isLabel())
+ return;
+
+ if (isLineNumber()) {
+ int asmLine = getASMLineNumber();
+ // sanity check
+ if (lineNumber != -1 && asmLine != lineNumber)
+ throw new IllegalStateException(
+ "linenumber instruction has lineNumber field set to a value different from instruction linenumber");
+ this.lineNumber = asmLine;
+ } else {
+ this.lineNumber = lineNumber;
+ }
+ }
+
+ /**
+ * At first, if this instruction constitutes a line number instruction this
+ * method tries to retrieve the lineNumber from the underlying asmNode and
+ * set the lineNumber field to the value given by the asmNode.
+ *
+ * This can lead to an IllegalStateException, should the lineNumber field
+ * have been set to another value previously
+ *
+ * After that, if the lineNumber field is still not initialized, this method
+ * returns false Otherwise it returns true
+ *
+ * @return a boolean.
+ */
+ public boolean hasLineNumberSet() {
+ retrieveLineNumber();
+ return lineNumber != -1;
+ }
+
+ /**
+ * If the underlying ASMNode is a LineNumberNode the lineNumber field of
+ * this instance will be set to the lineNumber contained in that
+ * LineNumberNode
+ *
+ * Should the lineNumber field have been set to a value different from that
+ * contained in the asmNode, this method throws an IllegalStateExeption
+ */
+ private void retrieveLineNumber() {
+ if (isLineNumber()) {
+ int asmLine = getASMLineNumber();
+ // sanity check
+ if (this.lineNumber != -1 && asmLine != this.lineNumber)
+ throw new IllegalStateException(
+ "lineNumber field was manually set to a value different from the actual lineNumber contained in LineNumberNode");
+ this.lineNumber = asmLine;
+ }
+ }
+
+ // --- graph section ---
+
+ /**
+ * Returns the ActualControlFlowGraph of this instructions method
+ *
+ * Convenience method. Redirects the call to GraphPool.getActualCFG()
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph} object.
+ */
+ public ActualControlFlowGraph getActualCFG() {
+
+ ActualControlFlowGraph myCFG = GraphPool.getInstance(classLoader).getActualCFG(className,
+ methodName);
+ if (myCFG == null)
+ throw new IllegalStateException(
+ "expect GraphPool to know CFG for every method for which an instruction is known");
+
+ return myCFG;
+ }
+
+ /**
+ * Returns the RawControlFlowGraph of this instructions method
+ *
+ * Convenience method. Redirects the call to GraphPool.getRawCFG()
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph} object.
+ */
+ public RawControlFlowGraph getRawCFG() {
+
+ RawControlFlowGraph myCFG = GraphPool.getInstance(classLoader).getRawCFG(className,
+ methodName);
+ if (myCFG == null)
+ throw new IllegalStateException(
+ "expect GraphPool to know CFG for every method for which an instruction is known");
+
+ return myCFG;
+ }
+
+ /**
+ * Returns the ControlDependenceGraph of this instructions method
+ *
+ * Convenience method. Redirects the call to GraphPool.getCDG()
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg.ControlDependenceGraph} object.
+ */
+ public ControlDependenceGraph getCDG() {
+
+ ControlDependenceGraph myCDG = GraphPool.getInstance(classLoader).getCDG(className,
+ methodName);
+ if (myCDG == null)
+ throw new IllegalStateException(
+ "expect GraphPool to know CDG for every method for which an instruction is known");
+
+ return myCDG;
+ }
+
+ // --- CDG-Section ---
+
+ /**
+ * Returns a cfg.Branch object for each branch this instruction is control
+ * dependent on as determined by the ControlDependenceGraph. If this
+ * instruction is only dependent on the root branch this method returns an
+ * empty set
+ *
+ * If this instruction is a Branch and it is dependent on itself - which can
+ * happen in loops for example - the returned set WILL contain this. If you
+ * do not need the full set in order to avoid loops, call
+ * getAllControlDependentBranches instead
+ *
+ * @return a {@link java.util.Set} object.
+ */
+ public Set getControlDependencies() {
+
+ BasicBlock myBlock = getBasicBlock();
+
+ // return new
+ // HashSet(myBlock.getControlDependencies());
+ return myBlock.getControlDependencies();
+ }
+
+ public Branch getControlDependentBranch() {
+
+ Set controlDependentBranches = getControlDependencies();
+
+ for (ControlDependency cd : controlDependentBranches)
+ return cd.getBranch();
+
+ return null; // root branch
+ }
+
+ // String methods
+
+ /**
+ *
+ * explain
+ *
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String explain() {
+ if (isBranch()) {
+ if (BranchPool.getInstance(classLoader).isKnownAsBranch(this)) {
+ Branch b = BranchPool.getInstance(classLoader).getBranchForInstruction(this);
+ if (b == null)
+ throw new IllegalStateException(
+ "expect BranchPool to be able to return Branches for instructions fullfilling BranchPool.isKnownAsBranch()");
+
+ return "Branch " + b.getActualBranchId() + " - "
+ + getInstructionType();
+ }
+ return "UNKNOWN Branch I" + instructionId + " "
+ + getInstructionType() + ", jump to " + ((JumpInsnNode) asmNode).label.getLabel();
+
+ // + " - " + ((JumpInsnNode) asmNode).label.getLabel();
+ }
+
+ return getASMNodeString();
+ }
+
+ /**
+ *
+ * getASMNodeString
+ *
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getASMNodeString() {
+ String type = getType();
+ String opcode = getInstructionType();
+
+ String stack = "n/a";
+
+ if (asmNode instanceof LabelNode) {
+ return "LABEL " + ((LabelNode) asmNode).getLabel().toString();
+ } else if (asmNode instanceof FieldInsnNode)
+ return "Field" + " " + ((FieldInsnNode) asmNode).owner + "."
+ + ((FieldInsnNode) asmNode).name + " Type=" + type
+ + ", Opcode=" + opcode;
+ else if (asmNode instanceof FrameNode)
+ return "Frame" + " " + asmNode.getOpcode() + " Type=" + type
+ + ", Opcode=" + opcode;
+ else if (asmNode instanceof IincInsnNode)
+ return "IINC " + ((IincInsnNode) asmNode).var + " Type=" + type
+ + ", Opcode=" + opcode;
+ else if (asmNode instanceof InsnNode)
+ return "" + opcode;
+ else if (asmNode instanceof IntInsnNode)
+ return "INT " + ((IntInsnNode) asmNode).operand + " Type=" + type
+ + ", Opcode=" + opcode;
+ else if (asmNode instanceof MethodInsnNode)
+ return opcode + " " + ((MethodInsnNode) asmNode).owner + "." + ((MethodInsnNode) asmNode).name + ((MethodInsnNode) asmNode).desc;
+ else if (asmNode instanceof JumpInsnNode)
+ return "JUMP " + ((JumpInsnNode) asmNode).label.getLabel()
+ + " Type=" + type + ", Opcode=" + opcode + ", Stack: "
+ + stack + " - Line: " + lineNumber;
+ else if (asmNode instanceof LdcInsnNode)
+ return "LDC " + ((LdcInsnNode) asmNode).cst + " Type=" + type; // +
+ // ", Opcode=";
+ // + opcode; // cst starts with mutationid if
+ // this is location of mutation
+ else if (asmNode instanceof LineNumberNode)
+ return "LINE " + " " + ((LineNumberNode) asmNode).line;
+ else if (asmNode instanceof LookupSwitchInsnNode)
+ return "LookupSwitchInsnNode" + " " + asmNode.getOpcode()
+ + " Type=" + type + ", Opcode=" + opcode;
+ else if (asmNode instanceof MultiANewArrayInsnNode)
+ return "MULTIANEWARRAY " + " " + asmNode.getOpcode() + " Type="
+ + type + ", Opcode=" + opcode;
+ else if (asmNode instanceof TableSwitchInsnNode)
+ return "TableSwitchInsnNode" + " " + asmNode.getOpcode() + " Type="
+ + type + ", Opcode=" + opcode;
+ else if (asmNode instanceof TypeInsnNode) {
+ switch (asmNode.getOpcode()) {
+ case Opcodes.NEW:
+ return "NEW " + ((TypeInsnNode) asmNode).desc;
+ case Opcodes.ANEWARRAY:
+ return "ANEWARRAY " + ((TypeInsnNode) asmNode).desc;
+ case Opcodes.CHECKCAST:
+ return "CHECKCAST " + ((TypeInsnNode) asmNode).desc;
+ case Opcodes.INSTANCEOF:
+ return "INSTANCEOF " + ((TypeInsnNode) asmNode).desc;
+ default:
+ return "Unknown node" + " Type=" + type + ", Opcode=" + opcode;
+ }
+ }
+ // return "TYPE " + " " + node.getOpcode() + " Type=" + type
+ // + ", Opcode=" + opcode;
+ else if (asmNode instanceof VarInsnNode)
+ return opcode + " " + ((VarInsnNode) asmNode).var;
+ else
+ return "Unknown node" + " Type=" + type + ", Opcode=" + opcode;
+ }
+
+ // --- Inherited from Object ---
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+
+ String r = "I" + instructionId;
+
+ r += " (" + +bytecodeOffset + ")";
+ r += " " + explain();
+
+ if (hasLineNumberSet() && !isLineNumber())
+ r += " l" + getLineNumber();
+
+ return r;
+ }
+
+ /**
+ * Convenience method:
+ *
+ * If this instruction is known by the BranchPool to be a Branch, you can
+ * call this method in order to retrieve the corresponding Branch object
+ * registered within the BranchPool.
+ *
+ * Otherwise this method will return null;
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch} object.
+ */
+ public Branch toBranch() {
+
+ try {
+ return BranchPool.getInstance(classLoader).getBranchForInstruction(this);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ *
+ * isLastInstructionInMethod
+ *
+ *
+ * @return a boolean.
+ */
+ public boolean isLastInstructionInMethod() {
+ return equals(getRawCFG().getInstructionWithBiggestId());
+ }
+
+ /**
+ *
+ * canBeExitPoint
+ *
+ *
+ * @return a boolean.
+ */
+ public boolean canBeExitPoint() {
+ return canReturnFromMethod() || isLastInstructionInMethod();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((className == null) ? 0 : className.hashCode());
+ result = prime * result + instructionId;
+ result = prime * result
+ + ((methodName == null) ? 0 : methodName.hashCode());
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ BytecodeInstruction other = (BytecodeInstruction) obj;
+ if (className == null) {
+ if (other.className != null)
+ return false;
+ } else if (!className.equals(other.className))
+ return false;
+ if (instructionId != other.instructionId)
+ return false;
+ if (methodName == null) {
+ return other.methodName == null;
+ } else return methodName.equals(other.methodName);
+ }
+
+ // inherited from Object
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ @Override
+ public int compareTo(BytecodeInstruction o) {
+ return getLineNumber() - o.getLineNumber();
+ }
+
+ /**
+ *
+ * getASMNode
+ *
+ *
+ * @return a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
+ */
+ public AbstractInsnNode getASMNode() {
+ return asmNode;
+ }
+
+ /**
+ *
+ * getInstructionType
+ *
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getInstructionType() {
+
+ if (asmNode.getOpcode() >= 0 && asmNode.getOpcode() < Printer.OPCODES.length)
+ return Printer.OPCODES[asmNode.getOpcode()];
+
+ if (isLineNumber())
+ return "LINE " + this.getLineNumber();
+
+ return getType();
+ }
+
+ /**
+ *
+ * getType
+ *
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getType() {
+ // TODO explain
+ String type = "";
+ if (asmNode.getType() >= 0 && asmNode.getType() < Printer.TYPES.length)
+ type = Printer.TYPES[asmNode.getType()];
+
+ return type;
+ }
+
+ /**
+ *
+ * canReturnFromMethod
+ *
+ *
+ * @return a boolean.
+ */
+ public boolean canReturnFromMethod() {
+ return isReturn() || isThrow();
+ }
+
+ /**
+ *
+ * isReturn
+ *
+ *
+ * @return a boolean.
+ */
+ public boolean isReturn() {
+ switch (asmNode.getOpcode()) {
+ case Opcodes.RETURN:
+ case Opcodes.ARETURN:
+ case Opcodes.IRETURN:
+ case Opcodes.LRETURN:
+ case Opcodes.DRETURN:
+ case Opcodes.FRETURN:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ *
+ * isThrow
+ *
+ *
+ * @return a boolean.
+ */
+ public boolean isThrow() {
+ // TODO: Need to check if this is a caught exception?
+ return asmNode.getOpcode() == Opcodes.ATHROW;
+ }
+
+ /**
+ *
+ * isJump
+ *
+ *
+ * @return a boolean.
+ */
+ public boolean isJump() {
+ return (asmNode instanceof JumpInsnNode);
+ }
+
+ /**
+ *
+ * isGoto
+ *
+ *
+ * @return a boolean.
+ */
+ public boolean isGoto() {
+ if (asmNode instanceof JumpInsnNode) {
+ return (asmNode.getOpcode() == Opcodes.GOTO);
+ }
+ return false;
+ }
+
+ /**
+ *
+ * isBranch
+ *
+ *
+ * @return a boolean.
+ */
+ public boolean isBranch() {
+ return (isJump() && !isGoto());
+ }
+
+ /**
+ * Determines if this instruction is a line number instruction
+ *
+ * More precisely this method checks if the underlying asmNode is a
+ * LineNumberNode
+ *
+ * @return a boolean.
+ */
+ public boolean isLineNumber() {
+ return (asmNode instanceof LineNumberNode);
+ }
+
+ /**
+ *
+ * getASMLineNumber
+ *
+ *
+ * @return a int.
+ */
+ public int getASMLineNumber() {
+ if (!isLineNumber())
+ return -1;
+
+ return ((LineNumberNode) asmNode).line;
+ }
+
+ /**
+ *
+ * isLabel
+ *
+ *
+ * @return a boolean.
+ */
+ public boolean isLabel() {
+ return asmNode instanceof LabelNode;
+ }
+
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionPool.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionPool.java
new file mode 100755
index 0000000000..2026f747ed
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionPool.java
@@ -0,0 +1,379 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg;
+
+import org.evomaster.client.java.instrumentation.dynamosa.AnnotatedLabel;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.BranchPool;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.*;
+import org.evomaster.client.java.utils.SimpleLogger;
+
+import java.util.*;
+
+/**
+ *
+ * BytecodeInstructionPool class.
+ *
+ *
+ */
+public class BytecodeInstructionPool {
+
+ private static final Map instanceMap = new LinkedHashMap<>();
+
+ private final ClassLoader classLoader;
+
+ private BytecodeInstructionPool(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ public static BytecodeInstructionPool getInstance(ClassLoader classLoader) {
+ if (!instanceMap.containsKey(classLoader)) {
+ instanceMap.put(classLoader, new BytecodeInstructionPool(classLoader));
+ }
+
+ return instanceMap.get(classLoader);
+ }
+
+ // maps className -> method inside that class -> list of
+ // BytecodeInstructions
+ private final Map>> instructionMap = new LinkedHashMap<>();
+
+ // fill the pool
+
+ /**
+ * Called by each CFGGenerator for it's corresponding method.
+ *
+ * The MethodNode contains all instructions within a method. A call to
+ * registerMethodNode() fills the instructionMap of the
+ * BytecodeInstructionPool with the instructions in that method and returns
+ * a List containing the BytecodeInstructions within that method.
+ *
+ * While registering all instructions the lineNumber of each
+ * BytecodeInstruction is set.
+ *
+ * @param node a {@link org.objectweb.asm.tree.MethodNode} object.
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @return a {@link java.util.List} object.
+ */
+ public List registerMethodNode(MethodNode node,
+ String className, String methodName) {
+ int lastLineNumber = -1;
+ int bytecodeOffset = 0;
+
+ for (int instructionId = 0; instructionId < node.instructions.size(); instructionId++) {
+ AbstractInsnNode instructionNode = node.instructions.get(instructionId);
+
+ BytecodeInstruction instruction = new BytecodeInstruction(
+ classLoader,
+ className,
+ methodName,
+ instructionId,
+ bytecodeOffset,
+ instructionNode
+ );
+
+ if (instruction.isLineNumber())
+ lastLineNumber = instruction.getLineNumber();
+ else if (lastLineNumber != -1)
+ instruction.setLineNumber(lastLineNumber);
+
+ bytecodeOffset += getBytecodeIncrement(instructionNode);
+
+ if (!instruction.isLabel() && !instruction.isLineNumber()
+ && !(instruction.getASMNode() instanceof FrameNode)) {
+ bytecodeOffset++;
+ }
+
+ registerInstruction(instruction);
+
+ }
+
+ List r = getInstructionsIn(className, methodName);
+ if (r == null || r.size() == 0)
+ throw new IllegalStateException(
+ "expect instruction pool to return non-null non-empty list of instructions for a previously registered method "
+ + methodName);
+
+ return r;
+ }
+
+ /**
+ * Determine how many bytes the current instruction occupies together with
+ * its operands
+ *
+ * @return
+ */
+ private int getBytecodeIncrement(AbstractInsnNode instructionNode) {
+ int opcode = instructionNode.getOpcode();
+ switch (opcode) {
+ case Opcodes.ALOAD: // index
+ case Opcodes.ASTORE: // index
+ case Opcodes.DLOAD:
+ case Opcodes.DSTORE:
+ case Opcodes.FLOAD:
+ case Opcodes.FSTORE:
+ case Opcodes.ILOAD:
+ case Opcodes.ISTORE:
+ case Opcodes.LLOAD:
+ case Opcodes.LSTORE:
+ VarInsnNode varNode = (VarInsnNode) instructionNode;
+ if (varNode.var > 3)
+ return 1;
+ else
+ return 0;
+ case Opcodes.BIPUSH: // byte
+ case Opcodes.NEWARRAY:
+ case Opcodes.RET:
+ return 1;
+ case Opcodes.LDC:
+ LdcInsnNode ldcNode = (LdcInsnNode) instructionNode;
+ if (ldcNode.cst instanceof Double || ldcNode.cst instanceof Long)
+ return 2; // LDC2_W
+ else
+ return 1;
+ case 19: //LDC_W
+ case 20: //LDC2_W
+ return 2;
+ case Opcodes.ANEWARRAY: // indexbyte1, indexbyte2
+ case Opcodes.CHECKCAST: // indexbyte1, indexbyte2
+ case Opcodes.GETFIELD:
+ case Opcodes.GETSTATIC:
+ case Opcodes.GOTO:
+ case Opcodes.IF_ACMPEQ:
+ case Opcodes.IF_ACMPNE:
+ case Opcodes.IF_ICMPEQ:
+ case Opcodes.IF_ICMPNE:
+ case Opcodes.IF_ICMPGE:
+ case Opcodes.IF_ICMPGT:
+ case Opcodes.IF_ICMPLE:
+ case Opcodes.IF_ICMPLT:
+ case Opcodes.IFLE:
+ case Opcodes.IFLT:
+ case Opcodes.IFGE:
+ case Opcodes.IFGT:
+ case Opcodes.IFNE:
+ case Opcodes.IFEQ:
+ case Opcodes.IFNONNULL:
+ case Opcodes.IFNULL:
+ case Opcodes.IINC:
+ case Opcodes.INSTANCEOF:
+ case Opcodes.INVOKESPECIAL:
+ case Opcodes.INVOKESTATIC:
+ case Opcodes.INVOKEVIRTUAL:
+ case Opcodes.JSR:
+ case Opcodes.NEW:
+ case Opcodes.PUTFIELD:
+ case Opcodes.PUTSTATIC:
+ case Opcodes.SIPUSH:
+ // case Opcodes.LDC_W
+ // case Opcodes.LDC2_W
+
+ return 2;
+ case Opcodes.MULTIANEWARRAY:
+ return 3;
+ case Opcodes.INVOKEDYNAMIC:
+ case Opcodes.INVOKEINTERFACE:
+ return 4;
+
+ case Opcodes.LOOKUPSWITCH:
+ case Opcodes.TABLESWITCH:
+ // TODO: Could be more
+ return 4;
+ // case Opcodes.GOTO_W
+ // case Opcodes.JSR_W
+ }
+ return 0;
+ }
+
+ /**
+ *
+ * registerInstruction
+ *
+ *
+ * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ */
+ public void registerInstruction(BytecodeInstruction instruction) {
+ String className = instruction.getClassName();
+ String methodName = instruction.getMethodName();
+
+ if (!instructionMap.containsKey(className))
+ instructionMap.put(className,
+ new LinkedHashMap<>());
+ if (!instructionMap.get(className).containsKey(methodName))
+ instructionMap.get(className).put(methodName,
+ new ArrayList<>());
+
+ instructionMap.get(className).get(methodName).add(instruction);
+ SimpleLogger.debug("Registering instruction " + instruction);
+ List instructions = instructionMap.get(className).get(methodName);
+ if (instructions.size() > 1) {
+ BytecodeInstruction previous = instructions.get(instructions.size() - 2);
+ if (previous.isLabel()) {
+ LabelNode ln = (LabelNode) previous.asmNode;
+ if (ln.getLabel() instanceof AnnotatedLabel) {
+ AnnotatedLabel aLabel = (AnnotatedLabel) ln.getLabel();
+ if (aLabel.isStartTag()) {
+ if (aLabel.shouldIgnore()) {
+ SimpleLogger.debug("Ignoring artificial branch: " + instruction);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ if (instruction.isBranch()) {
+ BranchPool.getInstance(classLoader).registerAsBranch(instruction);
+ }
+ }
+
+ // retrieve data from the pool
+
+ /**
+ *
+ * getInstruction
+ *
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @param instructionId a int.
+ * @param asmNode a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ */
+ public BytecodeInstruction getInstruction(String className, String methodName,
+ int instructionId, AbstractInsnNode asmNode) {
+
+ BytecodeInstruction r = getInstruction(className, methodName, instructionId);
+
+ assert r == null || (asmNode != null && asmNode.equals(r.getASMNode()));
+
+ return r;
+ }
+
+ /**
+ *
+ * getInstruction
+ *
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @param instructionId a int.
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ */
+ public BytecodeInstruction getInstruction(String className, String methodName,
+ int instructionId) {
+
+ if (instructionMap.get(className) == null) {
+ SimpleLogger.debug("unknown class: " + className);
+ SimpleLogger.debug(instructionMap.keySet().toString());
+ return null;
+ }
+ if (instructionMap.get(className).get(methodName) == null) {
+ SimpleLogger.debug("unknown method: " + methodName);
+ SimpleLogger.debug(instructionMap.get(className).keySet().toString());
+ return null;
+ }
+ for (BytecodeInstruction instruction : instructionMap.get(className).get(methodName)) {
+ if (instruction.getInstructionId() == instructionId)
+ return instruction;
+ }
+
+ SimpleLogger.debug("unknown instruction " + instructionId + ", have "
+ + instructionMap.get(className).get(methodName).size());
+ for (int i = 0; i < instructionMap.get(className).get(methodName).size(); i++) {
+ SimpleLogger.info(instructionMap.get(className).get(methodName).get(i).toString());
+ }
+
+ return null;
+ }
+
+ /**
+ *
+ * getInstruction
+ *
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @param node a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ */
+ public BytecodeInstruction getInstruction(String className, String methodName,
+ AbstractInsnNode node) {
+
+ if (instructionMap.get(className) == null) {
+ SimpleLogger.debug("unknown class: " + className);
+ SimpleLogger.debug(instructionMap.keySet().toString());
+ return null;
+ }
+ if (instructionMap.get(className).get(methodName) == null) {
+ SimpleLogger.debug("unknown method: " + methodName);
+ SimpleLogger.debug(instructionMap.get(className).keySet().toString());
+ return null;
+ }
+ for (BytecodeInstruction instruction : instructionMap.get(className).get(methodName)) {
+ if (instruction.asmNode == node)
+ return instruction;
+ }
+
+ SimpleLogger.debug("unknown instruction: " + node + ", have "
+ + instructionMap.get(className).get(methodName).size()
+ + " instructions for this method");
+ SimpleLogger.debug(instructionMap.get(className).get(methodName).toString());
+
+ return null;
+ }
+
+ /**
+ *
+ * getInstructionsIn
+ *
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @return a {@link java.util.List} object.
+ */
+ public List getInstructionsIn(String className, String methodName) {
+ if (instructionMap.get(className) == null
+ || instructionMap.get(className).get(methodName) == null)
+ return null;
+
+ List r = new ArrayList<>(instructionMap.get(className).get(methodName));
+
+ return r;
+ }
+
+ public List getInstructionsIn(String className) {
+ if (instructionMap.get(className) == null)
+ return null;
+
+ List r = new ArrayList<>();
+ Map> methodMap = instructionMap.get(className);
+ for (List methodInstructions : methodMap.values()) {
+ r.addAll(methodInstructions);
+ }
+
+ return r;
+ }
+
+ /**
+ *
+ * forgetInstruction
+ *
+ *
+ * @param ins a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @return a boolean.
+ */
+ public boolean forgetInstruction(BytecodeInstruction ins) {
+ if (!instructionMap.containsKey(ins.getClassName()))
+ return false;
+ if (!instructionMap.get(ins.getClassName()).containsKey(ins.getMethodName()))
+ return false;
+
+ return instructionMap.get(ins.getClassName()).get(ins.getMethodName()).remove(ins);
+ }
+
+}
+
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGGenerator.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGGenerator.java
new file mode 100755
index 0000000000..b541ba60e7
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGGenerator.java
@@ -0,0 +1,225 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.GraphPool;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.analysis.Frame;
+import org.evomaster.client.java.utils.SimpleLogger;
+
+import java.util.List;
+
+/**
+ * This classed is used to create the RawControlFlowGraph which can then be used
+ * to create the ActualControlFlowGraph
+ *
+ * When analyzing a CUT the BytecodeAnalyzer creates an instance of this class
+ * for each method contained in it
+ *
+ * This class's methods get called in the following order:
+ *
+ * - upon constructing, the method at hand is registered via
+ * registerMethodNode() which fills the BytecodeInstructionPool with all
+ * instructions inside that method
+ *
+ * - then registerControlFlowEdge() is called by the BytecodeAnalyzer for each
+ * possible transition from one byteCode instruction to another within the
+ * current method. In this step the CFGGenerator asks the
+ * BytecodeInstructionPool for the previously created instructions and fills up
+ * it's RawControlFlowGraph
+ *
+ * After those calls the RawControlFlowGraph of the method at hand is complete
+ * It should contain a Vertex for each BytecodeInstruction inside the specified
+ * method and an edge for every possible transition between these instructions
+ *
+ */
+public class CFGGenerator {
+
+ private RawControlFlowGraph rawGraph;
+
+ private boolean nodeRegistered = false;
+ private MethodNode currentMethod;
+ private String className;
+ private String methodName;
+ private final ClassLoader classLoader;
+
+ /**
+ * Initializes this generator to generate the CFG for the method identified
+ * by the given parameters
+ *
+ * Calls registerMethodNode() which in turn calls
+ * BytecodeInstructionPool.registerMethodNode() leading to the creation of
+ * all BytecodeInstruction instances for the method at hand
+ *
+ * TODO might not want to give asm.MethodNode to the outside, but rather a
+ * MyMethodNode extended from BytecodeInstruction or something
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @param node a {@link org.objectweb.asm.tree.MethodNode} object.
+ */
+ public CFGGenerator(ClassLoader classLoader, String className, String methodName,
+ MethodNode node) {
+ this.classLoader = classLoader;
+ registerMethodNode(node, className, methodName);
+ }
+
+ /**
+ * Adds the RawControlFlowGraph created by this instance to the GraphPool,
+ * computes the resulting ActualControlFlowGraph
+ */
+ public void registerCFGs() {
+
+ int removed = getRawGraph().removeIsolatedNodes();
+ if (removed > 0)
+ SimpleLogger.info("removed isolated nodes: " + removed + " in " + methodName);
+
+ // non-minimized cfg needed for defuse-coverage and control
+ // dependence calculation
+ GraphPool.getInstance(classLoader).registerRawCFG(getRawGraph());
+ GraphPool.getInstance(classLoader).registerActualCFG(computeActualCFG());
+ }
+
+ // build up the graph
+
+ private void registerMethodNode(MethodNode currentMethod, String className,
+ String methodName) {
+ if (nodeRegistered)
+ throw new IllegalStateException(
+ "registerMethodNode must not be called more than once for each instance of CFGGenerator");
+ if (currentMethod == null || methodName == null || className == null)
+ throw new IllegalArgumentException("null given");
+
+ this.currentMethod = currentMethod;
+ this.className = className;
+ this.methodName = methodName;
+
+ this.rawGraph = new RawControlFlowGraph(classLoader, className, methodName,
+ currentMethod.access);
+
+ List instructionsInMethod = BytecodeInstructionPool.getInstance(classLoader).registerMethodNode(currentMethod,
+ className,
+ methodName);
+
+ // sometimes there is a Label at the very end of a method without a
+ // controlFlowEdge to it. In order to keep the graph as connected as
+ // possible and since this is just a label we will simply ignore these
+ int count = 0;
+ for (BytecodeInstruction ins : instructionsInMethod) {
+ count++;
+ if (!ins.isLabel() || count < instructionsInMethod.size())
+ rawGraph.addVertex(ins);
+ }
+
+ nodeRegistered = true;
+ }
+
+ /**
+ * Internal management of fields and actual building up of the rawGraph
+ *
+ * Is called by the corresponding BytecodeAnalyzer whenever it detects a
+ * control flow edge
+ *
+ * @param src a int.
+ * @param dst a int.
+ * @param frames an array of {@link org.objectweb.asm.tree.analysis.Frame}
+ * objects.
+ * @param isExceptionEdge a boolean.
+ */
+ public void registerControlFlowEdge(int src, int dst, Frame>[] frames,
+ boolean isExceptionEdge) {
+ if (!nodeRegistered)
+ throw new IllegalStateException(
+ "CFGGenrator.registerControlFlowEdge() cannot be called unless registerMethodNode() was called first");
+ if (frames == null)
+ throw new IllegalArgumentException("null given");
+ Frame> dstFrame = frames[dst];
+
+ if (dstFrame == null) {
+ // documentation of getFrames() tells us the following:
+ // Returns:
+ // the symbolic state of the execution stack frame at each bytecode
+ // instruction of the method. The size of the returned array is
+ // equal to the number of instructions (and labels) of the method. A
+ // given frame is null if the corresponding instruction cannot be
+ // reached, or if an error occured during the analysis of the
+ // method.
+ // so let's say we expect the analyzer to return null only if
+ // dst is not reachable and if that happens we just suppress the
+ // corresponding ControlFlowEdge for now
+ // TODO can the CFG become disconnected like that?
+ return;
+ }
+
+ AbstractInsnNode srcNode = currentMethod.instructions.get(src);
+ AbstractInsnNode dstNode = currentMethod.instructions.get(dst);
+
+ // those nodes should have gotten registered by registerMethodNode()
+ BytecodeInstruction srcInstruction = BytecodeInstructionPool.getInstance(classLoader).getInstruction(className,
+ methodName,
+ src,
+ srcNode);
+ BytecodeInstruction dstInstruction = BytecodeInstructionPool.getInstance(classLoader).getInstruction(className,
+ methodName,
+ dst,
+ dstNode);
+
+ if (dstInstruction == null)
+ throw new IllegalStateException(
+ "expect BytecodeInstructionPool to know the instructions in the method of this edge");
+
+ if (null == rawGraph.addEdge(srcInstruction, dstInstruction, isExceptionEdge))
+ SimpleLogger.error("internal error while adding edge");
+ }
+
+ /**
+ * Computes the ActualCFG with BasicBlocks rather then BytecodeInstructions
+ * for this RawCFG.
+ *
+ * See ActualControlFlowGraph and GraphPool for further details.
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph} object.
+ */
+ public ActualControlFlowGraph computeActualCFG() {
+ return new ActualControlFlowGraph(rawGraph);
+ }
+
+ // getter
+
+ /**
+ *
+ * Getter for the field rawGraph.
+ *
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph} object.
+ */
+ public RawControlFlowGraph getRawGraph() {
+ return rawGraph;
+ }
+
+ /**
+ *
+ * Getter for the field className.
+ *
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getClassName() {
+ return className;
+ }
+
+ /**
+ *
+ * Getter for the field methodName.
+ *
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getMethodName() {
+ return methodName;
+ }
+
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlDependency.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlDependency.java
new file mode 100755
index 0000000000..7a44ead7be
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlDependency.java
@@ -0,0 +1,79 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch;
+
+import java.util.Objects;
+
+public class ControlDependency {
+
+ private final Branch branch;
+ private final boolean branchExpressionValue;
+
+ /**
+ * Constructor for ControlDependency.
+ *
+ * @param branch a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch} object.
+ * @param branchExpressionValue a boolean.
+ */
+ public ControlDependency(Branch branch, boolean branchExpressionValue) {
+ if (branch == null)
+ throw new IllegalArgumentException(
+ "control dependencies for the root branch are not permitted (null)");
+
+ this.branch = branch;
+ this.branchExpressionValue = branchExpressionValue;
+ }
+
+ /**
+ * Getter for the field branch.
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch} object.
+ */
+ public Branch getBranch() {
+ return branch;
+ }
+
+ /**
+ * Getter for the field branchExpressionValue.
+ *
+ * @return a boolean.
+ */
+ public boolean getBranchExpressionValue() {
+ return branchExpressionValue;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+
+ String r = "CD " + branch;
+
+ if (branchExpressionValue)
+ r += " - TRUE";
+ else
+ r += " - FALSE";
+
+ return r;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ControlDependency that = (ControlDependency) o;
+ return branchExpressionValue == that.branchExpressionValue &&
+ Objects.equals(branch, that.branch);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(branch, branchExpressionValue);
+ }
+
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowEdge.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowEdge.java
new file mode 100755
index 0000000000..beaa8b3412
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowEdge.java
@@ -0,0 +1,119 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch;
+import org.jgrapht.graph.DefaultEdge;
+
+public class ControlFlowEdge extends DefaultEdge {
+
+ private ControlDependency cd;
+ private boolean isExceptionEdge;
+
+ /**
+ * Constructor for ControlFlowEdge.
+ */
+ public ControlFlowEdge() {
+ this.cd = null;
+ this.isExceptionEdge = false;
+ }
+
+ /**
+ * Constructor for ControlFlowEdge.
+ *
+ * @param isExceptionEdge a boolean.
+ */
+ public ControlFlowEdge(boolean isExceptionEdge) {
+ this.isExceptionEdge = isExceptionEdge;
+ }
+
+ /**
+ * Constructor for ControlFlowEdge.
+ *
+ * @param cd a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlDependency} object.
+ * @param isExceptionEdge a boolean.
+ */
+ public ControlFlowEdge(ControlDependency cd, boolean isExceptionEdge) {
+ this.cd = cd;
+ this.isExceptionEdge = isExceptionEdge;
+ }
+
+
+ /**
+ * Sort of a copy constructor
+ *
+ * @param clone a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlFlowEdge} object.
+ */
+ public ControlFlowEdge(ControlFlowEdge clone) {
+ if (clone != null) {
+ this.cd = clone.cd;
+ this.isExceptionEdge = clone.isExceptionEdge;
+ }
+ }
+
+ /**
+ * getControlDependency
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlDependency} object.
+ */
+ public ControlDependency getControlDependency() {
+ return cd;
+ }
+
+ /**
+ * hasControlDependency
+ *
+ * @return a boolean.
+ */
+ public boolean hasControlDependency() {
+ return cd != null;
+ }
+
+ /**
+ * getBranchInstruction
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch} object.
+ */
+ public Branch getBranchInstruction() {
+ if (cd == null)
+ return null;
+
+ return cd.getBranch();
+ }
+
+ /**
+ * isExceptionEdge
+ *
+ * @return a boolean.
+ */
+ public boolean isExceptionEdge() {
+ return isExceptionEdge;
+ }
+
+ /**
+ * getBranchExpressionValue
+ *
+ * @return a boolean.
+ */
+ public boolean getBranchExpressionValue() {
+ if (hasControlDependency())
+ return cd.getBranchExpressionValue();
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ String r = "";
+ if (isExceptionEdge)
+ r += "E ";
+ if (cd != null)
+ r += cd.toString();
+ return r;
+ }
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowGraph.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowGraph.java
new file mode 100755
index 0000000000..07ac2deb19
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowGraph.java
@@ -0,0 +1,191 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.EvoMasterGraph;
+import org.jgrapht.graph.DefaultDirectedGraph;
+import org.objectweb.asm.Opcodes;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.Set;
+
+
+public abstract class ControlFlowGraph extends
+ EvoMasterGraph {
+
+ protected String className;
+ protected String methodName;
+ protected int access;
+
+ /**
+ * Creates a fresh and empty CFG for the given class and method
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @param access a int.
+ */
+ protected ControlFlowGraph(String className, String methodName, int access) {
+ super(ControlFlowEdge.class);
+
+ if (className == null || methodName == null)
+ throw new IllegalArgumentException("null given");
+
+ this.className = className;
+ this.methodName = methodName;
+ this.access = access;
+ }
+
+ /**
+ * Creates a CFG determined by the given jGraph for the given class and
+ * method
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @param access a int.
+ * @param jGraph a {@link org.jgrapht.graph.DefaultDirectedGraph} object.
+ */
+ protected ControlFlowGraph(String className, String methodName, int access,
+ DefaultDirectedGraph jGraph) {
+ super(jGraph, ControlFlowEdge.class);
+
+ if (className == null || methodName == null)
+ throw new IllegalArgumentException("null given");
+
+ this.className = className;
+ this.methodName = methodName;
+ this.access = access;
+ }
+
+ /**
+ * leadsToNode
+ *
+ * @param e a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlFlowEdge} object.
+ * @param b a V object.
+ * @return a boolean.
+ */
+ public boolean leadsToNode(ControlFlowEdge e, V b) {
+
+ Set handled = new HashSet<>();
+
+ Queue queue = new LinkedList<>();
+ queue.add(getEdgeTarget(e));
+ while (!queue.isEmpty()) {
+ V current = queue.poll();
+ if (handled.contains(current))
+ continue;
+ handled.add(current);
+
+ for (V next : getChildren(current))
+ if (next.equals(b))
+ return true;
+ else
+ queue.add(next);
+ }
+
+ return false;
+ }
+
+ // /**
+ // * Can be used to retrieve a Branch contained in this CFG identified by
+ // it's
+ // * branchId
+ // *
+ // * If no such branch exists in this CFG, null is returned
+ // */
+ // public abstract BytecodeInstruction getBranch(int branchId);
+
+ /**
+ * Can be used to retrieve an instruction contained in this CFG identified
+ * by it's instructionId
+ *
+ * If no such instruction exists in this CFG, null is returned
+ *
+ * @param instructionId a int.
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ */
+ public abstract BytecodeInstruction getInstruction(int instructionId);
+
+ /**
+ * Determines, whether a given instruction is contained in this CFG
+ *
+ * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @return a boolean.
+ */
+ public abstract boolean containsInstruction(BytecodeInstruction instruction);
+
+ /**
+ *
determineEntryPoint
+ *
+ * @return a V object.
+ */
+ public V determineEntryPoint() {
+ Set candidates = determineEntryPoints();
+
+ if (candidates.size() > 1)
+ throw new IllegalStateException(
+ "expect CFG of a method to contain at most one instruction with no parent in "
+ + methodName);
+
+ for (V instruction : candidates)
+ return instruction;
+
+ // there was a back loop to the first instruction within this CFG, so no
+ // candidate
+ // can also happen in empty methods
+ return null;
+ }
+
+ /**
+ * Getter for the field className.
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getClassName() {
+ return className;
+ }
+
+ /**
+ * Getter for the field methodName.
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getMethodName() {
+ return methodName;
+ }
+
+ /**
+ * Getter for the field access.
+ *
+ * @return a int.
+ */
+ public int getMethodAccess() {
+ return access;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName() {
+ return methodName + " " + getCFGType();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected String dotSubFolder() {
+ return toFileString(className) + "/" + getCFGType() + "/";
+ }
+
+ /**
+ * getCFGType
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public abstract String getCFGType();
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/EntryBlock.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/EntryBlock.java
new file mode 100755
index 0000000000..f7ba45042a
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/EntryBlock.java
@@ -0,0 +1,42 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg;
+
+public class EntryBlock extends BasicBlock {
+
+ /**
+ * Constructor for EntryBlock.
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ */
+ public EntryBlock(String className, String methodName) {
+ super(className, methodName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isEntryBlock() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName() {
+ return "EntryBlock for method " + methodName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return getName();
+ }
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ExitBlock.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ExitBlock.java
new file mode 100755
index 0000000000..8e9f34b1d3
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ExitBlock.java
@@ -0,0 +1,42 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg;
+
+public class ExitBlock extends BasicBlock {
+
+ /**
+ * Constructor for ExitBlock.
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ */
+ public ExitBlock(String className, String methodName) {
+ super(className, methodName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isExitBlock() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName() {
+ return "ExitBlock for method " + methodName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return getName();
+ }
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/RawControlFlowGraph.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/RawControlFlowGraph.java
new file mode 100755
index 0000000000..3798ce58dc
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/RawControlFlowGraph.java
@@ -0,0 +1,363 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.BranchPool;
+
+import org.evomaster.client.java.utils.SimpleLogger;
+
+import java.util.*;
+
+import static java.util.Comparator.comparingInt;
+
+/**
+ * Represents the complete CFG of a method
+ *
+ * Essentially this is a graph containing all BytecodeInstrucions of a method as
+ * nodes. From each such instruction there is an edge to each possible
+ * instruction the control flow can reach immediately after that instruction.
+ *
+ */
+public class RawControlFlowGraph extends ControlFlowGraph {
+
+ private final ClassLoader classLoader;
+
+ /**
+ * @return the classLoader
+ */
+ public ClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ /**
+ *
+ * Constructor for RawControlFlowGraph.
+ *
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param methodName a {@link java.lang.String} object.
+ * @param access a int.
+ */
+ public RawControlFlowGraph(ClassLoader classLoader, String className,
+ String methodName, int access) {
+ super(className, methodName, access);
+ this.classLoader = classLoader;
+ SimpleLogger.info("Creating new RawCFG for " + className + "." + methodName + ": " + this.vertexCount());
+ }
+
+ // inherited from ControlFlowGraph
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean containsInstruction(BytecodeInstruction instruction) {
+
+ return containsVertex(instruction);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public BytecodeInstruction getInstruction(int instructionId) {
+ return vertexSet().stream()
+ .filter(v -> v.getInstructionId() == instructionId)
+ .findFirst()
+ .orElse(null);
+ }
+
+ /**
+ *
+ * addEdge
+ *
+ *
+ * @param src a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @param target a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @param isExceptionEdge a boolean.
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlFlowEdge} object.
+ */
+ protected ControlFlowEdge addEdge(BytecodeInstruction src,
+ BytecodeInstruction target, boolean isExceptionEdge) {
+
+ SimpleLogger.debug("Adding edge to RawCFG of " + className + "." + methodName + ": " + this.vertexCount());
+
+ if (BranchPool.getInstance(classLoader).isKnownAsBranch(src) && src.isBranch()) {
+ return addBranchEdge(src, target, isExceptionEdge);
+ }
+
+ return addUnlabeledEdge(src, target, isExceptionEdge);
+ }
+
+ private ControlFlowEdge addUnlabeledEdge(BytecodeInstruction src,
+ BytecodeInstruction target, boolean isExceptionEdge) {
+
+ return internalAddEdge(src, target, new ControlFlowEdge(isExceptionEdge));
+ }
+
+ private ControlFlowEdge addBranchEdge(BytecodeInstruction src,
+ BytecodeInstruction target, boolean isExceptionEdge) {
+
+ boolean isJumping = !isNonJumpingEdge(src, target);
+ ControlDependency cd = new ControlDependency(src.toBranch(), isJumping);
+
+ ControlFlowEdge e = new ControlFlowEdge(cd, isExceptionEdge);
+
+ return internalAddEdge(src, target, e);
+ }
+
+ private ControlFlowEdge internalAddEdge(BytecodeInstruction src,
+ BytecodeInstruction target, ControlFlowEdge e) {
+
+ if (!super.addEdge(src, target, e)) {
+ // TODO find out why this still happens
+ SimpleLogger.debug("unable to add edge from " + src.toString() + " to "
+ + target.toString() + " into the rawCFG of " + getMethodName());
+ e = super.getEdge(src, target);
+ if (e == null)
+ throw new IllegalStateException(
+ "internal graph error - completely unexpected");
+ }
+
+ return e;
+ }
+
+ private boolean isNonJumpingEdge(BytecodeInstruction src, // TODO move to
+ // ControlFlowGraph
+ // and implement
+ // analog method
+ // in ActualCFG
+ BytecodeInstruction dst) {
+
+ return Math.abs(src.getInstructionId() - dst.getInstructionId()) == 1;
+ }
+
+ // functionality used to create ActualControlFlowGraph
+
+ /**
+ *
+ * determineBasicBlockFor
+ *
+ *
+ * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object.
+ */
+ public BasicBlock determineBasicBlockFor(BytecodeInstruction instruction) {
+ if (instruction == null)
+ throw new IllegalArgumentException("null given");
+
+ // TODO clean this up
+
+ SimpleLogger.debug("creating basic block for " + instruction);
+
+ List blockNodes = new ArrayList<>();
+ blockNodes.add(instruction);
+
+ Set handledChildren = new HashSet<>();
+ Set handledParents = new HashSet<>();
+
+ Queue queue = new LinkedList<>();
+ queue.add(instruction);
+ while (!queue.isEmpty()) {
+ BytecodeInstruction current = queue.poll();
+ SimpleLogger.debug("handling " + current.toString());
+
+ // add child to queue
+ if (outDegreeOf(current) == 1)
+ for (BytecodeInstruction child : getChildren(current)) {
+ // this must be only one edge if inDegree was 1
+
+ if (blockNodes.contains(child))
+ continue;
+
+ if (handledChildren.contains(child))
+ continue;
+ handledChildren.add(child);
+
+ if (inDegreeOf(child) < 2) {
+ // insert child right after current
+ // ... always thought ArrayList had insertBefore() and
+ // insertAfter() methods ... well
+ blockNodes.add(blockNodes.indexOf(current) + 1, child);
+
+ SimpleLogger.debug(" added child to queue: " + child.toString());
+ queue.add(child);
+ }
+ }
+
+ // add parent to queue
+ if (inDegreeOf(current) == 1)
+ for (BytecodeInstruction parent : getParents(current)) {
+ // this must be only one edge if outDegree was 1
+
+ if (blockNodes.contains(parent))
+ continue;
+
+ if (handledParents.contains(parent))
+ continue;
+ handledParents.add(parent);
+
+ if (outDegreeOf(parent) < 2) {
+ // insert parent right before current
+ blockNodes.add(blockNodes.indexOf(current), parent);
+
+ SimpleLogger.debug(" added parent to queue: " + parent.toString());
+ queue.add(parent);
+ }
+ }
+ }
+
+ BasicBlock r = new BasicBlock(classLoader, className, methodName, blockNodes);
+
+ SimpleLogger.debug("created nodeBlock: " + r);
+ return r;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public BytecodeInstruction determineEntryPoint() {
+
+ BytecodeInstruction noParent = super.determineEntryPoint();
+ if (noParent != null)
+ return noParent;
+
+ // copied from ControlFlowGraph.determineEntryPoint():
+ // there was a back loop to the first instruction within this CFG, so no
+ // candidate
+
+ return getInstructionWithSmallestId();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set determineExitPoints() {
+
+ Set r = super.determineExitPoints();
+
+ // if the last instruction loops back to a previous instruction there is
+ // no node without a child, so just take the last byteCode instruction
+
+ if (r.isEmpty())
+ r.add(getInstructionWithBiggestId());
+
+ return r;
+
+ }
+
+ /**
+ *
+ * determineBranches
+ *
+ *
+ * @return Set containing all nodes with out degree > 1
+ */
+ public Set determineBranches() {
+ Set r = new HashSet<>();
+ for (BytecodeInstruction instruction : vertexSet())
+ if (outDegreeOf(instruction) > 1)
+ r.add(instruction);
+ return r;
+ }
+
+ /**
+ *
+ * determineJoins
+ *
+ *
+ * @return Set containing all nodes with in degree > 1
+ */
+ public Set determineJoins() {
+ Set r = new HashSet<>();
+ for (BytecodeInstruction instruction : vertexSet())
+ if (inDegreeOf(instruction) > 1)
+ r.add(instruction);
+ return r;
+ }
+
+ /**
+ *
+ * getInstructionWithSmallestId
+ *
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ */
+ public BytecodeInstruction getInstructionWithSmallestId() {
+ return vertexSet().stream()
+ .min(comparingInt(BytecodeInstruction::getInstructionId))
+ .orElse(null);
+ }
+
+ /**
+ *
+ * getInstructionWithBiggestId
+ *
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ */
+ public BytecodeInstruction getInstructionWithBiggestId() {
+ return vertexSet().stream()
+ .max(comparingInt(BytecodeInstruction::getInstructionId))
+ .orElse(null);
+ }
+
+ /**
+ * In some cases there can be isolated nodes within a CFG. For example in an
+ * completely empty try-catch-finally. Since these nodes are not reachable
+ * but cause trouble when determining the entry point of a CFG they get
+ * removed.
+ *
+ * @return a int.
+ */
+ public int removeIsolatedNodes() {
+ Set candidates = determineEntryPoints();
+
+ int removed = 0;
+ if (candidates.size() > 1) {
+
+ for (BytecodeInstruction instruction : candidates) {
+ if (outDegreeOf(instruction) == 0 && graph.removeVertex(instruction)) {
+ removed++;
+ BytecodeInstructionPool.getInstance(classLoader).forgetInstruction(instruction);
+ }
+ }
+
+ }
+ return removed;
+ }
+
+ // miscellaneous
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ for (ControlFlowEdge e : graph.edgeSet()) {
+ sb.append(graph.getEdgeSource(e) + " -> " + graph.getEdgeTarget(e));
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getCFGType() {
+ return "RCFG";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean addVertex(BytecodeInstruction ins) {
+ return super.addVertex(ins);
+ }
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/Branch.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/Branch.java
new file mode 100755
index 0000000000..9ec1297d34
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/Branch.java
@@ -0,0 +1,173 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction;
+
+import java.io.Serializable;
+
+/**
+ * An object of this class corresponds to a Branch inside the class under test.
+ *
+ *
+ * Branches are created by the {@code CFGMethodVisitor} via the {@code BranchPool}. Each Branch
+ * holds its corresponding {@code BytecodeInstruction} from the {@code RawControlFlowGraph} and
+ * is associated with a unique {@code actualBranchId}.
+ *
+ *
+ * A Branch can either come from a jump instruction, as defined in
+ * {@code BytecodeInstruction.isBranch()}.
+ * Only {@code BytecodeInstructions} satisfying {@code BytecodeInstruction.isActualbranch()} are
+ * expected to be associated with a {@code Branch} object.
+ *
+ */
+public class Branch implements Serializable {
+
+ private static final long serialVersionUID = -4732587925060748263L;
+
+ private final int actualBranchId;
+
+ private final BytecodeInstruction instruction;
+
+ /**
+ * Canonical identifiers matching the descriptive ids used in {@code ObjectiveNaming}.
+ */
+ private String thenObjectiveId;
+ private String elseObjectiveId;
+
+ /**
+ * Constructor for usual jump instruction Branches.
+ *
+ * @param branchInstruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @param actualBranchId a int.
+ */
+ public Branch(BytecodeInstruction branchInstruction, int actualBranchId) {
+ if (!branchInstruction.isBranch())
+ throw new IllegalArgumentException("only branch instructions are accepted");
+
+ this.instruction = branchInstruction;
+ this.actualBranchId = actualBranchId;
+
+ if (this.actualBranchId < 1)
+ throw new IllegalStateException(
+ "expect branch to have actualBranchId set to positive value");
+ }
+
+ /**
+ *
+ * Getter for the field actualBranchId.
+ *
+ *
+ * @return a int.
+ */
+ public int getActualBranchId() {
+ return actualBranchId;
+ }
+
+ /**
+ *
+ * Getter for the field instruction.
+ *
+ *
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ */
+ public BytecodeInstruction getInstruction() {
+ return instruction;
+ }
+
+ /**
+ *
+ * getClassName
+ *
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getClassName() {
+ return instruction.getClassName();
+ }
+
+ /**
+ *
+ * getMethodName
+ *
+ *
+ * @return a {@link java.lang.String} object.
+ */
+ public String getMethodName() {
+ return instruction.getMethodName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + actualBranchId;
+ result = prime * result + ((instruction == null) ? 0 : instruction.hashCode());
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Branch other = (Branch) obj;
+ if (actualBranchId != other.actualBranchId)
+ return false;
+ if (instruction == null) {
+ if (other.instruction != null)
+ return false;
+ } else if (!instruction.equals(other.instruction))
+ return false;
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ String r = "I" + instruction.getInstructionId();
+ r += " Branch " + getActualBranchId();
+ r += " " + instruction.getInstructionType();
+ r += " L" + instruction.getLineNumber();
+
+ if (thenObjectiveId != null || elseObjectiveId != null) {
+ r += " [" + (thenObjectiveId == null ? "null" : thenObjectiveId) +
+ ", " + (elseObjectiveId == null ? "null" : elseObjectiveId) + "]";
+ }
+
+ return r;
+ }
+
+ /**
+ * Store the descriptive identifiers associated with this branch.
+ *
+ * @param thenId descriptive id for the "true" outcome
+ * @param elseId descriptive id for the "false" outcome
+ */
+ public void setObjectiveIds(String thenId, String elseId) {
+ this.thenObjectiveId = thenId;
+ this.elseObjectiveId = elseId;
+ }
+
+ public String getThenObjectiveId() {
+ return thenObjectiveId;
+ }
+
+ public String getElseObjectiveId() {
+ return elseObjectiveId;
+ }
+
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchPool.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchPool.java
new file mode 100755
index 0000000000..dbd5c2190f
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchPool.java
@@ -0,0 +1,179 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction;
+import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming;
+import org.evomaster.client.java.utils.SimpleLogger;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class is supposed to hold all the available information concerning
+ * Branches.
+ *
+ * The addBranch()-Method gets called during class analysis. Whenever the
+ * BytecodeInstructionPool detects a BytecodeInstruction that corresponds to a
+ * Branch in the class under test as defined in
+ * BytecodeInstruction.isActualBranch() it calls the registerAsBranch() method
+ * of this class which in turn properly registers the instruction within this
+ * pool.
+ *
+ *
+ */
+public class BranchPool {
+
+ // maps all known branch instructions to their branchId
+ private final Map registeredNormalBranches = new HashMap<>();
+
+ // number of known Branches - used for actualBranchIds
+ private int branchCounter = 0;
+
+ private static final Map instanceMap = new HashMap<>();
+
+ private final Map branchOrdinalCounters = new HashMap<>();
+
+ public static BranchPool getInstance(ClassLoader classLoader) {
+ if (!instanceMap.containsKey(classLoader)) {
+ instanceMap.put(classLoader, new BranchPool());
+ }
+
+ return instanceMap.get(classLoader);
+ }
+
+ @VisibleForTesting
+ public static void resetForTesting(ClassLoader classLoader) {
+ BranchPool pool = instanceMap.remove(classLoader);
+ if (pool != null) {
+ pool.registeredNormalBranches.clear();
+ pool.branchOrdinalCounters.clear();
+ pool.branchCounter = 0;
+ }
+ }
+ // fill the pool
+
+
+
+ /**
+ * Called by the BytecodeInstructionPool whenever it detects an instruction
+ * that corresponds to a Branch in the class under test as defined by
+ * BytecodeInstruction.isActualBranch().
+ *
+ * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ */
+ public void registerAsBranch(BytecodeInstruction instruction) {
+ if (!(instruction.isBranch()))
+ throw new IllegalArgumentException("CFGVertex of a branch expected");
+ if (isKnownAsBranch(instruction))
+ return;
+
+ registerInstruction(instruction);
+
+ }
+
+ private void registerInstruction(BytecodeInstruction v) {
+ if (isKnownAsBranch(v))
+ throw new IllegalStateException(
+ "expect registerInstruction() to be called at most once for each instruction");
+
+ if (!v.isBranch())
+ throw new IllegalArgumentException("expect given instruction to be a normal branch");
+
+ registerNormalBranchInstruction(v);
+ }
+
+ private void registerNormalBranchInstruction(BytecodeInstruction v) {
+ if (!v.isBranch())
+ throw new IllegalArgumentException("normal branch instruction expceted");
+
+ if (registeredNormalBranches.containsKey(v))
+ throw new IllegalArgumentException(
+ "instruction already registered as a normal branch");
+
+ branchCounter++;
+ Branch b = new Branch(v, branchCounter);
+ attachObjectiveIds(b);
+ registeredNormalBranches.put(v, b);
+ SimpleLogger.info("Branch " + branchCounter + " at line " + v.getLineNumber());
+ }
+
+ private void attachObjectiveIds(Branch branch) {
+ if (branch == null) {
+ return;
+ }
+ BytecodeInstruction instruction = branch.getInstruction();
+ if (instruction == null || !instruction.isBranch()) {
+ return;
+ }
+ try {
+ if (!instruction.hasLineNumberSet()) {
+ SimpleLogger.warn("Cannot attach objective ids to branch without line number: " + branch);
+ return;
+ }
+ int lineNumber = instruction.getLineNumber();
+ if (lineNumber <= 0) {
+ SimpleLogger.warn("Invalid line number while attaching objective ids to branch: " + branch);
+ return;
+ }
+ String className = instruction.getClassName();
+ String methodName = instruction.getMethodName();
+ int ordinal = nextOrdinalForLine(className, methodName, lineNumber);
+ int opcode = instruction.getASMNode().getOpcode();
+ String thenId = ObjectiveNaming.branchObjectiveName(className, lineNumber, ordinal, true, opcode);
+ String elseId = ObjectiveNaming.branchObjectiveName(className, lineNumber, ordinal, false, opcode);
+ branch.setObjectiveIds(thenId, elseId);
+ } catch (Exception e) {
+ SimpleLogger.warn("Failed to attach objective ids to branch " + branch + ": " + e.getMessage());
+ }
+ }
+
+ private int nextOrdinalForLine(String className, String methodName, int lineNumber) {
+ String key = className + "#" + methodName + "#" + lineNumber;
+ Integer ordinal = branchOrdinalCounters.getOrDefault(key, 0);
+ branchOrdinalCounters.put(key, ordinal + 1);
+ return ordinal;
+ }
+
+ // retrieve information from the pool
+
+ /**
+ * Checks whether the given instruction has Branch objects associated with
+ * it.
+ *
+ * Returns true if the given BytecodeInstruction previously passed a call to
+ * registerAsBranch(instruction), false otherwise
+ *
+ * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @return a boolean.
+ */
+ public boolean isKnownAsBranch(BytecodeInstruction instruction) {
+ return registeredNormalBranches.containsKey(instruction);
+ }
+
+ /**
+ *
+ * getBranchForInstruction
+ *
+ *
+ * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object.
+ * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch} object.
+ */
+ public Branch getBranchForInstruction(BytecodeInstruction instruction) {
+ if (instruction == null)
+ throw new IllegalArgumentException("null given");
+ if (!isKnownAsBranch(instruction))
+ throw new IllegalArgumentException(
+ "expect given instruction to be known as a normal branch");
+
+ Branch branch = registeredNormalBranches.get(instruction);
+ if (branch == null) {
+ throw new IllegalStateException("Branch not found for instruction");
+ }
+ return branch;
+ }
+
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGClassVisitor.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGClassVisitor.java
new file mode 100644
index 0000000000..676da385bf
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGClassVisitor.java
@@ -0,0 +1,47 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.visitor;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Minimal class visitor that wraps each method with CFGMethodVisitor to build graphs,
+ * while delegating to the downstream visitor chain unchanged.
+ */
+public class CFGClassVisitor extends ClassVisitor {
+
+ private final ClassLoader classLoader;
+ private String classNameWithDots;
+
+ public CFGClassVisitor(ClassLoader classLoader, ClassVisitor cv) {
+ super(Opcodes.ASM9, cv);
+ this.classLoader = classLoader;
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ this.classNameWithDots = name == null ? null : name.replace('/', '.');
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
+ MethodVisitor downstream = super.visitMethod(access, name, descriptor, signature, exceptions);
+ return new CFGMethodVisitor(
+ classLoader,
+ classNameWithDots,
+ access,
+ name,
+ descriptor,
+ signature,
+ exceptions,
+ downstream
+ );
+ }
+}
+
+
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGMethodVisitor.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGMethodVisitor.java
new file mode 100755
index 0000000000..da6aed4f3c
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGMethodVisitor.java
@@ -0,0 +1,132 @@
+/*
+ * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite)
+ * and modified for use in EvoMaster's Dynamosa module.
+ */
+package org.evomaster.client.java.instrumentation.dynamosa.visitor;
+
+import org.evomaster.client.java.instrumentation.dynamosa.AnnotatedMethodNode;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.CFGGenerator;
+import org.objectweb.asm.*;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.analysis.Analyzer;
+import org.objectweb.asm.tree.analysis.AnalyzerException;
+import org.objectweb.asm.tree.analysis.SourceInterpreter;
+import org.objectweb.asm.tree.analysis.SourceValue;
+import org.evomaster.client.java.utils.SimpleLogger;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Create a minimized control flow graph for the method and store it. In
+ * addition, this visitor also adds instrumentation for branch distance
+ * measurement
+ *
+ * defUse, concurrency and LCSAJs instrumentation is also added (if the
+ * properties are set).
+ *
+ */
+public class CFGMethodVisitor extends MethodVisitor {
+
+ /**
+ * Methods to skip during CFG analysis.
+ */
+ public static final List EXCLUDE = Arrays.asList("()V");
+
+ /**
+ * This is the name + the description of the method. It is more like the
+ * signature and less like the name. The name of the method can be found in
+ * this.plain_name
+ */
+ private final String methodName;
+
+ private final MethodVisitor next;
+ private final int access;
+ private final String className;
+ private final ClassLoader classLoader;
+
+ // no line tracking needed here
+
+ /**
+ *
+ * Constructor for CFGMethodVisitor.
+ *
+ *
+ * @param className a {@link java.lang.String} object.
+ * @param access a int.
+ * @param name a {@link java.lang.String} object.
+ * @param desc a {@link java.lang.String} object.
+ * @param signature a {@link java.lang.String} object.
+ * @param exceptions an array of {@link java.lang.String} objects.
+ * @param mv a {@link org.objectweb.asm.MethodVisitor} object.
+ */
+ public CFGMethodVisitor(ClassLoader classLoader, String className, int access,
+ String name, String desc, String signature, String[] exceptions,
+ MethodVisitor mv) {
+
+ // super(new MethodNode(access, name, desc, signature, exceptions),
+ // className,
+ // name.replace('/', '.'), null, desc);
+
+ super(Opcodes.ASM9, new AnnotatedMethodNode(access, name, desc, signature,
+ exceptions));
+
+ this.next = mv;
+ this.className = className; // .replace('/', '.');
+ this.access = access;
+ this.methodName = name + desc;
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void visitEnd() {
+ SimpleLogger.debug("Creating CFG of " + className + "." + methodName);
+ MethodNode mn = (AnnotatedMethodNode) mv;
+ // skip excluded, abstract or native methods
+ if (EXCLUDE.contains(methodName)
+ || (access & Opcodes.ACC_ABSTRACT) != 0
+ || (access & Opcodes.ACC_NATIVE) != 0) {
+ mn.accept(next);
+ return;
+ }
+ SimpleLogger.info("Analyzing method " + methodName + " in class " + className);
+ CFGGenerator cfgGenerator = new CFGGenerator(classLoader, className, methodName, mn);
+ SimpleLogger.info("Generating CFG for method " + methodName);
+ try {
+ Analyzer analyzer = new Analyzer(new SourceInterpreter()) {
+ @Override
+ protected void newControlFlowEdge(int src, int dst) {
+ cfgGenerator.registerControlFlowEdge(src, dst, getFrames(), false);
+ }
+
+ @Override
+ protected boolean newControlFlowExceptionEdge(int src, int dst) {
+ cfgGenerator.registerControlFlowEdge(src, dst, getFrames(), true);
+ return true;
+ }
+ };
+ analyzer.analyze(className, mn);
+ SimpleLogger.debug("Method graph for "
+ + className
+ + "."
+ + methodName
+ + " contains "
+ + cfgGenerator.getRawGraph().vertexSet().size()
+ + " nodes for " + analyzer.getFrames().length
+ + " instructions");
+ // compute Raw and ActualCFG and put both into GraphPool
+ cfgGenerator.registerCFGs();
+ SimpleLogger.info("Created CFG for method " + methodName);
+ } catch (AnalyzerException e) {
+ SimpleLogger.error("Analyzer exception while analyzing " + className + "."
+ + methodName + ": " + e);
+ e.printStackTrace();
+ }
+ mn.accept(next);
+ }
+
+ // removed auxiliary methods related to method listings and filters
+}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/AgentController.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/AgentController.java
index 0ecdbe7f6d..e1a3e3a6e7 100644
--- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/AgentController.java
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/AgentController.java
@@ -4,12 +4,15 @@
import org.evomaster.client.java.instrumentation.InstrumentationController;
import org.evomaster.client.java.instrumentation.staticstate.UnitsInfoRecorder;
import org.evomaster.client.java.utils.SimpleLogger;
+import org.evomaster.client.java.instrumentation.dynamosa.DynamosaConfig;
+import org.evomaster.client.java.instrumentation.external.DynamosaControlDependenceSnapshot;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
/**
@@ -65,6 +68,10 @@ public static void start(int port){
InstrumentationController.resetForNewSearch();
sendCommand(Command.ACK);
break;
+ case SET_DYNAMOSA_CONFIG:
+ handleDynamosaConfig();
+ sendCommand(Command.ACK);
+ break;
case NEW_TEST:
InstrumentationController.resetForNewTest();
sendCommand(Command.ACK);
@@ -108,6 +115,9 @@ public static void start(int port){
case EXTRACT_JVM_DTO:
handleExtractingSpecifiedDto();
break;
+ case DYNAMOSA_CDG_SNAPSHOT:
+ handleDynamosaControlDependenceSnapshot();
+ break;
default:
SimpleLogger.error("Unrecognized command: "+command);
return;
@@ -121,6 +131,21 @@ public static void start(int port){
thread.start();
}
+ private static void handleDynamosaConfig() {
+ try {
+ Object msg = in.readObject();
+ DynamosaConfigDto dto = (DynamosaConfigDto) msg;
+ if (dto.enableGraphs != null) {
+ DynamosaConfig.setEnableGraphs(dto.enableGraphs);
+ }
+ if (dto.writeCfg != null) {
+ DynamosaConfig.setWriteCfgEnabled(dto.writeCfg);
+ }
+ } catch (Exception e) {
+ SimpleLogger.error("Failure in handling DYNAMOSA config: "+e.getMessage());
+ }
+ }
+
private static void sendCommand(Command command){
try {
@@ -226,6 +251,24 @@ private static void handleExtractingSpecifiedDto(){
}
}
+ private static void handleDynamosaControlDependenceSnapshot(){
+ try {
+ Object msg = in.readObject();
+ int fromIndex = 0;
+ if (msg instanceof Integer) {
+ fromIndex = (Integer) msg;
+ }
+ DynamosaControlDependenceSnapshot snapshot = InstrumentationController.getControlDependenceSnapshot(fromIndex);
+ sendObject(snapshot);
+ } catch (Exception e){
+ SimpleLogger.error("Failure in handling Dynamosa CDG snapshot: "+e.getMessage());
+ try {
+ sendObject(new DynamosaControlDependenceSnapshot(Collections.emptyList(), 0));
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
private static void handleTargetInfos() {
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/Command.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/Command.java
index afb9ba7484..8448c97096 100644
--- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/Command.java
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/Command.java
@@ -22,5 +22,7 @@ public enum Command implements Serializable {
EXECUTING_ACTION,
BOOT_TIME_INFO,
EXTRACT_JVM_DTO,
- BOOTING_SUT
+ BOOTING_SUT,
+ SET_DYNAMOSA_CONFIG,
+ DYNAMOSA_CDG_SNAPSHOT
}
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/DynamosaConfigDto.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/DynamosaConfigDto.java
new file mode 100644
index 0000000000..fe5c54fdb3
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/DynamosaConfigDto.java
@@ -0,0 +1,8 @@
+package org.evomaster.client.java.instrumentation.external;
+
+import java.io.Serializable;
+
+public class DynamosaConfigDto implements Serializable {
+ public Boolean enableGraphs;
+ public Boolean writeCfg;
+}
\ No newline at end of file
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/DynamosaControlDependenceSnapshot.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/DynamosaControlDependenceSnapshot.java
new file mode 100644
index 0000000000..42b6f63572
--- /dev/null
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/DynamosaControlDependenceSnapshot.java
@@ -0,0 +1,36 @@
+package org.evomaster.client.java.instrumentation.external;
+
+import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Serializable payload used between the Java Agent and the controller to
+ * exchange newly discovered control-dependence graphs together with the
+ * next index to request.
+ */
+public class DynamosaControlDependenceSnapshot implements Serializable {
+
+ private static final long serialVersionUID = 6779255129756570792L;
+
+ private final List graphs;
+ private final int nextIndex;
+
+ public DynamosaControlDependenceSnapshot(List graphs, int nextIndex) {
+ this.graphs = graphs == null ? new ArrayList<>() : new ArrayList<>(graphs);
+ this.nextIndex = Math.max(nextIndex, 0);
+ }
+
+ public List getGraphs() {
+ return Collections.unmodifiableList(graphs);
+ }
+
+ public int getNextIndex() {
+ return nextIndex;
+ }
+}
+
+
diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ServerController.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ServerController.java
index 5df63053ce..50765e0be0 100644
--- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ServerController.java
+++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ServerController.java
@@ -1,6 +1,7 @@
package org.evomaster.client.java.instrumentation.external;
import org.evomaster.client.java.instrumentation.*;
+import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto;
import org.evomaster.client.java.instrumentation.staticstate.UnitsInfoRecorder;
import org.evomaster.client.java.utils.SimpleLogger;
@@ -11,6 +12,7 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
/**
@@ -34,6 +36,7 @@ public class ServerController {
private Socket socket;
protected ObjectOutputStream out;
protected ObjectInputStream in;
+ private int dynamosaCdgIndex = 0;
public synchronized int startServer() {
@@ -46,6 +49,7 @@ public synchronized int startServer() {
throw new IllegalStateException(e);
}
+ dynamosaCdgIndex = 0;
return server.getLocalPort();
}
@@ -58,6 +62,7 @@ public synchronized void closeServer() {
socket = null;
in = null;
out = null;
+ dynamosaCdgIndex = 0;
} catch (IOException e) {
throw new IllegalStateException(e);
}
@@ -216,6 +221,10 @@ public boolean setBootingSut(boolean isBooting){
return sendWithDataAndExpectACK(Command.BOOTING_SUT, isBooting);
}
+ public boolean setDynamosaConfig(DynamosaConfigDto dto){
+ return sendWithDataAndExpectACK(Command.SET_DYNAMOSA_CONFIG, dto);
+ }
+
// public synchronized List getAllCoveredTargetsInfo() {
// boolean sent = sendCommand(Command.ALL_COVERED_TARGETS_INFO);
// if (!sent) {
@@ -291,6 +300,34 @@ public synchronized List getAdditionalInfoList() {
return (List) response;
}
+ public synchronized List getDynamosaControlDependenceGraphs() {
+
+ boolean sent = sendCommand(Command.DYNAMOSA_CDG_SNAPSHOT);
+ if (!sent) {
+ SimpleLogger.error("Failed to send Dynamosa CDG request");
+ return Collections.emptyList();
+ }
+
+ if (!sendObject(dynamosaCdgIndex)) {
+ SimpleLogger.error("Failed to send Dynamosa CDG index");
+ return Collections.emptyList();
+ }
+
+ Object response = waitAndGetResponse();
+ if (response == null) {
+ SimpleLogger.error("Failed to read Dynamosa CDG response");
+ return Collections.emptyList();
+ }
+
+ if (!(response instanceof DynamosaControlDependenceSnapshot)) {
+ throw new IllegalStateException(errorMsgExpectingResponse(response, DynamosaControlDependenceSnapshot.class.getSimpleName()));
+ }
+
+ DynamosaControlDependenceSnapshot snapshot = (DynamosaControlDependenceSnapshot) response;
+ dynamosaCdgIndex = snapshot.getNextIndex();
+ return snapshot.getGraphs();
+ }
+
public synchronized BootTimeObjectiveInfo handleBootTimeObjectiveInfo() {
boolean sent = sendCommand(Command.BOOT_TIME_INFO);
diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/GraphPoolTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/GraphPoolTest.java
new file mode 100644
index 0000000000..5bf7a62949
--- /dev/null
+++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/GraphPoolTest.java
@@ -0,0 +1,155 @@
+package org.evomaster.client.java.instrumentation.dynamosa.graphs;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.InsnNode;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class GraphPoolTest {
+
+ private static final ClassLoader TEST_LOADER = GraphPoolTest.class.getClassLoader();
+ private static final String CLASS_NAME = "com.example.GraphPoolFixture";
+ private static final String METHOD_NAME = "sample()V";
+
+ // Custom class loader that intentionally throws an exception when the ExecutionTracer class is loaded.
+ private ClassLoader blockingLoader;
+
+ @AfterEach
+ void resetPools() {
+ GraphPool.resetForTesting(TEST_LOADER);
+ if (blockingLoader != null) {
+ GraphPool.resetForTesting(blockingLoader);
+ blockingLoader = null;
+ }
+ }
+
+ @Test
+ void getInstanceReturnsSingletonPerLoader() {
+ GraphPool first = GraphPool.getInstance(TEST_LOADER);
+ GraphPool second = GraphPool.getInstance(TEST_LOADER);
+ assertSame(first, second);
+
+ ClassLoader otherLoader = new SimpleDelegatingClassLoader(TEST_LOADER);
+ GraphPool third = GraphPool.getInstance(otherLoader);
+ assertNotSame(first, third);
+ GraphPool.resetForTesting(otherLoader);
+ }
+
+ @Test
+ void getRawCFGReturnsNullWhenClassIsUnknown() {
+ GraphPool pool = GraphPool.getInstance(TEST_LOADER);
+ assertNull(pool.getRawCFG("unknown.class", METHOD_NAME));
+ }
+
+ @Test
+ void registerRawCFGStoresGraphUntilCleared() {
+ GraphPool pool = GraphPool.getInstance(TEST_LOADER);
+ RawControlFlowGraph raw = simpleRawGraph(TEST_LOADER, CLASS_NAME, METHOD_NAME);
+
+ // Test that registerRawCFG and GetRawCFG works correctly
+ pool.registerRawCFG(raw);
+ assertSame(raw, pool.getRawCFG(CLASS_NAME, METHOD_NAME));
+
+ // Test that clear works correctly
+ pool.clear(CLASS_NAME, METHOD_NAME);
+ assertNull(pool.getRawCFG(CLASS_NAME, METHOD_NAME));
+ }
+
+ @Test
+ void registerActualCFGSkipsCdgWhenInstrumentationDisabled() {
+ blockingLoader = new NoExecutionTracerClassLoader(GraphPoolTest.class.getClassLoader());
+ GraphPool pool = GraphPool.getInstance(blockingLoader);
+ RawControlFlowGraph raw = simpleRawGraph(blockingLoader, CLASS_NAME, METHOD_NAME);
+ ActualControlFlowGraph cfg = new ActualControlFlowGraph(raw);
+
+ pool.registerActualCFG(cfg);
+
+ assertSame(cfg, pool.getActualCFG(CLASS_NAME, METHOD_NAME));
+ assertNull(pool.getCDG(CLASS_NAME, METHOD_NAME));
+ }
+
+ @Test
+ void resetForTestingDropsCachedInstancesAndGraphs() {
+ GraphPool pool = GraphPool.getInstance(TEST_LOADER);
+ RawControlFlowGraph raw = simpleRawGraph(TEST_LOADER, CLASS_NAME, METHOD_NAME);
+ pool.registerRawCFG(raw);
+
+ GraphPool.resetForTesting(TEST_LOADER);
+
+ GraphPool fresh = GraphPool.getInstance(TEST_LOADER);
+ assertNotSame(pool, fresh);
+ assertNull(fresh.getRawCFG(CLASS_NAME, METHOD_NAME));
+ }
+
+ private static RawControlFlowGraph simpleRawGraph(ClassLoader loader,
+ String className,
+ String methodName) {
+ TestRawControlFlowGraph raw = new TestRawControlFlowGraph(loader, className, methodName);
+
+ BytecodeInstruction entry = instruction(loader, className, methodName, 0, Opcodes.NOP);
+ BytecodeInstruction body = instruction(loader, className, methodName, 1, Opcodes.NOP);
+ BytecodeInstruction exit = instruction(loader, className, methodName, 2, Opcodes.RETURN);
+
+ raw.addVertex(entry);
+ raw.addVertex(body);
+ raw.addVertex(exit);
+
+ raw.connect(entry, body);
+ raw.connect(body, exit);
+
+ return raw;
+ }
+
+ private static BytecodeInstruction instruction(ClassLoader loader,
+ String className,
+ String methodName,
+ int instructionId,
+ int opcode) {
+ BytecodeInstruction instruction = new BytecodeInstruction(
+ loader,
+ className,
+ methodName,
+ instructionId,
+ instructionId,
+ new InsnNode(opcode)
+ );
+ instruction.setLineNumber(100 + instructionId);
+ return instruction;
+ }
+
+ private static final class TestRawControlFlowGraph extends RawControlFlowGraph {
+ private TestRawControlFlowGraph(ClassLoader loader, String className, String methodName) {
+ super(loader, className, methodName, Opcodes.ACC_PUBLIC);
+ }
+
+ private void connect(BytecodeInstruction src, BytecodeInstruction dst) {
+ super.addEdge(src, dst, false);
+ }
+ }
+
+ private static class SimpleDelegatingClassLoader extends ClassLoader {
+ private SimpleDelegatingClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+ }
+
+ private static class NoExecutionTracerClassLoader extends ClassLoader {
+ private NoExecutionTracerClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ @Override
+ protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if ("org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer".equals(name)) {
+ throw new ClassNotFoundException(name);
+ }
+ return super.loadClass(name, resolve);
+ }
+ }
+}
+
diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/ControlDependenceGraphTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/ControlDependenceGraphTest.java
new file mode 100644
index 0000000000..ca564bdc67
--- /dev/null
+++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/ControlDependenceGraphTest.java
@@ -0,0 +1,229 @@
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.GraphPool;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlDependency;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch;
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.BranchPool;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.JumpInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Tests for {@link ControlDependenceGraph}.
+ */
+class ControlDependenceGraphTest {
+
+ private static final ClassLoader TEST_LOADER = ControlDependenceGraphTest.class.getClassLoader();
+ private static final String CLASS_NAME = "com.example.DynamosaTestClass";
+ private static final String METHOD_NAME = "sample()V";
+
+ @AfterEach
+ void resetPools() {
+ resetBranchPool();
+ resetGraphPool();
+ }
+
+ @Test
+ void testConstructor() {
+ GraphFixture fixture = GraphFixture.build();
+ ControlDependenceGraph cdg = new ControlDependenceGraph(fixture.cfg);
+ assertEquals(fixture.cfg.getClassName(), cdg.getClassName());
+ assertEquals(fixture.cfg.getMethodName(), cdg.getMethodName());
+ assertEquals(fixture.cfg, cdg.getCFG());
+
+ // Synthetic entry block is the root node in the new graph.
+ BasicBlock syntheticEntry = cdg.vertexSet().stream()
+ .filter(BasicBlock::isEntryBlock)
+ .findFirst()
+ .orElseThrow(() -> new AssertionError("Synthetic entry block not found"));
+
+ // Vertexes should match the CFG minus the exit block
+ Set expectedVertices = new java.util.HashSet<>(fixture.cfg.vertexSet());
+ expectedVertices.remove(fixture.exitBlock);
+ assertEquals(expectedVertices, cdg.vertexSet());
+
+ // branch -> true path
+ // branch -> false path
+ assertTrue(cdg.containsEdge(fixture.branchBlock, fixture.trueBlock));
+ assertTrue(cdg.containsEdge(fixture.branchBlock, fixture.falseBlock));
+
+ // synthetic entry -> entry block
+ // synthetic entry -> branch block
+ assertTrue(cdg.containsEdge(syntheticEntry, fixture.entryBlock));
+ assertTrue(cdg.containsEdge(syntheticEntry, fixture.branchBlock));
+
+ }
+
+ @Test
+ void testGetControlDependentBranchesThrowsIllegalArgumentExceptionForNullBlock() {
+ GraphFixture fixture = GraphFixture.build();
+ ControlDependenceGraph cdg = new ControlDependenceGraph(fixture.cfg);
+ assertThrows(IllegalArgumentException.class, () -> cdg.getControlDependentBranches(null));
+ }
+
+ @Test
+ void testGetControlDependentBranchesThrowsUnknownBlockExceptionForABlockThaIsNotPartOfTheCFG() {
+ GraphFixture fixture = GraphFixture.build();
+ ControlDependenceGraph cdg = new ControlDependenceGraph(fixture.cfg);
+ // Here we are creating a mock block that is not part of the fixture.cfg, so
+ BasicBlock foreign = org.mockito.Mockito.mock(BasicBlock.class);
+ assertThrows(IllegalArgumentException.class, () -> cdg.getControlDependentBranches(foreign));
+ }
+
+ @Test
+ void testGetControlDependenciesReturnsTheCorrectDependenciesForEveryBlock() {
+ GraphFixture fixture = GraphFixture.build();
+ ControlDependenceGraph cdg = new ControlDependenceGraph(fixture.cfg);
+
+ // Entry block should have no dependencies
+ Set entryDeps = cdg.getControlDependentBranches(fixture.entryBlock);
+ assertTrue(entryDeps.isEmpty());
+
+ // Branch block should have no dependencies
+ Set branchDeps = cdg.getControlDependentBranches(fixture.branchBlock);
+ assertTrue(branchDeps.isEmpty());
+
+ // True block should have a dependency on the branch
+ Set trueDeps = cdg.getControlDependentBranches(fixture.trueBlock);
+ assertEquals(1, trueDeps.size());
+ assertTrue(trueDeps.contains(new ControlDependency(fixture.branch, true)));
+
+ // False block should have a dependency on the branch
+ Set falseDeps = cdg.getControlDependentBranches(fixture.falseBlock);
+ assertEquals(1, falseDeps.size());
+ assertTrue(falseDeps.contains(new ControlDependency(fixture.branch, false)));
+ }
+
+ private static void resetBranchPool() {
+ BranchPool.resetForTesting(TEST_LOADER);
+ }
+
+ private static void resetGraphPool() {
+ GraphPool.resetForTesting(TEST_LOADER);
+ }
+
+ private static final class GraphFixture {
+ final ActualControlFlowGraph cfg;
+ final BasicBlock branchBlock;
+ final BasicBlock trueBlock;
+ final BasicBlock falseBlock;
+ final BasicBlock exitBlock;
+ final Branch branch;
+ final BasicBlock entryBlock;
+
+ private GraphFixture(ActualControlFlowGraph cfg,
+ BasicBlock branchBlock,
+ BasicBlock trueBlock,
+ BasicBlock falseBlock,
+ BasicBlock exitBlock,
+ Branch branch,
+ BasicBlock entryBlock) {
+ this.cfg = cfg;
+ this.branchBlock = branchBlock;
+ this.trueBlock = trueBlock;
+ this.falseBlock = falseBlock;
+ this.exitBlock = exitBlock;
+ this.branch = branch;
+ this.entryBlock = entryBlock;
+ }
+
+ static GraphFixture build() {
+
+ // entry -> branch
+ // branch -> true path
+ // branch -> false path
+ // true path -> exit
+ // false path -> exit
+
+ TestRawControlFlowGraph raw = new TestRawControlFlowGraph();
+
+ BytecodeInstruction entry = instruction(0, Opcodes.NOP);
+ BytecodeInstruction branchInsn = branchInstruction(1);
+ BytecodeInstruction falsePath = instruction(2, Opcodes.NOP);
+ BytecodeInstruction truePath = instruction(10, Opcodes.NOP);
+ BytecodeInstruction exit = instruction(20, Opcodes.RETURN);
+
+ raw.addVertex(entry);
+ raw.addVertex(branchInsn);
+ raw.addVertex(falsePath);
+ raw.addVertex(truePath);
+ raw.addVertex(exit);
+
+ raw.connect(entry, branchInsn);
+ raw.connect(branchInsn, truePath); // jump (true)
+ raw.connect(branchInsn, falsePath); // fall-through (false)
+ raw.connect(truePath, exit);
+ raw.connect(falsePath, exit);
+
+ ActualControlFlowGraph cfg = new ActualControlFlowGraph(raw);
+
+ BasicBlock entryBlock = findBlockContaining(cfg, entry);
+ BasicBlock branchBlock = findBlockContaining(cfg, branchInsn);
+ BasicBlock trueBlock = findBlockContaining(cfg, truePath);
+ BasicBlock falseBlock = findBlockContaining(cfg, falsePath);
+ BasicBlock exitBlock = cfg.vertexSet().stream()
+ .filter(BasicBlock::isExitBlock)
+ .findFirst()
+ .orElseThrow(() -> new AssertionError("Exit block not found"));
+
+ Branch branch = BranchPool.getInstance(TEST_LOADER).getBranchForInstruction(branchInsn);
+
+ return new GraphFixture(cfg, branchBlock, trueBlock, falseBlock, exitBlock, branch, entryBlock);
+ }
+
+ private static final class TestRawControlFlowGraph extends RawControlFlowGraph {
+ private TestRawControlFlowGraph() {
+ super(TEST_LOADER, CLASS_NAME, METHOD_NAME, Opcodes.ACC_PUBLIC);
+ }
+
+ private void connect(BytecodeInstruction src, BytecodeInstruction dst) {
+ super.addEdge(src, dst, false);
+ }
+ }
+
+ private static BasicBlock findBlockContaining(ActualControlFlowGraph cfg, BytecodeInstruction instruction) {
+ return cfg.vertexSet().stream()
+ .filter(bb -> bb.containsInstruction(instruction))
+ .findFirst()
+ .orElseThrow(() -> new AssertionError("Block containing instruction not found"));
+ }
+
+ private static BytecodeInstruction instruction(int id, int opcode) {
+ BytecodeInstruction instruction = new BytecodeInstruction(
+ TEST_LOADER,
+ CLASS_NAME,
+ METHOD_NAME,
+ id,
+ id,
+ new InsnNode(opcode));
+ instruction.setLineNumber(100 + id);
+ return instruction;
+ }
+
+ private static BytecodeInstruction branchInstruction(int id) {
+ BytecodeInstruction instruction = new BytecodeInstruction(
+ TEST_LOADER,
+ CLASS_NAME,
+ METHOD_NAME,
+ id,
+ id,
+ new JumpInsnNode(Opcodes.IFEQ, new LabelNode()));
+ instruction.setLineNumber(200 + id);
+ BranchPool.getInstance(TEST_LOADER).registerAsBranch(instruction);
+ return instruction;
+ }
+ }
+}
+
+
diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorNodeTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorNodeTest.java
new file mode 100644
index 0000000000..cb32a65584
--- /dev/null
+++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorNodeTest.java
@@ -0,0 +1,85 @@
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class DominatorNodeTest {
+
+ @Test
+ void constructorSetsNodeAndLabel() {
+ DominatorNode node = new DominatorNode<>("entry");
+ assertEquals("entry", node.node);
+ assertSame(node, node.label);
+ }
+
+ @Test
+ void linkSetsAncestor() {
+ DominatorNode node = new DominatorNode<>("entry");
+ DominatorNode ancestor = new DominatorNode<>("ancestor");
+ node.link(ancestor);
+ assertSame(ancestor, node.ancestor);
+ }
+
+ @Test
+ void evalReturnsSelfWhenAncestorIsNull() {
+ DominatorNode node = new DominatorNode<>("entry");
+ assertSame(node, node.eval());
+ }
+
+ @Test
+ void evalCompressesAncestorWhenPresentAndReturnsUpdatedLabel() {
+ DominatorNode node = new DominatorNode<>("node");
+ DominatorNode ancestor = new DominatorNode<>("ancestor");
+ DominatorNode ancestorAncestor = new DominatorNode<>("ancestorAncestor");
+
+ // In the Lengauer-Tarjan algorithm used to create the DominatorTree, every visited node has a N value
+ // N is the order of discovery of the node in the DFS of the DominatorTree
+ // Smaller N means its higher in the tree (closer to the root)
+ // Label always points to the node's best dominator candidate so far (lowest N value)
+
+ // From every path on the start to the node, the node's semi-dominator is the node with the lowest N value
+ node.semiDominator = new DominatorNode<>("nodeSemi");
+ node.semiDominator.n = 10;
+
+ ancestor.semiDominator = new DominatorNode<>("ancestorSemi");
+ ancestor.semiDominator.n = 1;
+
+ // Link the nodes together to form a tree
+ node.link(ancestor);
+ ancestor.link(ancestorAncestor);
+
+ // Evaluate the node to find its immediate dominator
+ DominatorNode result = node.eval();
+
+ // eval must return the best candidate, in this case the ancestor
+ assertSame(ancestor, result);
+ // label must be updated to the best candidate
+ assertSame(ancestor, node.label);
+ // compression also flattens the ancestor chain, so node.ancestor now points
+ // directly to the ancestor's ancestor
+ assertSame(ancestorAncestor, node.ancestor);
+ }
+
+ @Test
+ void isRootNodeReturnsTrueWhenNIsOne() {
+ DominatorNode node = new DominatorNode<>("entry");
+ node.n = 1;
+ assertTrue(node.isRootNode());
+ }
+
+ @Test
+ void isRootNodeReturnsFalseWhenNGreaterThanOne() {
+ DominatorNode node = new DominatorNode<>("entry");
+ node.n = 2;
+ assertFalse(node.isRootNode());
+ }
+
+ @Test
+ void getFromBucketReturnsFirstElement() {
+ DominatorNode node = new DominatorNode<>("A");
+ DominatorNode bucketNode = new DominatorNode<>("B");
+ node.bucket.add(bucketNode);
+ assertSame(bucketNode, node.getFromBucket());
+ }
+}
diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ActualControlFlowGraphTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ActualControlFlowGraphTest.java
new file mode 100644
index 0000000000..0664bf04be
--- /dev/null
+++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ActualControlFlowGraphTest.java
@@ -0,0 +1,168 @@
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg;
+
+import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.BranchPool;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.JumpInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ActualControlFlowGraphTest {
+
+ private static final ClassLoader TEST_LOADER = ActualControlFlowGraphTest.class.getClassLoader();
+ private static final String CLASS_NAME = "com.example.ActualCFGFixture";
+
+ @AfterEach
+ void resetPools() {
+ BranchPool.resetForTesting(TEST_LOADER);
+ }
+
+ @Test
+ void constructorBuildsBasicBlocksAndBranches() {
+ GraphFixture fixture = GraphFixture.build("constructor");
+ ActualControlFlowGraph cfg = new ActualControlFlowGraph(fixture.raw);
+
+ BasicBlock entryBlock = cfg.vertexSet().stream()
+ .filter(BasicBlock::isEntryBlock)
+ .findFirst()
+ .orElseThrow(() -> new AssertionError("Synthetic entry block missing"));
+ BasicBlock exitBlock = cfg.vertexSet().stream()
+ .filter(BasicBlock::isExitBlock)
+ .findFirst()
+ .orElseThrow(() -> new AssertionError("Synthetic exit block missing"));
+
+ assertNotNull(entryBlock);
+ assertNotNull(exitBlock);
+
+ BasicBlock branchBlock = cfg.getBlockOf(fixture.branchInsn);
+ assertNotNull(branchBlock);
+
+ BasicBlock trueBlock = cfg.getBlockOf(fixture.trueInsn);
+ BasicBlock falseBlock = cfg.getBlockOf(fixture.falseInsn);
+
+ Set branchChildren = cfg.getChildren(branchBlock);
+ assertEquals(2, branchChildren.size());
+ assertTrue(branchChildren.contains(trueBlock));
+ assertTrue(branchChildren.contains(falseBlock));
+
+ assertEquals(1, cfg.getBranches().size());
+ assertTrue(cfg.getBranches().contains(fixture.branchInsn));
+ }
+
+ @Test
+ void computeReverseCFGPreservesVertexCount() {
+ GraphFixture fixture = GraphFixture.build("reverse");
+ ActualControlFlowGraph cfg = new ActualControlFlowGraph(fixture.raw);
+
+ ActualControlFlowGraph reverse = cfg.computeReverseCFG();
+
+ assertEquals(cfg.vertexCount(), reverse.vertexCount());
+ assertNotNull(reverse.getBlockOf(fixture.branchInsn));
+ }
+
+ @Test
+ void getBlockOfCachesBlocksAndGetInstructionUsesPool() {
+ GraphFixture fixture = GraphFixture.build("lookup");
+ ActualControlFlowGraph cfg = new ActualControlFlowGraph(fixture.raw);
+
+ BytecodeInstruction target = fixture.trueInsn;
+ BasicBlock first = cfg.getBlockOf(target);
+ assertNotNull(first);
+ BasicBlock second = cfg.getBlockOf(target);
+ assertSame(first, second);
+
+ BytecodeInstruction resolved = cfg.getInstruction(target.getInstructionId());
+ assertSame(target, resolved);
+ }
+
+ private static final class GraphFixture {
+ final RawControlFlowGraph raw;
+ final BytecodeInstruction branchInsn;
+ final BytecodeInstruction trueInsn;
+ final BytecodeInstruction falseInsn;
+
+ private GraphFixture(RawControlFlowGraph raw,
+ BytecodeInstruction branchInsn,
+ BytecodeInstruction trueInsn,
+ BytecodeInstruction falseInsn) {
+ this.raw = raw;
+ this.branchInsn = branchInsn;
+ this.trueInsn = trueInsn;
+ this.falseInsn = falseInsn;
+ }
+
+ static GraphFixture build(String methodSuffix) {
+ String methodName = METHOD_NAME(methodSuffix);
+ TestRawCFG raw = new TestRawCFG(methodName);
+
+ BytecodeInstruction entry = instruction(methodName, 0, Opcodes.NOP);
+ BytecodeInstruction branch = branchInstruction(methodName, 1);
+ BytecodeInstruction truePath = instruction(methodName, 2, Opcodes.NOP);
+ BytecodeInstruction falsePath = instruction(methodName, 3, Opcodes.NOP);
+ BytecodeInstruction exit = instruction(methodName, 4, Opcodes.RETURN);
+
+ raw.addVertex(entry);
+ raw.addVertex(branch);
+ raw.addVertex(truePath);
+ raw.addVertex(falsePath);
+ raw.addVertex(exit);
+
+ raw.connect(entry, branch);
+ raw.connect(branch, truePath);
+ raw.connect(branch, falsePath);
+ raw.connect(truePath, exit);
+ raw.connect(falsePath, exit);
+
+ return new GraphFixture(raw, branch, truePath, falsePath);
+ }
+
+ private static String METHOD_NAME(String suffix) {
+ return "sample_" + suffix + "()V";
+ }
+ }
+
+ private static BytecodeInstruction instruction(String methodName, int instructionId, int opcode) {
+ BytecodeInstruction instruction = new BytecodeInstruction(
+ TEST_LOADER,
+ CLASS_NAME,
+ methodName,
+ instructionId,
+ instructionId,
+ new InsnNode(opcode)
+ );
+ instruction.setLineNumber(100 + instructionId);
+ BytecodeInstructionPool.getInstance(TEST_LOADER).registerInstruction(instruction);
+ return instruction;
+ }
+
+ private static BytecodeInstruction branchInstruction(String methodName, int instructionId) {
+ BytecodeInstruction instruction = new BytecodeInstruction(
+ TEST_LOADER,
+ CLASS_NAME,
+ methodName,
+ instructionId,
+ instructionId,
+ new JumpInsnNode(Opcodes.IFEQ, new LabelNode())
+ );
+ instruction.setLineNumber(200 + instructionId);
+ BytecodeInstructionPool.getInstance(TEST_LOADER).registerInstruction(instruction);
+ BranchPool.getInstance(TEST_LOADER).registerAsBranch(instruction);
+ return instruction;
+ }
+
+ private static final class TestRawCFG extends RawControlFlowGraph {
+ private TestRawCFG(String methodName) {
+ super(TEST_LOADER, CLASS_NAME, methodName, Opcodes.ACC_PUBLIC);
+ }
+
+ private void connect(BytecodeInstruction src, BytecodeInstruction dst) {
+ super.addEdge(src, dst, false);
+ }
+ }
+}
+
diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BasicBlockTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BasicBlockTest.java
new file mode 100644
index 0000000000..e79cd12f50
--- /dev/null
+++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BasicBlockTest.java
@@ -0,0 +1,105 @@
+package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg;
+
+import org.junit.jupiter.api.Test;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.InsnNode;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class BasicBlockTest {
+
+ private static final ClassLoader TEST_LOADER = BasicBlockTest.class.getClassLoader();
+ private static final String CLASS_NAME = "com.example.BasicBlockFixture";
+ private static final String METHOD_NAME = "sample()V";
+
+ @Test
+ void constructorRejectsNullArguments() {
+ BytecodeInstruction instruction = instruction(CLASS_NAME, METHOD_NAME, 0, 100);
+ List