From 684a8f593394179fb5a29dfce7b3a3652cd7614a Mon Sep 17 00:00:00 2001 From: francastagna Date: Thu, 20 Nov 2025 08:45:23 -0300 Subject: [PATCH 01/21] wip --- .../java/controller/api/dto/SutRunDto.java | 10 + .../controller/ExternalSutController.java | 23 + .../controller/internal/EMController.java | 5 + .../controller/internal/SutController.java | 16 + .../java/instrumentation/AnnotatedLabel.java | 76 ++ .../instrumentation/AnnotatedMethodNode.java | 70 + .../java/instrumentation/Instrumentator.java | 7 + .../RemoveFinalClassAdapter.java | 77 ++ .../dynamosa/DynamosaConfig.java | 29 + .../dynamosa/graphs/EvoSuiteGraph.java | 872 ++++++++++++ .../dynamosa/graphs/GraphPool.java | 315 +++++ .../graphs/cdg/ControlDependenceGraph.java | 589 ++++++++ .../dynamosa/graphs/cdg/DominatorNode.java | 115 ++ .../dynamosa/graphs/cdg/DominatorTree.java | 311 +++++ .../dynamosa/graphs/cfg/ASMWrapper.java | 905 +++++++++++++ .../graphs/cfg/ActualControlFlowGraph.java | 843 ++++++++++++ .../dynamosa/graphs/cfg/BasicBlock.java | 528 ++++++++ .../dynamosa/graphs/cfg/BytecodeAnalyzer.java | 136 ++ .../graphs/cfg/BytecodeInstruction.java | 1193 +++++++++++++++++ .../cfg/BytecodeInstructionFactory.java | 48 + .../cfg/BytecodeInstructionIdComparator.java | 42 + .../graphs/cfg/BytecodeInstructionPool.java | 581 ++++++++ .../dynamosa/graphs/cfg/CFGClassAdapter.java | 43 + .../dynamosa/graphs/cfg/CFGFrame.java | 49 + .../dynamosa/graphs/cfg/CFGGenerator.java | 253 ++++ .../dynamosa/graphs/cfg/CFGMethodAdapter.java | 132 ++ .../graphs/cfg/ControlDependency.java | 142 ++ .../dynamosa/graphs/cfg/ControlFlowEdge.java | 164 +++ .../dynamosa/graphs/cfg/ControlFlowGraph.java | 298 ++++ .../dynamosa/graphs/cfg/EntryBlock.java | 59 + .../dynamosa/graphs/cfg/ExitBlock.java | 59 + .../graphs/cfg/RawControlFlowGraph.java | 487 +++++++ .../dynamosa/graphs/cfg/branch/Branch.java | 310 +++++ .../graphs/cfg/branch/BranchPool.java | 834 ++++++++++++ .../external/AgentController.java | 20 + .../instrumentation/external/Command.java | 3 +- .../external/DynamosaConfigDto.java | 10 + .../external/ServerController.java | 4 + .../kotlin/org/evomaster/core/EMConfig.kt | 9 +- .../service/RemoteControllerImplementation.kt | 4 + 40 files changed, 9669 insertions(+), 2 deletions(-) create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AnnotatedLabel.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AnnotatedMethodNode.java create mode 100644 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/RemoveFinalClassAdapter.java create mode 100644 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/DynamosaConfig.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoSuiteGraph.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/GraphPool.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/ControlDependenceGraph.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorNode.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorTree.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ASMWrapper.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ActualControlFlowGraph.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BasicBlock.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeAnalyzer.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstruction.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionFactory.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionIdComparator.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionPool.java create mode 100644 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGClassAdapter.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGFrame.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGGenerator.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGMethodAdapter.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlDependency.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowEdge.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowGraph.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/EntryBlock.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ExitBlock.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/RawControlFlowGraph.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/Branch.java create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchPool.java create mode 100644 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/DynamosaConfigDto.java diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java index 95f8149f45..e2039bc8f3 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java @@ -45,6 +45,16 @@ public class SutRunDto { */ public String methodReplacementCategories; + /** + * Whether to enable Dynamosa graphs generation in the Java agent + */ + public Boolean enableDynamosaGraphs; + + /** + * Whether to write generated graphs (DOT/PNGs) to disk on the agent side + */ + public Boolean writeCfg; + public SutRunDto() { } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java index 08c08b5814..7a4a13d76b 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java @@ -12,6 +12,7 @@ import org.evomaster.client.java.controller.internal.SutController; import org.evomaster.client.java.instrumentation.external.JarAgentLocator; import org.evomaster.client.java.instrumentation.external.ServerController; +import org.evomaster.client.java.instrumentation.external.DynamosaConfigDto; import java.io.BufferedReader; import java.io.IOException; @@ -522,6 +523,28 @@ public final void setExecutingInitMongo(boolean executingInitMongo) { ExecutionTracer.setExecutingInitMongo(executingInitMongo); } + /** + * Send Dynamosa configuration to the Java Agent. + */ + public final void setDynamosaGraphsEnabled(boolean enableGraphs) { + checkInstrumentation(); + DynamosaConfigDto dto = new DynamosaConfigDto(); + dto.enableGraphs = enableGraphs; + dto.writeCfg = null; + serverController.setDynamosaConfig(dto); + } + + /** + * Control whether the agent writes DOT/PNG graphs to disk. + */ + public final void setWriteCfgEnabled(boolean writeCfg) { + checkInstrumentation(); + DynamosaConfigDto dto = new DynamosaConfigDto(); + dto.enableGraphs = null; + dto.writeCfg = writeCfg; + serverController.setDynamosaConfig(dto); + } + @Override public final void setExecutingAction(boolean executingAction){ checkInstrumentation(); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java index ea08188418..39293a790c 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java @@ -377,6 +377,11 @@ public Response runSut(SutRunDto dto, @Context HttpServletRequest httpServletReq if (!noKillSwitch(() -> sutController.isSutRunning())) { noKillSwitch(() -> sutController.bootingSut(true)); baseUrlOfSUT = noKillSwitch(() -> sutController.startSut()); + // Configure Dynamosa graphs on the agent, if requested by core + Boolean enableGraphs = dto.enableDynamosaGraphs; + if (enableGraphs != null) noKillSwitch(() -> sutController.setDynamosaGraphsEnabled(enableGraphs)); + Boolean writeCfg = dto.writeCfg; + if (writeCfg != null) noKillSwitch(() -> sutController.setWriteCfgEnabled(writeCfg)); noKillSwitch(() -> sutController.bootingSut(false)); if (baseUrlOfSUT == null) { //there has been an internal failure in starting the SUT diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index e6f64b89bf..0a610bc74f 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -1564,6 +1564,22 @@ public abstract List getTargetInfos(Collection ids, public abstract void setExecutingAction(boolean executingAction); + /** + * Enable/disable Dynamosa graphs generation in the Java agent. + * Default no-op; external controllers can override to send to agent. + */ + public void setDynamosaGraphsEnabled(boolean enableGraphs){ + // no-op by default + } + + /** + * Enable/disable writing graphs to disk in the Java agent. + * Default no-op; external controllers can override to send to agent. + */ + public void setWriteCfgEnabled(boolean writeCfg){ + // no-op by default + } + /** * specify whether the SUT is booting (ie starting up), or not. diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AnnotatedLabel.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AnnotatedLabel.java new file mode 100755 index 0000000000..05d593279f --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AnnotatedLabel.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ + +package org.evomaster.client.java.instrumentation; + +import org.objectweb.asm.Label; +import org.objectweb.asm.tree.LabelNode; + +/** + * Annotated labels are used to identify instrumented code + * such that EvoSuite knows how to deal with + * + * @author fraser + */ +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/AnnotatedMethodNode.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AnnotatedMethodNode.java new file mode 100755 index 0000000000..9da154dd6a --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AnnotatedMethodNode.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ + +package org.evomaster.client.java.instrumentation; + +import org.objectweb.asm.Label; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.MethodNode; + +/** + *

AnnotatedMethodNode class.

+ * + * @author fraser + */ +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/Instrumentator.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/Instrumentator.java index 2853537d93..6e92256bec 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.graphs.cfg.CFGClassAdapter; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; @@ -71,6 +73,11 @@ public byte[] transformBytes(ClassLoader classLoader, ClassName className, Class cv = new ThirdPartyClassVisitor(cv, className); } + boolean dynamosaGraphs = DynamosaConfig.isGraphsEnabled(); + if (dynamosaGraphs) { + cv = new CFGClassAdapter(classLoader, cv); + } + try { cn.accept(cv); } catch(Throwable e){ diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/RemoveFinalClassAdapter.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/RemoveFinalClassAdapter.java new file mode 100644 index 0000000000..a87416c89d --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/RemoveFinalClassAdapter.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.instrumentation; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import java.util.LinkedHashSet; +import java.util.Set; + +public class RemoveFinalClassAdapter extends ClassVisitor { + + public static final Set finalClasses = new LinkedHashSet<>(); + + public RemoveFinalClassAdapter(ClassVisitor cv) { + super(Opcodes.ASM9, cv); + } + + /** + * Remove "final" accessor from class definition + */ + @Override + public void visit(int version, int access, String name, String signature, + String superName, String[] interfaces) { + if ((access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL) { + finalClasses.add(name.replace('/', '.')); + } + if ((access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT && + (access & Opcodes.ACC_PUBLIC) == 0 && + (access & Opcodes.ACC_PRIVATE) == 0 && + (access & Opcodes.ACC_PROTECTED) == 0) { + // If a class is abstract and default-accessible, then we make it public to allow + // Mockito to create mocks + super.visit(version, (access & ~Opcodes.ACC_FINAL) | Opcodes.ACC_PUBLIC, name, signature, superName, interfaces); + } else { + super.visit(version, access & ~Opcodes.ACC_FINAL, name, signature, superName, interfaces); + } + } + + /** + * Remove "final" accessor from inner class definition + */ + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + if ((access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL) { + finalClasses.add(name.replace('/', '.')); + } + super.visitInnerClass(name, outerName, innerName, access & ~Opcodes.ACC_FINAL); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + return super.visitMethod(access & ~Opcodes.ACC_FINAL, name, desc, signature, exceptions); + } + + public static void reset() { + finalClasses.clear(); + } +} 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..6b98bf172f --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/DynamosaConfig.java @@ -0,0 +1,29 @@ +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() { + return enableGraphs; + } + + public static void setEnableGraphs(boolean value) { + enableGraphs = value; + } + + public static boolean isWriteCfgEnabled() { + return writeCfg; + } + + public static void setWriteCfgEnabled(boolean value) { + writeCfg = value; + } +} + + diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoSuiteGraph.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoSuiteGraph.java new file mode 100755 index 0000000000..15a2f8d826 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoSuiteGraph.java @@ -0,0 +1,872 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +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; + +/** + * Supposed to become the super class of all kinds of graphs used within + * EvoSuite Examples are the raw and minimal Control Flow Graph and hopefully at + * one point the Control Dependency Tree + *

+ * This class is supposed to hide the jGraph library from the rest of EvoSuite + * and is supposed to serve as an interface for all kinds of primitive graph- + * functionality such as asking for information about the nodes and edges of the + * graph and the relations between them. + *

+ * Hopefully at some point only this class and it's sub classes are the only + * files in EvoSuite that import anything from the jGraph library - at least + * that's the idea This is very similar to the way cfg.ASMWrapper is supposed to + * hide the ASM library and serve as an interface for BytecodeInstrucions + *

+ * So most of this class' methods are just wrappers that redirect the specific + * call to the corresponding jGraph-method + *

+ * For now an EvoSuiteGraph can always be represented by a DefaultDirectedGraph + * from the jGraph library - that is a directed graph not allowed to contain + * multiple edges between to nodes but allowed to contain cycles + * + * @author Andre Mis + */ +public abstract class EvoSuiteGraph { + + private static int evoSuiteGraphs = 0; + protected int graphId; + + protected DirectedGraph graph; + protected Class edgeClass; + + // for .dot functionality + // TODO need jgrapht-0.8.3 + ComponentAttributeProvider vertexAttributeProvider = null; + ComponentAttributeProvider edgeAttributeProvider = null; + + /** + *

Constructor for EvoSuiteGraph.

+ * + * @param edgeClass a {@link java.lang.Class} object. + */ + protected EvoSuiteGraph(Class edgeClass) { + + graph = new DefaultDirectedGraph<>(edgeClass); + this.edgeClass = edgeClass; + + setId(); + } + + /** + *

Constructor for EvoSuiteGraph.

+ * + * @param graph a {@link org.jgrapht.DirectedGraph} object. + * @param edgeClass a {@link java.lang.Class} object. + */ + protected EvoSuiteGraph(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 + + /** + *

addVertices

+ * + * @param other a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.EvoSuiteGraph} object. + */ + protected void addVertices(EvoSuiteGraph 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 "EvoSuiteGraph_" + 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..d046220912 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/GraphPool.java @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.instrumentation.dynamosa.graphs; + +import org.evomaster.client.java.instrumentation.ClassesToExclude; +import org.evomaster.client.java.instrumentation.shared.ClassName; +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.RawControlFlowGraph; +import org.evomaster.client.java.instrumentation.dynamosa.DynamosaConfig; +import org.evomaster.client.java.utils.SimpleLogger; + +import java.util.HashMap; +import java.util.Map; + +/** + * Gives access to all Graphs computed during CUT analysis such as CFGs created + * by the CFGGenerator and BytcodeAnalyzer in the CFGMethodAdapter + *

+ * 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. + * + * @author Andre Mis + */ +public class GraphPool { + + private static final Map instanceMap = new HashMap<>(); + + 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); + } + + /** + * 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); + } + + /** + *

+ * Getter for the field rawCFGs. + *

+ * + * @param className a {@link java.lang.String} object. + * @return a {@link java.util.Map} object. + */ + public Map getRawCFGs(String className) { + if (rawCFGs.get(className) == null) { + SimpleLogger.warn("Class unknown: " + className); + SimpleLogger.warn(rawCFGs.keySet().toString()); + return null; + } + + return rawCFGs.get(className); + } + + /** + *

+ * 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); + } + + // 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); + cfg.finalise(); + 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(); + } + + /** + *

+ * 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(); + } + +} 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..fd6fe9b751 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/ControlDependenceGraph.java @@ -0,0 +1,589 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg; + +import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch; +import org.evomaster.client.java.instrumentation.dynamosa.graphs.EvoSuiteGraph; +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 EvoSuiteGraph { + + 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(); + // TODO check sanity + } + + /** + * Convenience method redirecting to getControlDependentBranches(BasicBlock) + * if the given instruction is known to this CDG. Otherwise an + * IllegalArgumentException will be thrown. + * + * Should no longer be used: rather ask a BasicBlock for its CDs, so it can + * cache it. + */ + // public Set + // getControlDependentBranches(BytecodeInstruction ins) { + // if (ins == null) + // throw new IllegalArgumentException("null not accepted"); + // if (!knowsInstruction(ins)) + // throw new IllegalArgumentException( + // "instruction not known to this CDG: " + methodName + // + ins.toString()); + // + // BasicBlock insBlock = ins.getBasicBlock(); + // + // return getControlDependentBranches(insBlock); + // } + + /** + * Checks whether this graph knows the given instruction. That is there is a + * BasicBlock in this graph's vertexSet containing the given instruction. + * + * @param ins a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @return a boolean. + */ + public boolean knowsInstruction(BytecodeInstruction ins) { + return cfg.knowsInstruction(ins); + } + + /** + *

getControlDependenceDepth

+ * + * @param dependence a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlDependency} object. + * @return a int. + */ + public int getControlDependenceDepth(ControlDependency dependence) { + int min = Integer.MAX_VALUE; + for (BasicBlock root : determineEntryPoints()) { + int distance = getDistance(root, + dependence.getBranch().getInstruction().getBasicBlock()); + if (distance < min) + min = distance; + } + return min; + } + + /** + *

getAlternativeBlocks

+ * + * @param dependency a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlDependency} object. + * @return a {@link java.util.Set} object. + */ + public Set getAlternativeBlocks(ControlDependency dependency) { + Set blocks = new LinkedHashSet<>(); + Branch branch = dependency.getBranch(); + + BasicBlock block = branch.getInstruction().getBasicBlock(); + for (ControlFlowEdge e : outgoingEdgesOf(block)) { + // TODO: Why can this be null? + if (e.getControlDependency() == null + || e.getControlDependency().equals(dependency)) + continue; + BasicBlock next = getEdgeTarget(e); + blocks.add(next); + getReachableBasicBlocks(blocks, next); + // blocks.addAll(getReachableBasicBlocks(next)); + } + return blocks; + } + + private void getReachableBasicBlocks(Set blocks, BasicBlock start) { + for (ControlFlowEdge e : outgoingEdgesOf(start)) { + BasicBlock next = getEdgeTarget(e); + if (!blocks.contains(next)) { + blocks.add(next); + getReachableBasicBlocks(blocks, next); + // blocks.addAll(getReachableBasicBlocks(next)); + } + } + // return blocks; + } + + /** + * 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)); + } + + } + + // TODO need RootBranch Object!!! + // TODO the following does not hold! a node can be dependent on the root + // branch AND another branch! TODO !!! + // // sanity check + // if (r.isEmpty()) { + // Set insParents = getParents(insBlock); + // if (insParents.size() != 1) { + // + // for (BasicBlock b : insParents) + // logger.error(b.toString()); + // + // throw new IllegalStateException( + // "expect instruction dependent on root branch to have exactly one parent in it's CDG namely the EntryBlock: " + // + insBlock.toString()); + // } + // + // for (BasicBlock b : insParents) + // if (!b.isEntryBlock() && !getControlDependentBranches(b).isEmpty()) + // throw new IllegalStateException( + // "expect instruction dependent on root branch to have exactly one parent in it's CDG namely the EntryBlock" + // + insBlock.toString() + methodName); + // } + + return r; + } + + /** + *

getControlDependentBranchIds

+ * + * @param ins a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. + * @return a {@link java.util.Set} object. + */ + public Set getControlDependentBranchIds(BasicBlock ins) { + + Set dependentBranches = getControlDependentBranches(ins); + + Set r = new LinkedHashSet<>(); + + for (ControlDependency cd : dependentBranches) { + if (cd == null) + throw new IllegalStateException( + "expect set returned by getControlDependentBranches() not to contain null"); + + r.add(cd.getBranch().getActualBranchId()); + } + + // to indicate this is only dependent on root branch, + // meaning entering the method + if (isRootDependent(ins)) + r.add(-1); + + return r; + } + + // /** + // * Determines whether the given Branch has to be evaluated to true or to + // * false in order to reach the given BytecodeInstruction - given the + // * instruction is directly control dependent on the given Branch. + // * + // * In other words this method checks whether there is an incoming + // * ControlFlowEdge to the given instruction's BasicBlock containing the + // * given Branch as it's BranchInstruction and if so, that edges + // * branchExpressionValue is returned. If the given instruction is directly + // * control dependent on the given branch such a ControlFlowEdge must + // exist. + // * Should this assumption be violated an IllegalStateException is thrown. + // * + // * If the given instruction is not known to this CDG or not directly + // control + // * dependent on the given Branch an IllegalArgumentException is thrown. + // */ + // public boolean getBranchExpressionValue(BytecodeInstruction ins, Branch + // b) { + // if (ins == null) + // throw new IllegalArgumentException("null given"); + // if (!ins.isDirectlyControlDependentOn(b)) + // throw new IllegalArgumentException( + // "only allowed to call this method for instructions and their directly control dependent branches"); + // if (b == null) + // return true; // root branch special case + // + // BasicBlock insBlock = ins.getBasicBlock(); + // + // for (ControlFlowEdge e : incomingEdgesOf(insBlock)) { + // if (e.isExceptionEdge() && !e.hasControlDependency()) + // continue; + // + // Branch current = e.getBranchInstruction(); + // if (current == null) { + // try { + // BasicBlock in = getEdgeSource(e); + // return getBranchExpressionValue(in.getFirstInstruction(), b); + // } catch (Exception ex) { + // continue; + // } + // } else if (current.equals(b)) + // return e.getBranchExpressionValue(); + // } + // + // throw new IllegalStateException( + // "expect CDG to contain an incoming edge to the given instructions basic block containing the given branch if isControlDependent() returned true on those two "); + // } + + // initialization + + /** + * Determines whether the given BytecodeInstruction is directly control + * dependent on the given Branch. It's BasicBlock is control dependent on + * the given Branch. + *

+ * If b is null, it is assumed to be the root branch. + *

+ * If the given instruction is not known to this CDG an + * IllegalArgumentException is thrown. + * + * @param ins a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @param b a {@link org.evomaster.client.java.coverage.branch.Branch} object. + * @return a boolean. + */ + public boolean isDirectlyControlDependentOn(BytecodeInstruction ins, Branch b) { + if (ins == null) + throw new IllegalArgumentException("null given"); + + BasicBlock insBlock = ins.getBasicBlock(); + + return isDirectlyControlDependentOn(insBlock, b); + } + + /** + * Determines whether the given BasicBlock is directly control dependent on + * the given Branch. Meaning within this CDG there is an incoming + * ControlFlowEdge to this instructions BasicBlock holding the given Branch + * as it's branchInstruction. + *

+ * If b is null, it is assumed to be the root branch. + *

+ * If the given instruction is not known to this CDG an + * IllegalArgumentException is thrown. + * + * @param insBlock a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. + * @param b a {@link org.evomaster.client.java.coverage.branch.Branch} object. + * @return a boolean. + */ + public boolean isDirectlyControlDependentOn(BasicBlock insBlock, Branch b) { + Set incomming = incomingEdgesOf(insBlock); + + if (incomming.size() == 1) { + // in methods with a try-catch-block it is possible that there + // are nodes in the CDG that have exactly one parent with an + // edge without a branchInstruction that is a non exceptional + // edge + // should the given instruction be such a node, follow the parents + // until + // you reach one where the above conditions are not met + + for (ControlFlowEdge e : incomming) { + if (!e.hasControlDependency() && !e.isExceptionEdge()) { + return isDirectlyControlDependentOn(getEdgeSource(e), b); + } + } + } + + boolean isRootDependent = isRootDependent(insBlock); + if (b == null) + return isRootDependent; + if (isRootDependent && b != null) + return false; + + for (ControlFlowEdge e : incomming) { + Branch current = e.getBranchInstruction(); + + if (e.isExceptionEdge()) { + if (current != null) + throw new IllegalStateException( + "expect exception edges to have no BranchInstruction set"); + else + continue; + } + + if (current == null) + continue; + // throw new IllegalStateException( + // "expect non exceptional ControlFlowEdges whithin the CDG that don't come from EntryBlock to have branchInstructions set " + // + insBlock.toString() + methodName); + + if (current.equals(b)) + return true; + } + + return false; + + } + + /** + * Checks whether the given instruction is dependent on the root branch of + * it's method + *

+ * This is the case if the BasicBlock of the given instruction is directly + * adjacent to the EntryBlock + * + * @param ins a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @return a boolean. + */ + public boolean isRootDependent(BytecodeInstruction ins) { + + return isRootDependent(ins.getBasicBlock()); + } + + /** + * Checks whether the given basicBlock is dependent on the root branch of + * it's method + *

+ * This is the case if the BasicBlock of the given instruction is directly + * adjacent to the EntryBlock + * + * @param insBlock a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. + * @return a boolean. + */ + public boolean isRootDependent(BasicBlock insBlock) { + if (isAdjacentToEntryBlock(insBlock)) + return true; + + for (ControlFlowEdge in : incomingEdgesOf(insBlock)) { + if (in.hasControlDependency()) + continue; + BasicBlock inBlock = getEdgeSource(in); + if (inBlock.equals(insBlock)) + continue; + + if (isRootDependent(inBlock)) + return true; + } + + return false; + + } + + /** + * Returns true if the given BasicBlock has an incoming edge from this CDG's + * EntryBlock or is itself the EntryBlock + * + * @param insBlock a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. + * @return a boolean. + */ + public boolean isAdjacentToEntryBlock(BasicBlock insBlock) { + + if (insBlock.isEntryBlock()) + return true; + + Set parents = getParents(insBlock); + for (BasicBlock parent : parents) + if (parent.isEntryBlock()) + return true; + + return false; + } + + // /** + // * If the given instruction is known to this graph, the BasicBlock holding + // * that instruction is returned. Otherwise an IllegalArgumentException + // will + // * be thrown. + // * + // * Just a convenience method that more or less just redirects the call to + // * the CFG + // */ + // public BasicBlock getBlockOf(BytecodeInstruction ins) { + // if (ins == null) + // throw new IllegalArgumentException("null given"); + // if (!cfg.knowsInstruction(ins)) + // throw new IllegalArgumentException("unknown instruction"); + // + // BasicBlock insBlock = cfg.getBlockOf(ins); + // if (insBlock == null) + // throw new IllegalStateException( + // "expect CFG to return non-null BasicBlock for instruction it knows"); + // + // return insBlock; + // } + + // init + + private void computeGraph() { + + createGraphNodes(); + computeControlDependence(); + } + + private void createGraphNodes() { + // copy CFG nodes + addVertices(cfg); + + 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. TODO TRY! + + 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; + } +} 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..8b59cb4274 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorNode.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.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 + * + * @author Andre Mis + */ +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..a53308e503 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorTree.java @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg; + +import org.evomaster.client.java.instrumentation.dynamosa.graphs.EvoSuiteGraph; +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 + * + * @author Andre Mis + */ +public class DominatorTree extends EvoSuiteGraph, 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"); + // TODO more sanity checks - no one likes to be insane ;) + } + + 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/ASMWrapper.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ASMWrapper.java new file mode 100755 index 0000000000..9b0a1df0e6 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ASMWrapper.java @@ -0,0 +1,905 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.*; +import org.objectweb.asm.util.Printer; + +// TODO: the following methods about control dependence are flawed right now: +// - the BytecodeInstruction of a Branch does not have it's control dependent +// branchId +// but it's own branchId set +// - this seems to be OK for ChromosomeRecycling as it stands, but +// especially getControlDependentBranch() will fail hard when called on a Branch +// the same may hold for the other ones as well. +// - look at BranchCoverageGoal and Branch for more information + +/** + * Wrapper class for the underlying byteCode instruction library ASM + *

+ * Gives access to a lot of methods that interpret the raw information in + * AbstractInsnNode to usable chunks of information, inside EvoSuite + *

+ * This class is supposed to hide the ASM library from the rest of EvoSuite as + * much as possible + *

+ * After initialization, all information about byteCode instructions should be + * accessible via the BytecodeInstruction-, DefUse- and BranchPool. Each of + * those has data structures holding all BytecodeInstruction, DefUse and Branch + * objects respectively created during initialization. + *

+ * BytecodeInstruction directly extends ASMWrapper and is the first way to + * instantiate an ASMWrapper. Branch and DefUse extend BytecodeInstruction, + * where DefUse is abstract and Branch is not ("concrete"?) DefUse is further + * extended by Definition and Use + * + * @author Andre Mis + */ +public abstract class ASMWrapper { + + // from ASM library + protected AbstractInsnNode asmNode; + protected CFGFrame frame; + + /** + *

+ * 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(); + } + + public String getMethodCallDescriptor() { + if (!isMethodCall()) + return null; + MethodInsnNode meth = (MethodInsnNode) asmNode; + return meth.desc; + } + + /** + *

+ * 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; + } + + /** + *

+ * getInstructionId + *

+ * + * @return a int. + */ + public abstract int getInstructionId(); + + /** + *

+ * getMethodName + *

+ * + * @return a {@link java.lang.String} object. + */ + public abstract String getMethodName(); + + /** + *

+ * getClassName + *

+ * + * @return a {@link java.lang.String} object. + */ + public abstract String getClassName(); + + public abstract boolean isMethodCallOfField(); + + public abstract String getFieldMethodCallName(); + + // methods for branch analysis + + /** + *

+ * isActualBranch + *

+ * + * @return a boolean. + */ + public boolean isActualBranch() { + return isBranch() || isSwitch(); + } + + /** + *

+ * isSwitch + *

+ * + * @return a boolean. + */ + public boolean isSwitch() { + return isLookupSwitch() || isTableSwitch(); + } + + /** + *

+ * 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; + } + + /** + *

+ * isTableSwitch + *

+ * + * @return a boolean. + */ + public boolean isTableSwitch() { + return (asmNode instanceof TableSwitchInsnNode); + } + + /** + *

+ * isLookupSwitch + *

+ * + * @return a boolean. + */ + public boolean isLookupSwitch() { + return (asmNode instanceof LookupSwitchInsnNode); + } + + /** + *

+ * isBranchLabel + *

+ * + * @return a boolean. + */ + public boolean isBranchLabel() { + return asmNode instanceof LabelNode + && ((LabelNode) asmNode).getLabel().info instanceof Integer; + } + + /** + *

+ * isJump + *

+ * + * @return a boolean. + */ + public boolean isJump() { + return (asmNode instanceof JumpInsnNode); + } + + /** + *

+ * isInvokeStatic + *

+ * + * @return a boolean representing whether the instruction is a static method + * call. + */ + public boolean isInvokeStatic() { + if (asmNode instanceof MethodInsnNode) { + return (asmNode.getOpcode() == Opcodes.INVOKESTATIC); + } + return false; + } + + /** + *

+ * 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()); + } + + // public int getBranchId() { + // // return ((Integer)((LabelNode)node).getLabel().info).intValue(); + // return line_no; + // } + + /** + *

+ * isIfNull + *

+ * + * @return a boolean. + */ + public boolean isIfNull() { + if (asmNode instanceof JumpInsnNode) { + return (asmNode.getOpcode() == Opcodes.IFNULL); + } + return false; + } + + /** + *

+ * isFrame + *

+ * + * @return a boolean. + */ + public boolean isFrame() { + return asmNode instanceof FrameNode; + } + + /** + *

+ * isMethodCall + *

+ * + * @return a boolean. + */ + public boolean isMethodCall() { + return asmNode instanceof MethodInsnNode; + } + + /** + * Returns the conjunction of the name and method descriptor of the method + * called by this instruction + * + * @return a {@link java.lang.String} object. + */ + public String getCalledMethod() { + if (!isMethodCall()) + return null; + MethodInsnNode meth = (MethodInsnNode) asmNode; + return meth.name + meth.desc; + } + + /** + * Returns the conjunction of the name of the method called by this + * instruction + * + * @return a {@link java.lang.String} object. + */ + public String getCalledMethodName() { + if (!isMethodCall()) + return null; + MethodInsnNode meth = (MethodInsnNode) asmNode; + return meth.name; + } + + /** + * Returns true if and only if the class of the method called by this + * instruction is the same as the given className + * + * @param className a {@link java.lang.String} object. + * @return a boolean. + */ + public boolean isMethodCallForClass(String className) { + if (isMethodCall()) { + // System.out.println("in isMethodCallForClass of "+toString()+" for arg "+className+" calledMethodsClass: "+getCalledMethodsClass()+" calledMethod "+getCalledMethod()); + return getCalledMethodsClass().equals(className); + } + return false; + } + + /** + * Returns the class of the method called by this instruction + * + * @return a {@link java.lang.String} object. + */ + public String getCalledMethodsClass() { + if (isMethodCall()) { + MethodInsnNode mn = (MethodInsnNode) asmNode; + return mn.owner.replaceAll("/", "\\."); + } + return null; + } + + /** + * Returns the number of arguments of the method called by this instruction + * + * @return a int. + */ + public int getCalledMethodsArgumentCount() { + if (isMethodCall()) { + // int r = 0; + MethodInsnNode mn = (MethodInsnNode) asmNode; + Type[] argTypes = Type.getArgumentTypes(mn.desc); + + return argTypes.length; + // for(Type argType : argTypes) { + // r+=argType.getSize(); + // } + // return r; + } + return -1; + } + + /** + *

+ * isLoadConstant + *

+ * + * @return a boolean. + */ + public boolean isLoadConstant() { + return asmNode.getOpcode() == Opcodes.LDC; + } + + /** + *

+ * isConstant + *

+ * + * @return a boolean. + */ + public boolean isConstant() { + switch (asmNode.getOpcode()) { + case Opcodes.LDC: + case Opcodes.ICONST_0: + case Opcodes.ICONST_1: + case Opcodes.ICONST_2: + case Opcodes.ICONST_3: + case Opcodes.ICONST_4: + case Opcodes.ICONST_5: + case Opcodes.ICONST_M1: + case Opcodes.LCONST_0: + case Opcodes.LCONST_1: + case Opcodes.DCONST_0: + case Opcodes.DCONST_1: + case Opcodes.FCONST_0: + case Opcodes.FCONST_1: + case Opcodes.FCONST_2: + case Opcodes.BIPUSH: + case Opcodes.SIPUSH: + return true; + default: + return false; + } + } + + // methods for defUse analysis + + /** + *

+ * isDefUse + *

+ * + * @return a boolean. + */ + public boolean isDefUse() { + return isDefinition() || isUse(); + } + + /** + *

+ * isFieldDU + *

+ * + * @return a boolean. + */ + public boolean isFieldDU() { + return isFieldDefinition() || isFieldUse(); + } + + public boolean isFieldNodeDU() { + return isFieldNodeDefinition() || isFieldNodeUse(); + } + + /** + *

+ * isLocalDU + *

+ * + * @return a boolean. + */ + public boolean isLocalDU() { + return isLocalVariableDefinition() || isLocalVariableUse(); + } + + /** + *

+ * isDefinition + *

+ * + * @return a boolean. + */ + public boolean isDefinition() { + return isFieldDefinition() || isLocalVariableDefinition(); + } + + /** + *

+ * isUse + *

+ * + * @return a boolean. + */ + public boolean isUse() { + return isFieldUse() || isLocalVariableUse() || isArrayLoadInstruction(); + } + + /** + *

+ * isFieldDefinition + *

+ * + * @return a boolean. + */ + public boolean isFieldDefinition() { + return asmNode.getOpcode() == Opcodes.PUTFIELD + || asmNode.getOpcode() == Opcodes.PUTSTATIC + || isFieldArrayDefinition(); + } + + /** + *

+ * isFieldDefinition + *

+ * + * @return a boolean. + */ + public boolean isFieldNodeDefinition() { + return asmNode.getOpcode() == Opcodes.PUTFIELD + || asmNode.getOpcode() == Opcodes.PUTSTATIC || isFieldArrayDefinition(); + } + + /** + *

+ * isFieldUse + *

+ * + * @return a boolean. + */ + public boolean isFieldUse() { + return asmNode.getOpcode() == Opcodes.GETFIELD + || asmNode.getOpcode() == Opcodes.GETSTATIC; + } + + /** + *

+ * isFieldUse + *

+ * + * @return a boolean. + */ + public boolean isFieldNodeUse() { + return asmNode.getOpcode() == Opcodes.GETFIELD + || asmNode.getOpcode() == Opcodes.GETSTATIC; + } + + /** + *

+ * isStaticDefUse + *

+ * + * @return a boolean. + */ + public boolean isStaticDefUse() { + return asmNode.getOpcode() == Opcodes.PUTSTATIC + || asmNode.getOpcode() == Opcodes.GETSTATIC || isStaticArrayUsage(); + } + + // retrieving information about variable names from ASM + + /** + *

+ * getDUVariableName + *

+ * + * @return a {@link java.lang.String} object. + */ + public String getVariableName() { + if (isArrayStoreInstruction()) { + return getArrayVariableName(); + } else if (isArrayLoadInstruction()) { + return getArrayVariableName(); + } else if (isLocalDU()) { + return getLocalVariableName(); + } else if (isMethodCallOfField()) { + return getFieldMethodCallName(); + } else if (isFieldDU()) { + return getFieldName(); + } else { + return null; + } + } + + /** + *

+ * getFieldName + *

+ * + * @return a {@link java.lang.String} object. + */ + protected String getFieldSimpleName() { + FieldInsnNode fieldNode = (FieldInsnNode) asmNode; + return fieldNode.name; + // return fieldNode.name; + } + + /** + *

+ * getFieldName + *

+ * + * @return a {@link java.lang.String} object. + */ + protected String getFieldName() { + FieldInsnNode fieldNode = (FieldInsnNode) asmNode; + return fieldNode.owner + "." + fieldNode.name; + // return fieldNode.name; + } + + /** + *

+ * getFieldName + *

+ * + * @return a {@link java.lang.String} object. + */ + public String getFieldType() { + FieldInsnNode fieldNode = (FieldInsnNode) asmNode; + return fieldNode.desc; + // return fieldNode.name; + } + + /** + *

+ * getLocalVarName + *

+ * + * @return a {@link java.lang.String} object. + */ + protected String getLocalVariableName() { + String ret = getMethodName() + "_LV_" + getLocalVariableSlot(); + return ret; + } + + // TODO unsafe + + /** + *

+ * getLocalVar + *

+ * + * @return a int. + */ + public int getLocalVariableSlot() { + if (asmNode instanceof VarInsnNode) + return ((VarInsnNode) asmNode).var; + else if (asmNode instanceof IincInsnNode) + return ((IincInsnNode) asmNode).var; + else + return -1; + } + + /** + *

+ * isLocalVarDefinition + *

+ * + * @return a boolean. + */ + public boolean isLocalVariableDefinition() { + return asmNode.getOpcode() == Opcodes.ISTORE + || asmNode.getOpcode() == Opcodes.LSTORE + || asmNode.getOpcode() == Opcodes.FSTORE + || asmNode.getOpcode() == Opcodes.DSTORE + || asmNode.getOpcode() == Opcodes.ASTORE + || asmNode.getOpcode() == Opcodes.IINC || isLocalArrayDefinition(); + } + + /** + *

+ * isLocalVarUse + *

+ * + * @return a boolean. + */ + public boolean isLocalVariableUse() { + return asmNode.getOpcode() == Opcodes.ILOAD + || asmNode.getOpcode() == Opcodes.LLOAD + || asmNode.getOpcode() == Opcodes.FLOAD + || asmNode.getOpcode() == Opcodes.DLOAD + || asmNode.getOpcode() == Opcodes.IINC + || (asmNode.getOpcode() == Opcodes.ALOAD && !loadsReferenceToThis()); + } + + public boolean isIINC() { + return asmNode.getOpcode() == Opcodes.IINC; + } + + /** + * Determines whether this is the special ALOAD that pushes 'this' onto the + * stack + *

+ * In non static methods the variable slot 0 holds the reference to "this". + * Loading this reference is not seen as a Use for defuse purposes. This + * method checks if this is the case + * + * @return a boolean. + */ + public boolean loadsReferenceToThis() { + if (getRawCFG().isStaticMethod()) { + return false; + } + + return asmNode.getOpcode() == Opcodes.ALOAD && getLocalVariableSlot() == 0; + } + + public abstract RawControlFlowGraph getRawCFG(); + + public boolean isLocalArrayDefinition() { + if (!isArrayStoreInstruction()) + return false; + + // only local var if arrayref on stack is from local var use + BytecodeInstruction arrayLoader = getSourceOfArrayReference(); + if (arrayLoader == null) + return false; + + return arrayLoader.isLocalVariableUse(); + } + + public boolean isFieldArrayDefinition() { + if (!isArrayStoreInstruction()) + return false; + + // only field var if arrayref on stack is from field var use + BytecodeInstruction arrayLoader = getSourceOfArrayReference(); + if (arrayLoader == null) + return false; + + return arrayLoader.isFieldUse(); + } + + public boolean isStaticArrayUsage() { + if (!isArrayStoreInstruction()) + return false; + + BytecodeInstruction arrayLoader = getSourceOfArrayReference(); + if (arrayLoader == null) + return false; + + return arrayLoader.isStaticDefUse(); + } + + public boolean isArrayStoreInstruction() { + return asmNode.getOpcode() == Opcodes.IASTORE + || asmNode.getOpcode() == Opcodes.LASTORE + || asmNode.getOpcode() == Opcodes.FASTORE + || asmNode.getOpcode() == Opcodes.DASTORE + || asmNode.getOpcode() == Opcodes.AASTORE; + } + + public boolean isArrayLoadInstruction() { + return asmNode.getOpcode() == Opcodes.IALOAD + || asmNode.getOpcode() == Opcodes.LALOAD + || asmNode.getOpcode() == Opcodes.FALOAD + || asmNode.getOpcode() == Opcodes.DALOAD + || asmNode.getOpcode() == Opcodes.AALOAD; + } + + protected String getArrayVariableName() { + BytecodeInstruction arrayLoader = getSourceOfArrayReference(); + if (arrayLoader == null) + return null; + + return arrayLoader.getVariableName(); + } + + public abstract BytecodeInstruction getSourceOfArrayReference(); + + /** + *

+ * isDefinitionForVariable + *

+ * + * @param var a {@link java.lang.String} object. + * @return a boolean. + */ + public boolean isDefinitionForVariable(String var) { + return (isDefinition() && getVariableName().equals(var)); + } + + /** + *

+ * isInvokeSpecial + *

+ * + * @return a boolean. + */ + public boolean isInvokeSpecial() { + return asmNode.getOpcode() == Opcodes.INVOKESPECIAL; + } + + /** + * Checks whether this instruction is an INVOKESPECIAL instruction that + * calls a constructor. + * + * @return a boolean. + */ + public boolean isConstructorInvocation() { + if (!isInvokeSpecial()) + return false; + + MethodInsnNode invoke = (MethodInsnNode) asmNode; + // if (!invoke.owner.equals(className.replaceAll("\\.", "/"))) + // return false; + + return invoke.name.equals(""); + } + + /** + * Checks whether this instruction is an INVOKESPECIAL instruction that + * calls a constructor of the given class. + * + * @param className a {@link java.lang.String} object. + * @return a boolean. + */ + public boolean isConstructorInvocation(String className) { + if (!isInvokeSpecial()) + return false; + + MethodInsnNode invoke = (MethodInsnNode) asmNode; + if (!invoke.owner.equals(className.replaceAll("\\.", "/"))) + return false; + + return invoke.name.equals(""); + } + + // other classification methods + + /** + * 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); + } + + /** + *

+ * getLineNumber + *

+ * + * @return a int. + */ + public int getLineNumber() { + if (!isLineNumber()) + return -1; + + return ((LineNumberNode) asmNode).line; + } + + /** + *

+ * isLabel + *

+ * + * @return a boolean. + */ + public boolean isLabel() { + return asmNode instanceof LabelNode; + } + + // sanity checks + + /** + *

+ * sanityCheckAbstractInsnNode + *

+ * + * @param node a {@link org.objectweb.asm.tree.AbstractInsnNode} object. + */ + public boolean sanityCheckAbstractInsnNode(AbstractInsnNode node) { + if (node == null) + return false; //throw new IllegalArgumentException("null given"); + return node.equals(this.asmNode); + // + // throw new IllegalStateException("sanity check failed for " + // + node.toString() + " on " + getMethodName() + toString()); + } + +} 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..3e3c2fa4a9 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ActualControlFlowGraph.java @@ -0,0 +1,843 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; + +import org.evomaster.client.java.utils.SimpleLogger; + +import java.util.HashSet; +import java.util.Set; + +/** + * Supposed to become the new implementation of a control flow graph inside + * EvoSuite + *

+ * The "actual" CFG does not contain single cfg.BytecodeInstructions as nodes, + * but contains cfg.BasicBlocks - look at that class for more information + *

+ * Simply put this is a minimized version of the complete/raw CFG the + * cfg.BytecodeAnalyzer and cfg.CFGGenerator produce - which holds a node for + * every BytecodeInstruction + *

+ * Out of the above described raw CFG the following "pin-points" are extracted: + *

+ * 1) the entryNode (first instruction in the method) + *

+ * 2) all exitNodes (outDegree 0) + *

+ * 3.1) all branches (outDegree>1) 3.2) in a subsequent step all targets of all + * branches + *

+ * 4.1) all joins (inDegree>1) 4.2) in a subsequent step all sources of all + * joins + *

+ * All those "pin-points" are put into a big set (some of the above categories + * may overlap) and for all those single BytecodeInstrucions their corresponding + * BasicBlock is computed and added to this CFGs vertexSet. After that the raw + * CFG is asked for the parents of the first instruction of each BasicBlock and + * the children of that blocks last instruction. Then the edges to their + * corresponding BasicBlocks are added to this CFG + *

+ * TODO: verify that this method works :D + *

+ *

+ * cfg.EvoSuiteGraph is used as the underlying data structure holding the + * graphical representation of the CFG + *

+ * WORK IN PROGRESS + *

+ * TODO implement + * + * @author Andre Mis + */ +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(); + } + + // "revert" constructor ... for now ... TODO + + /** + *

+ * 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 EvoSuiteGraph for + // all sub class of EvoSuiteGraph + 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"); + // if (!branch.isActualBranch()) // TODO this happens if your in a + // try-catch ... handle! + // throw new IllegalArgumentException( + // "unexpected branch byteCode instruction type: " + // + branch.getInstructionType()); + + // TODO the following doesn't work at this point + // because the BranchPool is not yet filled yet + // BUT one could fill the pool right now and drop further analysis + // later + // way cooler, because then filling of the BranchPool is unrelated + // to + // BranchInstrumentation - then again that instrumentation is needed + // anyways i guess + + // if (!BranchPool.isKnownAsBranch(instruction)) + // throw new IllegalStateException( + // "expect BranchPool to know all branching instructions: " + // + instruction.toString()); + + 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(); + + // TODO: Need to make that compatible with Testability Transformation + // checkSanity(); + + 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"); + + // for (BasicBlock test : graph.vertexSet()) { + // logger.debug("experimental self-equals on " + test.getName()); + // if (nodeBlock.equals(test)) + // logger.debug("true"); + // else + // logger.debug("false"); + // if (!containsBlock(test)) + // throw new IllegalStateException("wtf"); + // + // logger.debug("experimental equals on " + test.getName() + " with " + // + nodeBlock.getName()); + // if (test.equals(nodeBlock)) + // logger.debug("true"); + // else + // logger.debug("false"); + // + // logger.debug("experimental dual-equal"); + // if (nodeBlock.equals(test)) + // logger.debug("true"); + // else + // logger.debug("false"); + // + // } + + 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. + */ + public int getDistance(BytecodeInstruction v1, BytecodeInstruction v2) { + if (v1 == null || v2 == null) + throw new IllegalArgumentException("null given"); + if (!knowsInstruction(v1) || !knowsInstruction(v2)) + throw new IllegalArgumentException("instructions not contained in this CFG"); + + BasicBlock b1 = v1.getBasicBlock(); + BasicBlock b2 = v2.getBasicBlock(); + + if (b1 == null || b2 == null) + throw new IllegalStateException( + "expect CFG to contain the BasicBlock for each instruction knowsInstruction() returns true on"); + + return getDistance(b1, b2); + } + + /** + *

+ * isDirectSuccessor + *

+ * + * @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 boolean. + */ + public boolean isDirectSuccessor(BytecodeInstruction v1, BytecodeInstruction v2) { + if (v1 == null || v2 == null) + throw new IllegalArgumentException("null given"); + if (!knowsInstruction(v1) || !knowsInstruction(v2)) + throw new IllegalArgumentException("instructions not contained in this CFG"); + + BasicBlock b1 = v1.getBasicBlock(); + BasicBlock b2 = v2.getBasicBlock(); + + if (b1 == null || b2 == null) + throw new IllegalStateException( + "expect CFG to contain the BasicBlock for each instruction knowsInstruction() returns true on"); + + return isDirectSuccessor(b1, b2); + } + + // retrieve information about the CFG + + /** + *

+ * isEntryPoint + *

+ * + * @param block a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. + * @return a boolean. + */ + public boolean isEntryPoint(BasicBlock block) { + if (block == null) + throw new IllegalArgumentException("null given"); + + // // sanity check + // if (!block.getFirstInstruction().equals(entryPoint)) { + // logger.error("entryPoint: "+entryPoint.toString()); + // logger.error("current block: "+block.explain()); + // throw new IllegalStateException( + // "expect entryPoint of a method to be the first instruction from the entryBlock of that method"); + // } + return block.containsInstruction(entryPoint); + } + + /** + *

+ * isExitPoint + *

+ * + * @param block a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. + * @return a boolean. + */ + public boolean isExitPoint(BasicBlock block) { + if (block == null) + throw new IllegalArgumentException("null given"); + + for (BytecodeInstruction exitPoint : exitPoints) + if (block.containsInstruction(exitPoint)) { + // // sanity check + // if (!block.getLastInstruction().equals(exitPoint)) + // throw new IllegalStateException( + // "expect exitPoints of a method to be the last instruction from an exitBlock of that method"); + return true; + } + + return false; + } + + /** + *

+ * belongsToMethod + *

+ * + * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @return a boolean. + */ + public boolean belongsToMethod(BytecodeInstruction instruction) { + if (instruction == null) + throw new IllegalArgumentException("null given"); + + if (!className.equals(instruction.getClassName())) + return false; + return methodName.equals(instruction.getMethodName()); + } + + // sanity checks + + /** + *

+ * checkSanity + *

+ */ + public void checkSanity() { + + SimpleLogger.debug("checking sanity of CFG for " + methodName); + + if (isEmpty()) + throw new IllegalStateException("a CFG must contain at least one element"); + + for (BytecodeInstruction initInstruction : getInitiallyKnownInstructions()) { + if (!knowsInstruction(initInstruction)) + throw new IllegalStateException( + "expect CFG to contain all initially known instructions"); + } + + SimpleLogger.debug(".. all initInstructions contained"); + + // checkNodeSanity(); + + checkInstructionsContainedOnceConstraint(); + + SimpleLogger.debug(".. CFG sanity ensured"); + } + + private void checkInstructionsContainedOnceConstraint() { + + for (BytecodeInstruction ins : rawGraph.vertexSet()) { + if (!knowsInstruction(ins)) + throw new IllegalStateException( + "expect all instructions ins underlying RawCFG to be known by Actual CFG"); + + BasicBlock insBlock = ins.getBasicBlock(); + if (insBlock == null) + throw new IllegalStateException( + "expect ActualCFG.getBlockOf() to return non-null BasicBlocks for all instructions it knows"); + + for (BasicBlock block : vertexSet()) { + if (!block.equals(insBlock) && block.containsInstruction(ins)) + throw new IllegalStateException( + "expect ActualCFG to contain exactly one BasicBlock for each original bytecode instruction, not more!"); + } + } + + } + + void checkNodeSanity() { + // ensure graph is connected and isEntry and isExitBlock() work as + // expected + for (BasicBlock node : vertexSet()) { + + checkEntryExitPointConstraint(node); + + checkSingleCFGNodeConstraint(node); + + checkNodeMinimalityConstraint(node); + } + SimpleLogger.debug("..all node constraints ensured"); + } + + void checkEntryExitPointConstraint(BasicBlock node) { + // exit point constraint + int out = outDegreeOf(node); + if (!isExitPoint(node) && out == 0) + throw new IllegalStateException( + "expect nodes without outgoing edges to be exitBlocks: " + + node.toString()); + // entry point constraint + int in = inDegreeOf(node); + if (!isEntryPoint(node) && in == 0) + throw new IllegalStateException( + "expect nodes without incoming edges to be the entryBlock: " + + node.toString()); + } + + void checkSingleCFGNodeConstraint(BasicBlock node) { + int in = inDegreeOf(node); + int out = outDegreeOf(node); + if (in + out == 0 && vertexCount() != 1) + throw new IllegalStateException( + "node with neither child nor parent only allowed if CFG consists of a single block: " + + node.toString()); + + if (vertexCount() == 1 && !(isEntryPoint(node) && isExitPoint(node))) + throw new IllegalStateException( + "if a CFG consists of a single basic block that block must be both entry and exitBlock: " + + node.toString()); + } + + void checkNodeMinimalityConstraint(BasicBlock node) { + + if (hasNPartentsMChildren(node, 1, 1)) { + for (BasicBlock child : getChildren(node)) + if (hasNPartentsMChildren(child, 1, 1)) + throw new IllegalStateException( + "whenever a node has exactly one child and one parent, it is expected that the same is true for either of those"); + + for (BasicBlock parent : getParents(node)) + if (hasNPartentsMChildren(parent, 1, 1)) + throw new IllegalStateException( + "whenever a node has exactly one child and one parent, it is expected that the same is true for either of those"); + } + } + + // 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; + } + + // @Override + // public BytecodeInstruction getBranch(int branchId) { + // + // Branch searchedFor = BranchPool.getBranch(branchId); + // if (searchedFor == null) + // return null; + // + // if (containsInstruction(searchedFor.getInstruction())) + // return searchedFor.getInstruction(); + // + // // TODO more sanity checks? + // + // return null; + // } + + /** + *

+ * Getter for the field entryPoint. + *

+ * + * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + */ + public BytecodeInstruction getEntryPoint() { + return entryPoint; + } + + /** + *

+ * Getter for the field exitPoints. + *

+ * + * @return a {@link java.util.Set} object. + */ + public Set getExitPoints() { + return new HashSet<>(exitPoints); + } + + /** + *

+ * Getter for the field branches. + *

+ * + * @return a {@link java.util.Set} object. + */ + public Set getBranches() { + return new HashSet<>(branches); + } + + /** + *

+ * Getter for the field joins. + *

+ * + * @return a {@link java.util.Set} object. + */ + public Set getJoins() { + return new HashSet<>(joins); + } + + /** + * {@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..20e1cfc0fe --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BasicBlock.java @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +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 org.objectweb.asm.tree.AbstractInsnNode; +import org.evomaster.client.java.utils.SimpleLogger; + +import java.io.Serializable; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 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 + * + * @author Andre Mis + * @see 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; + + // experiment: since finding the control dependent branches in the CDG might + // take a little to long, we might want to remember them + private Set controlDependencies; + private Set controlDependentBranchIDs; + + protected boolean isAuxiliaryBlock = false; + + private final List instructions = new ArrayList<>(); + + // DONE reference each BytecodeInstruction's BasicBlock at the instruction + // DONE determine ControlDependentBranches once for each BasicBlock, then + // ask BasicBloc, whenever instruction is asked + // TODO remember distance to each control dependent Branch in order to speed + // up ControlFlowDistance calculation even more + + /** + *

+ * 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); + + checkSanity(); + } + + /** + * 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 all branchIds of Branches this instruction is directly control + * dependent on as determined by the ControlDependenceGraph for this + * instruction's method. + *

+ * If this instruction is control dependent on the root branch the id -1 + * will be contained in this set + * + * @return a {@link java.util.Set} object. + */ + public Set getControlDependentBranchIds() { + + ControlDependenceGraph myDependence = getCDG(); + + if (controlDependentBranchIDs == null) { + controlDependentBranchIDs = myDependence.getControlDependentBranchIds(this); + //be sure we can iterate over it deterministically + controlDependentBranchIDs = + controlDependentBranchIDs.stream().sorted().collect(Collectors.toCollection(LinkedHashSet::new)); + } + return controlDependentBranchIDs; + } + + /** + * 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"); + + // not sure if this holds: + // .. apparently it doesn't. at least check + // fails for java2.util2.Pattern TODO + + // BytecodeInstruction previousInstruction = getLastInstruction(); + // if (previousInstruction != null + // && instruction.getInstructionId() < previousInstruction + // .getInstructionId()) + // throw new IllegalStateException( + // "expect instructions in a basic block to be ordered by their instructionId"); + + 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); + } + + /** + *

+ * constainsInstruction + *

+ * + * @param insnNode a {@link org.objectweb.asm.tree.AbstractInsnNode} object. + * @return a boolean. + */ + public boolean constainsInstruction(AbstractInsnNode insnNode) { + for (BytecodeInstruction instruction : instructions) { + if (instruction.getASMNode().equals(insnNode)) + return true; + } + return false; + } + + /** + *

+ * 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; + } + + /** + *

+ * explain + *

+ * + * @return a {@link java.lang.String} object. + */ + public String explain() { + StringBuilder r = new StringBuilder(); + r.append(getName() + ":\n"); + + int i = 0; + for (BytecodeInstruction instruction : instructions) { + i++; + r.append("\t" + i + ")\t" + instruction.toString() + "\n"); + } + + return r.toString(); + } + + // 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(); + } + + /** + *

+ * checkSanity + *

+ */ + public void checkSanity() { + + SimpleLogger.debug("checking sanity of " + this); + + // TODO + + // not true, there are branches that don't really jump + // for example if you have no statement in a then-part: + // if (exp) { ; } + // you will not have a second outgoing edge for that if + + // for(BytecodeInstruction instruction : instructions) { + // if (!instruction.equals(getLastInstruction()) + // && instruction.isActualBranch()) + // throw new IllegalStateException( + // "expect actual branches to always end a basic block: "+instruction.toString()+" \n"+explain()); + // } + + // TODO handle specialBlocks + } + + /** + *

+ * 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/BytecodeAnalyzer.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeAnalyzer.java new file mode 100755 index 0000000000..1c7036c2fa --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeAnalyzer.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; + +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.Frame; +import org.objectweb.asm.tree.analysis.SourceInterpreter; + +/** + * This class analyzes the byteCode from a method in the CUT and generates it's + * CFG using a cfg.CFGGenerator + *

+ * This is done using the ASM library, extending from it's asm.Analyzer and + * redirecting the calls to newControlFlowEdge() to an instance of + * cfg.CFGGenerator which in turn builds up a graph representation of the CFG, + * which later is used to build a "smaller" CFG containing not + * BytecodeInstructions but BasicBlocks of BytecodeInstructions which are always + * executed successively + * + * @author Gordon Fraser, Andre Mis + */ +public class BytecodeAnalyzer extends Analyzer { + + CFGGenerator cfgGenerator; + + /** + *

+ * Constructor for BytecodeAnalyzer. + *

+ */ + public BytecodeAnalyzer() { + super(new SourceInterpreter()); + } + + // main interface + + /** + * Analyzes the method corresponding to the given Strings and initializes + * the CFGGenerator and thus the BytecodeInstructionPool for the given + * method in the given class. + * + * @param owner a {@link java.lang.String} object. + * @param method a {@link java.lang.String} object. + * @param node a {@link org.objectweb.asm.tree.MethodNode} object. + * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.CFGFrame} object. + * @throws org.objectweb.asm.tree.analysis.AnalyzerException if any. + */ + public CFGFrame analyze(ClassLoader classLoader, String owner, String method, + MethodNode node) throws AnalyzerException { + + cfgGenerator = new CFGGenerator(classLoader, owner, method, node); + this.analyze(owner, node); + + Frame[] frames = getFrames(); + + if (frames.length == 0) + return null; + + return (CFGFrame) getFrames()[0]; + } + + /** + * After running analyze() this method yields the filled CFGGenerator for + * further processing of the gathered information from analyze() within the + * ByteCode representation of EvoSuite + * + * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.CFGGenerator} object. + */ + public CFGGenerator retrieveCFGGenerator() { + if (cfgGenerator == null) + throw new IllegalStateException( + "you have to call analyze() first before retrieving the CFGGenerator"); + return cfgGenerator; + } + + // inherited from asm.Analyzer + + /** + * {@inheritDoc} + *

+ * Called for each non-exceptional cfg edge + */ + @Override + protected void newControlFlowEdge(int src, int dst) { + + cfgGenerator.registerControlFlowEdge(src, dst, getFrames(), false); + } + + /** + * {@inheritDoc} + *

+ * We also need to keep track of exceptional edges - they are also branches + */ + @Override + protected boolean newControlFlowExceptionEdge(int src, int dst) { + // TODO: Make use of information that this is an exception edge? + cfgGenerator.registerControlFlowEdge(src, dst, getFrames(), true); + + return true; + } + + /** + * {@inheritDoc} + */ + @Override + protected Frame newFrame(int nLocals, int nStack) { + return new CFGFrame(nLocals, nStack); + } + + /** + * {@inheritDoc} + */ + @Override + protected Frame newFrame(Frame src) { + return new CFGFrame(src); + } +} 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..0c9e57132a --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstruction.java @@ -0,0 +1,1193 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +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.tree.analysis.SourceValue; + +import java.io.Serializable; +import java.util.Set; + +/** + * Internal representation of a BytecodeInstruction + *

+ * Extends ASMWrapper which serves as an interface to the ASM library. + *

+ * Known super classes are DefUse and Branch which yield specific functionality + * needed to achieve theirs respective coverage criteria + *

+ * Old: Node of the control flow graph + * + * @author Gordon Fraser, Andre Mis + */ +public class BytecodeInstruction extends ASMWrapper implements Serializable, + Comparable { + + private static final long serialVersionUID = 3630449183355518857L; + + // 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); + this.frame = wrap.frame; + } + + /** + *

+ * 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; + } + + /** + *

+ * setCFGFrame + *

+ * + * @param frame a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.CFGFrame} object. + */ + public void setCFGFrame(CFGFrame frame) { + this.frame = frame; + } + + // --- Field Management --- + + /** + * {@inheritDoc} + */ + @Override + public int getInstructionId() { + return instructionId; + } + + /** + *

+ * getBytecodeOffset + *

+ * + * @return a int. + */ + public int getBytecodeOffset() { + return bytecodeOffset; + } + + /** + * {@inheritDoc} + */ + @Override + public String getMethodName() { + return methodName; + } + + /** + *

+ * Getter for the field className. + *

+ * + * @return a {@link java.lang.String} object. + */ + @Override + public String getClassName() { + return className; + } + + /** + *

+ * getName + *

+ * + * @return a {@link java.lang.String} object. + */ + public String getName() { + return "BytecodeInstruction " + instructionId + " in " + methodName; + } + + /** + * 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} + */ + @Override + 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 = super.getLineNumber(); + // 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 = super.getLineNumber(); + // 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(); + } + + /** + * This method returns a random Branch among all Branches this instruction + * is control dependent on + *

+ * If this instruction is only dependent on the root branch, this method + * returns null + *

+ * Since EvoSuite was previously unable to detect multiple control + * dependencies for one instruction this method serves as a backwards + * compatibility bridge + * + * @return a {@link org.evomaster.client.java.coverage.branch.Branch} object. + */ + public Branch getControlDependentBranch() { + + Set controlDependentBranches = getControlDependencies(); + + for (ControlDependency cd : controlDependentBranches) + return cd.getBranch(); + + return null; // root branch + } + + /** + * Returns all branchIds of Branches this instruction is directly control + * dependent on as determined by the ControlDependenceGraph for this + * instruction's method. + *

+ * If this instruction is control dependent on the root branch the id -1 + * will be contained in this set + * + * @return a {@link java.util.Set} object. + */ + public Set getControlDependentBranchIds() { + + BasicBlock myBlock = getBasicBlock(); + + return myBlock.getControlDependentBranchIds(); + } + + /** + * Determines whether or not this instruction is control dependent on the + * root branch of it's method by calling getControlDependentBranchIds() to + * see if the return contains -1. + * + * @return a boolean. + */ + public boolean isRootBranchDependent() { + return getControlDependencies().isEmpty(); + } + + /** + * This method returns a random branchId among all branchIds this + * instruction is control dependent on. + *

+ * This method returns -1 if getControlDependentBranch() returns null, + * otherwise that Branch's branchId is returned + *

+ * Note: The returned branchExpressionValue comes from the same Branch + * getControlDependentBranch() and getControlDependentBranchId() return + *

+ * Since EvoSuite was previously unable to detect multiple control + * dependencies for one instruction this method serves as a backwards + * compatibility bridge + * + * @return a int. + */ + public int getControlDependentBranchId() { + + Branch b = getControlDependentBranch(); + if (b == null) + return -1; + + return b.getActualBranchId(); + } + + /** + * This method returns the branchExpressionValue from a random Branch among + * all Branches this instruction is control dependent on. + *

+ * This method returns true if getControlDependentBranch() returns null, + * otherwise that Branch's branchExpressionValue is returned + *

+ * Note: The returned branchExpressionValue comes from the same Branch + * getControlDependentBranch() and getControlDependentBranchId() return + *

+ * Since EvoSuite was previously unable to detect multiple control + * dependencies for one instruction this method serves as a backwards + * compatibility bridge + * + * @return a boolean. + */ + public boolean getControlDependentBranchExpressionValue() { + + Branch b = getControlDependentBranch(); + return getBranchExpressionValue(b); + } + + /** + *

+ * getBranchExpressionValue + *

+ * + * @param b a {@link org.evomaster.client.java.coverage.branch.Branch} object. + * @return a boolean. + */ + public boolean getBranchExpressionValue(Branch b) { + if (!isDirectlyControlDependentOn(b)) + throw new IllegalArgumentException( + "this method can only be called for branches that this instruction is directly control dependent on."); + + if (b == null) + return true; // root branch special case + + return getControlDependency(b).getBranchExpressionValue(); + } + + /** + * Determines whether this BytecodeInstruction is directly control dependent + * on the given Branch. Meaning within this instruction CDG there is an + * incoming ControlFlowEdge to this instructions BasicBlock holding the + * given Branch as it's branchInstruction. + *

+ * If the given Branch is null, this method checks whether the this + * instruction is control dependent on the root branch of it's method. + * + * @param branch a {@link org.evomaster.client.java.coverage.branch.Branch} object. + * @return a boolean. + */ + public boolean isDirectlyControlDependentOn(Branch branch) { + if (branch == null) + return getControlDependentBranchIds().contains(-1); + + for (ControlDependency cd : getControlDependencies()) + if (cd.getBranch().equals(branch)) + return true; + + return false; + } + + /** + *

+ * getControlDependency + *

+ * + * @param branch a {@link org.evomaster.client.java.coverage.branch.Branch} object. + * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlDependency} object. + */ + public ControlDependency getControlDependency(Branch branch) { + if (!isDirectlyControlDependentOn(branch)) + throw new IllegalArgumentException( + "instruction not directly control dependent on given branch"); + + for (ControlDependency cd : getControlDependencies()) + if (cd.getBranch().equals(branch)) + return cd; + + throw new IllegalStateException( + "expect getControlDependencies() to contain a CD for each branch that isDirectlyControlDependentOn() returns true on"); + } + + // /** + // * WARNING: better don't user this method right now TODO + // * + // * Determines whether the CFGVertex is transitively control dependent on + // the + // * given Branch + // * + // * A CFGVertex is transitively control dependent on a given Branch if the + // * Branch and the vertex are in the same method and the vertex is either + // * directly control dependent on the Branch - look at + // * isDirectlyControlDependentOn(Branch) - or the CFGVertex of the control + // * dependent branch of this CFGVertex is transitively control dependent on + // * the given branch. + // * + // */ + // public boolean isTransitivelyControlDependentOn(Branch branch) { + // if (!getClassName().equals(branch.getClassName())) + // return false; + // if (!getMethodName().equals(branch.getMethodName())) + // return false; + // + // // TODO: this method does not take into account, that there might be + // // multiple branches this instruction is control dependent on + // + // BytecodeInstruction vertexHolder = this; + // do { + // if (vertexHolder.isDirectlyControlDependentOn(branch)) + // return true; + // vertexHolder = vertexHolder.getControlDependentBranch() + // .getInstruction(); + // } while (vertexHolder != null); + // + // return false; + // } + + // /** + // * WARNING: better don't user this method right now TODO + // * + // * Determines the number of branches that have to be passed in order to + // pass + // * this CFGVertex + // * + // * Used to determine TestFitness difficulty + // */ + + /** + *

+ * getCDGDepth + *

+ * + * @return a int. + */ + public int getCDGDepth() { + int min = Integer.MAX_VALUE; + Set dependencies = getControlDependencies(); + if (dependencies.isEmpty()) + min = 1; + for (ControlDependency dependency : dependencies) { + int depth = getCDG().getControlDependenceDepth(dependency); + if (depth < min) + min = depth; + } + return min; + /* + * // TODO: this method does not take into account, that there might be + * // multiple branches this instruction is control dependent on Branch + * current = getControlDependentBranch(); int r = 1; while (current != + * null) { r++; current = + * current.getInstruction().getControlDependentBranch(); } return r; + */ + } + + // 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 = ""; + if (frame == null) + stack = "null"; + else + for (int i = 0; i < frame.getStackSize(); i++) { + stack += frame.getStack(i) + ","; + } + + 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; + } + + /** + *

+ * printFrameInformation + *

+ */ + public void printFrameInformation() { + System.out.println("Frame STACK:"); + for (int i = 0; i < frame.getStackSize(); i++) { + SourceValue v = (SourceValue) frame.getStack(i); + System.out.print(" " + i + "(" + v.insns.size() + "): "); + for (Object n : v.insns) { + AbstractInsnNode node = (AbstractInsnNode) n; + BytecodeInstruction ins = BytecodeInstructionPool.getInstance(classLoader).getInstruction(className, + methodName, + node); + System.out.print(ins.toString() + ", "); + } + System.out.println(); + } + + System.out.println("Frame LOCALS:"); + for (int i = 1; i < frame.getLocals(); i++) { + SourceValue v = (SourceValue) frame.getLocal(i); + System.out.print(" " + i + "(" + v.insns.size() + "): "); + for (Object n : v.insns) { + AbstractInsnNode node = (AbstractInsnNode) n; + BytecodeInstruction ins = BytecodeInstructionPool.getInstance(classLoader).getInstruction(className, + methodName, + node); + System.out.print(ins.toString() + ", "); + } + System.out.println(); + } + } + + // --- 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.coverage.branch.Branch} object. + */ + public Branch toBranch() { + + try { + return BranchPool.getInstance(classLoader).getBranchForInstruction(this); + } catch (Exception e) { + return null; + } + } + + /** + *

+ * proceedsOwnConstructorInvocation + *

+ * + * @return a boolean. + */ + public boolean proceedsOwnConstructorInvocation() { + + RawControlFlowGraph cfg = getRawCFG(); + for (BytecodeInstruction other : cfg.vertexSet()) + if (other.isConstructorInvocation() + && other.isMethodCallOnSameObject()) + if (getInstructionId() < other.getInstructionId()) + return true; + + return false; + } + + /** + *

+ * isWithinConstructor + *

+ * + * @return a boolean. + */ + public boolean isWithinConstructor() { + return getMethodName().startsWith(""); + } + + /** + *

+ * isLastInstructionInMethod + *

+ * + * @return a boolean. + */ + public boolean isLastInstructionInMethod() { + return equals(getRawCFG().getInstructionWithBiggestId()); + } + + /** + *

+ * canBeExitPoint + *

+ * + * @return a boolean. + */ + public boolean canBeExitPoint() { + return canReturnFromMethod() || isLastInstructionInMethod(); + } + + /** + * Returns the RawCFG of the method called by this instruction + * + * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph} object. + */ + public RawControlFlowGraph getCalledCFG() { + if (!isMethodCall()) + return null; + + return GraphPool.getInstance(classLoader).getRawCFG(getCalledMethodsClass(), + getCalledMethod()); + } + + /** + * Determines whether this instruction calls a method on its own Object + * ('this') + *

+ * This is done using the getSourceOfMethodInvocationInstruction() method + * and checking if the return of that method loads this using loadsReferenceToThis() + * + * @return a boolean. + */ + public boolean isMethodCallOnSameObject() { + BytecodeInstruction srcInstruction = getSourceOfMethodInvocationInstruction(); + if (srcInstruction == null) + return false; + return srcInstruction.loadsReferenceToThis(); + } + + /** + * Determines whether this instruction calls a method on a field variable + *

+ * This is done using the getSourceOfMethodInvocationInstruction() method + * and checking if the return of that method is a field use instruction + * + * @return a boolean. + */ + + + public boolean isMethodCallOfField() { + if (!this.isMethodCall()) + return false; + if (this.isInvokeStatic()) + return false; + // If the instruction belongs to static initialization block of the + // class, then the method call cannot be done on a fields. + if (this.methodName.contains("")) + return false; + BytecodeInstruction srcInstruction = getSourceOfMethodInvocationInstruction(); + if (srcInstruction == null) + return false; + + //is a field use? But field uses are also "GETSTATIC" + if (srcInstruction.isFieldNodeUse()) { + + //is static? if not, return yes. This control is not necessary in theory, but you never know... + if (srcInstruction.isStaticDefUse()) { + //is static, check if the name of the class that contain the static field is equals to the current class name + //if is equals, return true, otherwise we are in a case where we are calling a field over an external static class + //e.g. System.out + if (srcInstruction.asmNode instanceof FieldInsnNode) { + String classNameField = ((FieldInsnNode) srcInstruction.asmNode).owner; + classNameField = classNameField.replace('/', '.'); + return classNameField.equals(className); + } + } else { + return true; + } + } + return false; + + } + + /** + * Determines the name of the field variable this method call is invoked on + *

+ * This is done using the getSourceOfMethodInvocationInstruction() method + * and returning its variable name + * + * @return a {@link java.lang.String} object. + */ + @Override + public String getFieldMethodCallName() { + BytecodeInstruction srcInstruction = getSourceOfMethodInvocationInstruction(); + if (srcInstruction == null) + return null; + return srcInstruction.getVariableName(); + } + + /** + * If this is a method call instruction this method will return the + * instruction that loaded the reference of the Object the method is invoked + * onto the stack. + *

+ * This is done using getSourceOfStackInstruction() + *

+ * The reference is found on top of the stack minus the number of the called + * methods argument + */ + public BytecodeInstruction getSourceOfMethodInvocationInstruction() { + if (!isMethodCall()) + return null; + + // the object on which this method is called is on top of the stack + // minus the number of arguments the called method has + return getSourceOfStackInstruction(getCalledMethodsArgumentCount()); + } + + /** + * If this instruction is an array instruction this method will return the + * BytecodeInstruction that loaded the reference of the array onto the + * stack. + *

+ * This is done using getSourceOfStackMethod() + *

+ * The reference is found on top of the stack minus two + */ + public BytecodeInstruction getSourceOfArrayReference() { + if (isArrayStoreInstruction()) { + // when reaching an array store instruction the stack should end in + // ,,. so the array reference is on top of the + // stack minus two + return getSourceOfStackInstruction(2); + + } else if (isArrayLoadInstruction()) { + // when reaching an array store instruction the stack should end in + // ,. so the array reference is on top of the + // stack minus one + return getSourceOfStackInstruction(1); + + } else { + return null; + } + + } + + /** + * This method returns the BytecodeInstruction that loaded the reference + * which is located on top of the stack minus positionFromTop when this + * instruction is executed. + *

+ * This is done using the CFGFrame created by the SourceInterpreter() of the + * BytecodeAnalyzer via the CFGGenerator + *

+ * Note that this method may return null. This can happen when aliasing is + * involved. For example for method invocations on objects this can happen + * when you first store the object in a local variable and then call a + * method on that variable + *

+ * see PairTestClass.sourceCallerTest() for an even worse example. + *

+ * TODO: this could be done better by following the SourceValues even + * further. + */ + public BytecodeInstruction getSourceOfStackInstruction(int positionFromTop) { + if (frame == null) + throw new IllegalStateException( + "expect each BytecodeInstruction to have its CFGFrame set"); + + int stackPos = frame.getStackSize() - (1 + positionFromTop); + if (stackPos < 0) { + StackTraceElement[] se = new Throwable().getStackTrace(); + int t = 0; + System.out.println("Stack trace: "); + while (t < se.length) { + System.out.println(se[t]); + t++; + } + return null; + } + SourceValue source = (SourceValue) frame.getStack(stackPos); + if (source.insns.size() != 1) { + // we don't know for sure, let's be conservative + return null; + } + Object sourceIns = source.insns.iterator().next(); + AbstractInsnNode sourceInstruction = (AbstractInsnNode) sourceIns; + BytecodeInstruction src = BytecodeInstructionPool.getInstance(classLoader).getInstruction(className, + methodName, + sourceInstruction); + return src; + } + + /** + *

+ * isCallToPublicMethod + *

+ * + * @return a boolean. + */ + public boolean isCallToPublicMethod() { + if (!isMethodCall()) + return false; + + if (getCalledCFG() == null) { + // TODO not sure if I am supposed to throw an Exception at this + // point + return false; + } + + return getCalledCFG().isPublicMethod(); + } + + /** + *

+ * isCallToStaticMethod + *

+ * + * @return a boolean. + */ + public boolean isCallToStaticMethod() { + if (!isMethodCall()) + return false; + + if (getCalledCFG() == null) { + // TODO not sure if I am supposed to throw an Exception at this + // point + return false; + } + + return getCalledCFG().isStaticMethod(); + } + + /** + *

+ * canBeInstrumented + *

+ * + * @return a boolean. + */ + public boolean canBeInstrumented() { + // System.out.println("i cant be instrumented "+toString()); + return !isWithinConstructor() || !proceedsOwnConstructorInvocation(); + } + + /** + * {@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(); + } + +} diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionFactory.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionFactory.java new file mode 100755 index 0000000000..24cb3abf9c --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; + +import org.objectweb.asm.tree.AbstractInsnNode; + +public class BytecodeInstructionFactory { + + /** + *

+ * createBytecodeInstruction + *

+ * + * @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 node a {@link org.objectweb.asm.tree.AbstractInsnNode} object. + * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + */ + public static BytecodeInstruction createBytecodeInstruction(ClassLoader classLoader, + String className, String methodName, int instructionId, int bytecodeOffset, + AbstractInsnNode node) { + + BytecodeInstruction instruction = new BytecodeInstruction(classLoader, className, + methodName, instructionId, bytecodeOffset, node); + + return instruction; + } + +} diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionIdComparator.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionIdComparator.java new file mode 100755 index 0000000000..86e4009c1f --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionIdComparator.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.graphs.cfg; + +import java.util.Comparator; + +/** + * Orders CFGVertices according to their id + *

+ * This is mainly used to put BytecodeInstructions into a PriorityQueue in + * ControlFlowGraph.getMaximalInitialDistance() + * + * @author Gordon Fraser + */ +public class BytecodeInstructionIdComparator implements Comparator { + + /** + * {@inheritDoc} + */ + @Override + public int compare(BytecodeInstruction arg0, BytecodeInstruction arg1) { + + return Integer.compare(arg0.getInstructionId(), arg1.getInstructionId()); + } +} 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..530667241a --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionPool.java @@ -0,0 +1,581 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +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.instrumentation.AnnotatedLabel; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.*; +import org.evomaster.client.java.utils.SimpleLogger; + +import java.util.*; + +/** + *

+ * BytecodeInstructionPool class. + *

+ * + * @author Andre Mis + */ +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<>(); + + private final List knownMethodNodes = new ArrayList<>(); + + // 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) { + registerMethodNode(node); + + int lastLineNumber = -1; + int bytecodeOffset = 0; + + for (int instructionId = 0; instructionId < node.instructions.size(); instructionId++) { + AbstractInsnNode instructionNode = node.instructions.get(instructionId); + + BytecodeInstruction instruction = BytecodeInstructionFactory.createBytecodeInstruction(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.isFrame()) { + 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; + } + + private void registerMethodNode(MethodNode node) { + for (MethodNode mn : knownMethodNodes) + if (mn == node) + SimpleLogger.debug("CFGGenerator.analyze() apparently got called for the same MethodNode twice"); + + knownMethodNodes.add(node); + } + + /** + *

+ * 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.isActualBranch()) { + 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 || (r.sanityCheckAbstractInsnNode(asmNode)); + + 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; + } + + /** + *

+ * knownClasses + *

+ * + * @return a {@link java.util.Set} object. + */ + public Set knownClasses() { + return new LinkedHashSet<>(instructionMap.keySet()); + } + + /** + *

+ * knownMethods + *

+ * + * @param className a {@link java.lang.String} object. + * @return a {@link java.util.Set} object. + */ + public Set knownMethods(String className) { + Set r = new LinkedHashSet<>(); + + if (instructionMap.get(className) != null) + r.addAll(instructionMap.get(className).keySet()); + + return r; + } + + public boolean hasMethod(String className, String methodName) { + if (instructionMap.get(className) != null) + return instructionMap.get(className).containsKey(methodName); + + return false; + } + + /** + *

+ * 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; + } + + public List getAllInstructions() { + List r = new ArrayList<>(); + for (String className : instructionMap.keySet()) { + Map> methodMap = instructionMap.get(className); + for (List methodInstructions : methodMap.values()) { + r.addAll(methodInstructions); + } + } + + return r; + } + + /** + *

+ * logInstructionsIn + *

+ * + * @param className a {@link java.lang.String} object. + * @param methodName a {@link java.lang.String} object. + */ + public void logInstructionsIn(String className, String methodName) { + + SimpleLogger.debug("Printing instructions in " + className + "." + methodName + ":"); + + List instructions = getInstructionsIn(className, methodName); + if (instructions == null) { + SimpleLogger.debug("..unknown method"); + } else { + for (BytecodeInstruction instruction : instructions) { + SimpleLogger.debug("\t" + instruction); + } + } + + } + + /** + *

+ * createFakeInstruction + *

+ * + * @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.BytecodeInstruction} object. + */ + public BytecodeInstruction createFakeInstruction(String className, String methodName) { + + AbstractInsnNode fakeNode = new InsnNode(Opcodes.NOP); + + int instructionId = getInstructionsIn(className, methodName).size(); + + BytecodeInstruction instruction = new BytecodeInstruction(classLoader, className, + methodName, instructionId, -1, fakeNode); + + registerInstruction(instruction); + + return instruction; + + } + + /** + *

+ * clear + *

+ */ + public void clear() { + instructionMap.clear(); + knownMethodNodes.clear(); + } + + public static void clearAll() { + BytecodeInstructionPool.instanceMap.clear(); + } + + /** + *

+ * clear + *

+ * + * @param className a {@link java.lang.String} object. + */ + public void clear(String className) { + instructionMap.remove(className); + } + + public static void clearAll(String className) { + for (BytecodeInstructionPool pool : instanceMap.values()) { + pool.clear(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 (instructionMap.containsKey(className)) + instructionMap.get(className).remove(methodName); + } + + public static void clearAll(String className, String methodName) { + for (BytecodeInstructionPool pool : instanceMap.values()) { + pool.clear(className, methodName); + } + } + + /** + *

+ * 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); + } + + public int getFirstLineNumberOfMethod(String className, String methodName) { + if (instructionMap.get(className) == null) + throw new IllegalArgumentException("unknown class " + className); + if (instructionMap.get(className).get(methodName) == null) + throw new IllegalArgumentException("unknown method " + methodName + + " in class " + className); + if (instructionMap.get(className).get(methodName).isEmpty()) + throw new IllegalArgumentException("no instructions in method " + methodName + + " in class " + className); + + int r = Integer.MAX_VALUE; + for (BytecodeInstruction ins : instructionMap.get(className).get(methodName)) { + if (ins.getLineNumber() < r) + r = ins.getLineNumber(); + } + return r; + } + + public BytecodeInstruction getFirstInstructionAtLineNumber(String className, String methodName, int lineNumber) { + // TODO + if (instructionMap.get(className) == null) + return null; + if (instructionMap.get(className).get(methodName) == null) + return null; + if (instructionMap.get(className).get(methodName).isEmpty()) + return null; + + for (BytecodeInstruction ins : instructionMap.get(className).get(methodName)) { + if (ins.getLineNumber() == lineNumber) + return ins; + } + return null; + } +} diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGClassAdapter.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGClassAdapter.java new file mode 100644 index 0000000000..97b38e4a96 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGClassAdapter.java @@ -0,0 +1,43 @@ +package org.evomaster.client.java.graphs.cfg; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * Minimal class adapter that wraps each method with CFGMethodAdapter to build graphs, + * while delegating to the downstream visitor chain unchanged. + */ +public class CFGClassAdapter extends ClassVisitor { + + private final ClassLoader classLoader; + private String classNameWithDots; + + public CFGClassAdapter(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 CFGMethodAdapter( + classLoader, + classNameWithDots, + access, + name, + descriptor, + signature, + exceptions, + downstream + ); + } +} + + diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGFrame.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGFrame.java new file mode 100755 index 0000000000..0e7ab6d5d1 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGFrame.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.graphs.cfg; + +import org.objectweb.asm.tree.analysis.Frame; + +import java.util.HashMap; +import java.util.Map; + +public class CFGFrame extends Frame { + Map successors = new HashMap<>(); + + /** + *

Constructor for CFGFrame.

+ * + * @param nLocals a int. + * @param nStack a int. + */ + public CFGFrame(int nLocals, int nStack) { + super(nLocals, nStack); + } + + /** + *

Constructor for CFGFrame.

+ * + * @param src a {@link org.objectweb.asm.tree.analysis.Frame} object. + */ + public CFGFrame(Frame src) { + super(src); + } + +} 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..56038a6ff9 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGGenerator.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +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 + * + * @author Andre Mis + */ +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"); + CFGFrame srcFrame = (CFGFrame) frames[src]; + Frame dstFrame = frames[dst]; + + if (srcFrame == null) + throw new IllegalArgumentException( + "expect given frames to know srcFrame for " + src); + + 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; + } + + srcFrame.successors.put(dst, (CFGFrame) dstFrame); + + 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); + + srcInstruction.setCFGFrame(srcFrame); + + 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() { + BytecodeInstructionPool.getInstance(classLoader).logInstructionsIn(className, + methodName); + + return new ActualControlFlowGraph(rawGraph); + } + + // getter + + /** + *

+ * Getter for the field rawGraph. + *

+ * + * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph} object. + */ + protected 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/CFGMethodAdapter.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGMethodAdapter.java new file mode 100755 index 0000000000..795cd5c43f --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGMethodAdapter.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; + +import org.evomaster.client.java.instrumentation.AnnotatedMethodNode; +import org.objectweb.asm.*; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.analysis.AnalyzerException; +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 adapter also adds instrumentation for branch distance + * measurement + *

+ * defUse, concurrency and LCSAJs instrumentation is also added (if the + * properties are set). + * + * @author Gordon Fraser + */ +public class CFGMethodAdapter 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 CFGMethodAdapter. + *

+ * + * @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 CFGMethodAdapter(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); + BytecodeAnalyzer bytecodeAnalyzer = new BytecodeAnalyzer(); + SimpleLogger.info("Generating CFG for method " + methodName); + try { + bytecodeAnalyzer.analyze(classLoader, className, methodName, mn); + SimpleLogger.debug("Method graph for " + + className + + "." + + methodName + + " contains " + + bytecodeAnalyzer.retrieveCFGGenerator().getRawGraph().vertexSet().size() + + " nodes for " + bytecodeAnalyzer.getFrames().length + + " instructions"); + // compute Raw and ActualCFG and put both into GraphPool + bytecodeAnalyzer.retrieveCFGGenerator().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/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..615b3fab1e --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlDependency.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; + +import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch; + +import java.io.Serializable; +import java.util.Objects; + +public class ControlDependency implements Serializable, Comparable { + + private static final long serialVersionUID = 6288839964561655730L; + + private final Branch branch; + private final boolean branchExpressionValue; + + /** + *

Constructor for ControlDependency.

+ * + * @param branch a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.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} object. + */ + public Branch getBranch() { + return branch; + } + + /** + *

Getter for the field branchExpressionValue.

+ * + * @return a boolean. + */ + public boolean getBranchExpressionValue() { + return branchExpressionValue; + } + +// @Override +// public int hashCode() { +// final int prime = 31; +// int result = 1; +// result = prime * result + ((branch == null) ? 0 : branch.hashCode()); +// result = prime * result + (branchExpressionValue ? 1231 : 1237); +// return result; +// } +// +// @Override +// public boolean equals(Object obj) { +// if (this == obj) +// return true; +// if (obj == null) +// return false; +// if (getClass() != obj.getClass()) +// return false; +// ControlDependency other = (ControlDependency) obj; +// if (branch == null) { +// if (other.branch != null) +// return false; +// } else if (!branch.equals(other.branch)) +// return false; +// if (branchExpressionValue != other.branchExpressionValue) +// return false; +// return true; +// } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + + String r = "CD " + branch; + + if (!branch.isSwitchCaseBranch()) { + 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); + } + + @Override + public int compareTo(ControlDependency o) { + int x = branch.compareTo(o.branch); + if (x != 0) + return x; + + if (branchExpressionValue == o.branchExpressionValue) { + return 0; + } else if (branchExpressionValue) { + return 1; + } else { + return -1; + } + } +} 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..3c06a60c99 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowEdge.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +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 static final long serialVersionUID = -5009449930477928101L; + + 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.coverage.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; + } + +// @Override +// public int hashCode() { +// final int prime = 31; +// int result = 1; +// result = prime * result + ((cd == null) ? 0 : cd.hashCode()); +// result = prime * result + (isExceptionEdge ? 1231 : 1237); +// return result; +// } +// +// @Override +// public boolean equals(Object obj) { +// if (this == obj) +// return true; +// if (obj == null) +// return false; +// if (getClass() != obj.getClass()) +// return false; +// ControlFlowEdge other = (ControlFlowEdge) obj; +// if (cd == null) { +// if (other.cd != null) +// return false; +// } else if (!cd.equals(other.cd)) +// return false; +// if (isExceptionEdge != other.isExceptionEdge) +// return false; +// 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..66fd5e5f28 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowGraph.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; + +import org.evomaster.client.java.instrumentation.dynamosa.graphs.EvoSuiteGraph; +import org.jgrapht.graph.DefaultDirectedGraph; +import org.objectweb.asm.Opcodes; +import org.evomaster.client.java.utils.SimpleLogger; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; + + +/** + * Abstract base class for both forms of CFGs inside EvoSuite + *

+ * One implementation of this is cfg.RawControlFlowGraph, which is also known as + * the complete CFG The other implementation of this is + * cfg.ActualControlFlowGraph which is also known as the minimal CFG Look at the + * respective classes for more detailed information + *

+ * The CFGs can be accessed via the GraphPool which holds for each CUT and each + * of their methods a complete and a minimal CFG + *

+ * CFGs are created by the CFGGenerator during the analysis of the CUTs' + * byteCode performed by the BytecodeAnalyzer + * + * @author Gordon Fraser, Andre Mis + */ +public abstract class ControlFlowGraph extends + EvoSuiteGraph { + + protected String className; + protected String methodName; + protected int access; + + private int diameter = -1; + + /** + * 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); + + /** + * Computes the diameter of this CFG and the mutation distances + *

+ * Since both takes some time this is not automatically done on each CFG + *

+ * GraphPool will automatically call this immediately after the + * instantiation of an ActualControlFlowGraph, but not after the creation of + * a RawControlFlowGraph + */ + public void finalise() { + computeDiameter(); + // TODO: call this! + // and sanity check with a flag whenever a call + // to this method is assumed to have been made + } + + /** + * Returns the Diameter of this CFG + *

+ * If the diameter of this graph was not computed previously it is computed + * first + * + * @return a int. + */ + public int getDiameter() { + if (diameter == -1) { + SimpleLogger.debug("diameter not computed yet. calling computeDiameter() first!"); + computeDiameter(); + } + + return diameter; + } + + public int getCyclomaticComplexity() { + // E = the number of edges of the graph. + // N = the number of nodes of the graph. + // M = E − N + 2 + int E = this.edgeCount(); + int N = this.vertexCount(); + return E - N + 2; + } + + /** + *

computeDiameter

+ */ + protected void computeDiameter() { + // The diameter is just an upper bound for the approach level + // Let's try to use something that's easier to compute than + // FLoydWarshall + diameter = this.edgeCount(); + } + + /** + *

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 + // TODO for now return null and handle in super class + // RawControlFlowGraph separately by overwriting this method + + // 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; + } + + /** + *

getMethodAccess

+ * + * @return a int. + */ + public int getMethodAccess() { + return access; + } + + /** + *

isPublicMethod

+ * + * @return a boolean. + */ + public boolean isPublicMethod() { + return (access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC; + } + + /** + *

isStaticMethod

+ * + * @return a boolean. + */ + public boolean isStaticMethod() { + return (access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC; + } + + /** + * {@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..a5aa499deb --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/EntryBlock.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.graphs.cfg; + +public class EntryBlock extends BasicBlock { + + private static final long serialVersionUID = -4279595207017734232L; + + /** + *

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..52472c1f44 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ExitBlock.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.graphs.cfg; + +public class ExitBlock extends BasicBlock { + + private static final long serialVersionUID = 970110985248711972L; + + /** + *

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..3a3d4bc93e --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/RawControlFlowGraph.java @@ -0,0 +1,487 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +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.objectweb.asm.tree.LabelNode; +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. + * + * @author Andre Mis + */ +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); + } + + // @Override + // public BytecodeInstruction getBranch(int branchId) { + // for (BytecodeInstruction v : vertexSet()) { + // if (v.isBranch() && v.getControlDependentBranchId() == branchId) { + // return v; + // } + // } + // return 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)) + if (src.isBranch()) + return addBranchEdge(src, target, isExceptionEdge); + else if (src.isSwitch()) + return addSwitchBranchEdge(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 addSwitchBranchEdge(BytecodeInstruction src, + BytecodeInstruction target, boolean isExceptionEdge) { + if (!target.isLabel()) + throw new IllegalStateException( + "expect control flow edges from switch statements to always target labelNodes"); + + LabelNode label = (LabelNode) target.getASMNode(); + + List switchCaseBranches = BranchPool.getInstance(classLoader).getBranchForLabel(label); + + if (switchCaseBranches == null) { + SimpleLogger.debug("not a switch case label: " + label.toString() + " " + + target); + return internalAddEdge(src, target, new ControlFlowEdge(isExceptionEdge)); + } + // throw new IllegalStateException( + // "expect BranchPool to contain a Branch for each switch-case-label"+src.toString()+" to "+target.toString()); + + // TODO there is an inconsistency when it comes to switches with + // empty case: blocks. they do not have their own label, so there + // can be multiple ControlFlowEdges from the SWITCH instruction to + // one LabelNode. + // But currently our RawCFG does not permit multiple edges between + // two nodes + + for (Branch switchCaseBranch : switchCaseBranches) { + + // TODO n^2 + Set soFar = incomingEdgesOf(target); + boolean handled = false; + for (ControlFlowEdge old : soFar) + if (switchCaseBranch.equals(old.getBranchInstruction())) + handled = true; + + if (handled) + continue; + /* + * previous try to add fake intermediate nodes for each empty case + * block to help the CDG - unsuccessful: + * if(switchCaseBranches.size()>1) { // // e = new + * ControlFlowEdge(isExceptionEdge); // + * e.setBranchInstruction(switchCaseBranch); // + * e.setBranchExpressionValue(true); // BytecodeInstruction + * fakeInstruction = + * BytecodeInstructionPool.createFakeInstruction(className + * ,methodName); // addVertex(fakeInstruction); // + * internalAddEdge(src,fakeInstruction,e); // // e = new + * ControlFlowEdge(isExceptionEdge); // + * e.setBranchInstruction(switchCaseBranch); // + * e.setBranchExpressionValue(true); // // e = + * internalAddEdge(fakeInstruction,target,e); // } else { + */ + + ControlDependency cd = new ControlDependency(switchCaseBranch, true); + ControlFlowEdge e = new ControlFlowEdge(cd, isExceptionEdge); + + e = internalAddEdge(src, target, e); + + } + + return new ControlFlowEdge(isExceptionEdge); + } + + 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; + + } + + /** + *

+ * 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; + } + + // control distance functionality + + /** + * Returns the Set of BytecodeInstructions that can potentially be executed + * from entering the method of this CFG until the given BytecodeInstruction + * is reached. + * + * @param v a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @return a {@link java.util.Set} object. + */ + public Set getPreviousInstructionsInMethod(BytecodeInstruction v) { + Set visited = new HashSet<>(); + PriorityQueue queue = new PriorityQueue<>( + graph.vertexSet().size(), new BytecodeInstructionIdComparator()); + queue.add(v); + while (queue.peek() != null) { + BytecodeInstruction current = queue.poll(); + if (visited.contains(current)) + continue; + Set incomingEdges = graph.incomingEdgesOf(current); + for (ControlFlowEdge incomingEdge : incomingEdges) { + BytecodeInstruction source = graph.getEdgeSource(incomingEdge); + if (source.getInstructionId() >= current.getInstructionId()) + continue; + queue.add(source); + } + visited.add(current); + } + return visited; + } + + /** + * Returns the Set of BytecodeInstructions that can potentially be executed + * from passing the given BytecodeInstruction until the end of the method of + * this CFG is reached. + * + * @param v a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @return a {@link java.util.Set} object. + */ + public Set getLaterInstructionsInMethod(BytecodeInstruction v) { + Set visited = new HashSet<>(); + Comparator reverseComp = new BytecodeInstructionIdComparator().reversed(); + PriorityQueue queue = new PriorityQueue<>( + graph.vertexSet().size(), reverseComp); + queue.add(v); + while (queue.peek() != null) { + BytecodeInstruction current = queue.poll(); + if (visited.contains(current)) + continue; + Set outgoingEdges = graph.outgoingEdgesOf(current); + for (ControlFlowEdge outgoingEdge : outgoingEdges) { + BytecodeInstruction target = graph.getEdgeTarget(outgoingEdge); + if (target.getInstructionId() < current.getInstructionId()) + continue; + queue.add(target); + } + visited.add(current); + } + return visited; + } + + // 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..0610ae56da --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/Branch.java @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch; + +import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction; +import org.objectweb.asm.tree.LabelNode; + +import java.io.Serializable; + +/** + * An object of this class corresponds to a Branch inside the class under test. + * + *

+ * Branches are created by the {@code CFGMethodAdapter} 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()} - which will be called normal branches - or it + * can be associated with a case: of a switch statement as defined in + * {@code BytecodeInstruction.isSwitch()} - which will be called switch case branches. + * Only {@code BytecodeInstructions} satisfying {@code BytecodeInstruction.isActualbranch()} are + * expected to be associated with a {@code Branch} object. + * + *

+ * For SWITCH statements each case : block corresponds to a {@code Branch} that can + * be created by constructing a {@code Branch} with the SWITCH statement and the + * as the targetCaseValue. The default: case of switch statement can also be + * modeled this way - it has the {@code targetCaseValue} set to {@code null}. + * + * @author Andre Mis + */ +public class Branch implements Serializable, Comparable { + + private static final long serialVersionUID = -4732587925060748263L; + + private final int actualBranchId; + + private boolean isSwitch = false; + + // for switch branches this value indicates to which case of the switch this + // branch belongs. if this value is null and this is in fact a switch this + // means this branch is the default: case of that switch + private Integer targetCaseValue = null; + + private final BytecodeInstruction instruction; + + /** + * Keep track of branches that were introduced as part of TT + */ + private boolean isInstrumented = false; + + /** + * Constructor for usual jump instruction Branches, that are not SWITCH + * instructions. + * + * @param branchInstruction a {@link org.evosuite.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"); + } + + /** + * Constructor for switch case branches + * + * @param switchInstruction a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. + * @param targetCaseValue a {@link java.lang.Integer} object. + * @param targetLabel a {@link org.objectweb.asm.tree.LabelNode} object. + * @param actualBranchId a int. + */ + public Branch(BytecodeInstruction switchInstruction, Integer targetCaseValue, + LabelNode targetLabel, int actualBranchId) { + if (!switchInstruction.isSwitch()) + throw new IllegalArgumentException("switch instruction expected"); + if (targetLabel == null) + throw new IllegalArgumentException( + "expect targetLabel to not be null for case branches"); + + this.instruction = switchInstruction; + this.actualBranchId = actualBranchId; + + // this.targetLabel = targetLabel; + this.targetCaseValue = targetCaseValue; + this.isSwitch = true; + + 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; + } + + /** + * Tells whether this branch corresponds to the {@code default} case within a Java + * {@code switch} statement. + * + * @return {@code true} if this branch represents the {@code default} case + */ + public boolean isDefaultCase() { + return isSwitch && targetCaseValue == null; + } + + /** + * Tells whether this branch corresponds to a statement labeled with {@code case} within a Java + * {@code switch} statement. + * + * @return {@code true} if the statement represented by this branch is labeled with {@code case} + * @see Branch#isDefaultCase() + */ + public boolean isActualCase() { + return isSwitch && targetCaseValue != null; + } + + /** + *

+ * Getter for the field targetCaseValue. + *

+ * + * @return a {@link java.lang.Integer} object. + */ + public Integer getTargetCaseValue() { + // in order to avoid confusion when targetCaseValue is null + if (!isSwitch) + throw new IllegalStateException( + "method only allowed to be called on non-switch-Branches"); + + return targetCaseValue; // null for default case + } + + /** + *

+ * Getter for the field instruction. + *

+ * + * @return a {@link org.evosuite.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(); + } + + /** + *

+ * isSwitchCaseBranch + *

+ * + * @return a boolean. + */ + public boolean isSwitchCaseBranch() { + return isSwitch; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + actualBranchId; + result = prime * result + ((instruction == null) ? 0 : instruction.hashCode()); + result = prime * result + (isSwitch ? 1231 : 1237); + result = prime * result + + ((targetCaseValue == null) ? 0 : targetCaseValue.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; + if (isSwitch != other.isSwitch) + return false; + if (targetCaseValue == null) { + return other.targetCaseValue == null; + } else return targetCaseValue.equals(other.targetCaseValue); + } + + /* (non-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + @Override + public int compareTo(Branch other) { + return instruction.getLineNumber() - other.getInstruction().getLineNumber(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + String r = "I" + instruction.getInstructionId(); + r += " Branch " + getActualBranchId(); + r += " " + instruction.getInstructionType(); + if (isSwitch) { + r += " L" + instruction.getLineNumber(); + if (targetCaseValue != null) + r += " Case " + targetCaseValue; + else + r += " Default-Case"; + } else + r += " L" + instruction.getLineNumber(); + + return r; + } + + /** + *

+ * isInstrumented + *

+ * + * @return a boolean. + */ + public boolean isInstrumented() { + return isInstrumented; + } + + /** + *

+ * setInstrumented + *

+ * + * @param isInstrumented a boolean. + */ + public void setInstrumented(boolean isInstrumented) { + this.isInstrumented = isInstrumented; + } + + private boolean ignoreFalse = false; + + public boolean ignoreFalseBranch() { + return ignoreFalse; + } + + public void setIgnoreFalse(boolean value) { + ignoreFalse = value; + } +} 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..8b5af7a40a --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchPool.java @@ -0,0 +1,834 @@ +/* + * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite + * contributors + * + * This file is part of EvoSuite. + * + * EvoSuite is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * EvoSuite is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with EvoSuite. If not, see . + */ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch; + +import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.LookupSwitchInsnNode; +import org.objectweb.asm.tree.TableSwitchInsnNode; +import org.evomaster.client.java.utils.SimpleLogger; + +import java.util.*; + +// TODO: root branches should not be special cases +// every root branch should be a branch just +// like every other branch with it's own branchId and all + +/** + * 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. + *

+ * There are two kinds of Branch objects: normal branches and switch case + * branches. For more details about the difference between these two look at the + * Branch class. + * + * @author Andre Mis + */ +public class BranchPool { + + // maps className -> method inside that class -> list of branches inside + // that method + private final Map>> branchMap = new HashMap<>(); + + // set of all known methods without a Branch + private final Map> branchlessMethods = new HashMap<>(); + + // maps the branchIDs assigned by this pool to their respective Branches + private final Map branchIdMap = new HashMap<>(); + + // maps all known branch instructions to their branchId + private final Map registeredNormalBranches = new HashMap<>(); + + // maps all known switch instructions to a list containing all of their + // associated Branch objects + private final Map> registeredSwitches = new HashMap<>(); + + private final Map registeredDefaultCases = new HashMap<>(); + + private final Map> switchLabels = new HashMap<>(); + + // number of known Branches - used for actualBranchIds + private int branchCounter = 0; + + private static final Map instanceMap = new HashMap<>(); + + public static BranchPool getInstance(ClassLoader classLoader) { + if (!instanceMap.containsKey(classLoader)) { + instanceMap.put(classLoader, new BranchPool()); + } + + return instanceMap.get(classLoader); + } + // fill the pool + + /** + * Gets called by the CFGMethodAdapter whenever it detects a method without + * any branches. + * + * @param methodName Unique methodName - consisting of . - + * of a method without Branches + * @param className a {@link java.lang.String} object. + */ + public void addBranchlessMethod(String className, String methodName, + int lineNumber) { + if (!branchlessMethods.containsKey(className)) + branchlessMethods.put(className, new HashMap<>()); + branchlessMethods.get(className).put(methodName, lineNumber); + } + + /** + * 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.evosuite.graphs.cfg.BytecodeInstruction} object. + */ + public void registerAsBranch(BytecodeInstruction instruction) { + if (!(instruction.isActualBranch())) + 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()) + registerNormalBranchInstruction(v); + else if (v.isSwitch()) + registerSwitchInstruction(v); + else + throw new IllegalArgumentException( + "expect given instruction to be an actual branch"); + } + + 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++; + registeredNormalBranches.put(v, branchCounter); + + Branch b = new Branch(v, branchCounter); + addBranchToMap(b); + branchIdMap.put(branchCounter, b); + + SimpleLogger.info("Branch " + branchCounter + " at line " + v.getLineNumber()); + } + + private void registerSwitchInstruction(BytecodeInstruction v) { + if (!v.isSwitch()) + throw new IllegalArgumentException("expect a switch instruction"); + + LabelNode defaultLabel; + + switch (v.getASMNode().getOpcode()) { + case Opcodes.TABLESWITCH: + TableSwitchInsnNode tableSwitchNode = (TableSwitchInsnNode) v.getASMNode(); + registerTableSwitchCases(v, tableSwitchNode); + defaultLabel = tableSwitchNode.dflt; + + break; + case Opcodes.LOOKUPSWITCH: + LookupSwitchInsnNode lookupSwitchNode = (LookupSwitchInsnNode) v.getASMNode(); + registerLookupSwitchCases(v, lookupSwitchNode); + defaultLabel = lookupSwitchNode.dflt; + break; + default: + throw new IllegalStateException( + "expect ASMNode of a switch to either be a LOOKUP- or TABLESWITCH"); + } + + registerDefaultCase(v, defaultLabel); + } + + private void registerDefaultCase(BytecodeInstruction v, LabelNode defaultLabel) { + + if (defaultLabel == null) + throw new IllegalStateException("expect variable to bet set"); + + Branch defaultBranch = createSwitchCaseBranch(v, null, defaultLabel); + if (!defaultBranch.isSwitchCaseBranch() || !defaultBranch.isDefaultCase()) + throw new IllegalStateException( + "expect created branch to be a default case branch of a switch"); + } + + private void registerTableSwitchCases(BytecodeInstruction v, + TableSwitchInsnNode tableSwitchNode) { + + int num = 0; + + for (int i = tableSwitchNode.min; i <= tableSwitchNode.max; i++) { + LabelNode targetLabel = tableSwitchNode.labels.get(num); + Branch switchBranch = createSwitchCaseBranch(v, i, targetLabel); + if (!switchBranch.isSwitchCaseBranch() || !switchBranch.isActualCase()) + throw new IllegalStateException( + "expect created branch to be an actual case branch of a switch"); + num++; + } + } + + private void registerLookupSwitchCases(BytecodeInstruction v, + LookupSwitchInsnNode lookupSwitchNode) { + + for (int i = 0; i < lookupSwitchNode.keys.size(); i++) { + LabelNode targetLabel = lookupSwitchNode.labels.get(i); + Branch switchBranch = createSwitchCaseBranch(v, + lookupSwitchNode.keys.get(i), + targetLabel); + if (!switchBranch.isSwitchCaseBranch() || !switchBranch.isActualCase()) + throw new IllegalStateException( + "expect created branch to be an actual case branch of a switch"); + } + } + + private Branch createSwitchCaseBranch(BytecodeInstruction v, + Integer caseValue, LabelNode targetLabel) { + + branchCounter++; + + Branch switchBranch = new Branch(v, caseValue, targetLabel, branchCounter); + registerSwitchBranch(v, switchBranch); + addBranchToMap(switchBranch); + branchIdMap.put(branchCounter, switchBranch); + + registerSwitchLabel(switchBranch, targetLabel); + + // default case + if (caseValue == null) { + if (registeredDefaultCases.containsKey(v)) + throw new IllegalStateException( + "instruction already registered as a branch"); + registeredDefaultCases.put(v, switchBranch); + } + + if (!switchBranch.isSwitchCaseBranch()) + throw new IllegalStateException("expect created Branch to be a switch branch"); + + return switchBranch; + } + + private void registerSwitchLabel(Branch b, LabelNode targetLabel) { + + if (switchLabels.get(targetLabel) == null) + switchLabels.put(targetLabel, new ArrayList<>()); + + List oldList = switchLabels.get(targetLabel); + + if (oldList.contains(b)) + throw new IllegalStateException( + "branch already registered for this switch label"); + + oldList.add(b); + + // TODO several Branches can map to one Label, so switchLabels should + // either map from branches to labels, not the other way around. or it + // should map labels to a list of branches + // this stems from the fact that empty case: blocks do not have their + // own label + + // TODO STOPPED HERE + + switchLabels.put(targetLabel, oldList); + } + + private void registerSwitchBranch(BytecodeInstruction v, Branch switchBranch) { + if (!v.isSwitch()) + throw new IllegalArgumentException("switch instruction expected"); + + if (registeredSwitches.get(v) == null) + registeredSwitches.put(v, new ArrayList<>()); + + List oldList = registeredSwitches.get(v); + + if (oldList.contains(switchBranch)) + throw new IllegalArgumentException("switch branch already registered " + + switchBranch.toString()); + + oldList.add(switchBranch); + + registeredSwitches.put(v, oldList); + } + + private void addBranchToMap(Branch b) { + + SimpleLogger.info("Adding to map the branch " + b.toString()); + + String className = b.getClassName(); + String methodName = b.getMethodName(); + + if (!branchMap.containsKey(className)) + branchMap.put(className, new HashMap<>()); + if (!branchMap.get(className).containsKey(methodName)) + branchMap.get(className).put(methodName, new ArrayList<>()); + branchMap.get(className).get(methodName).add(b); + } + + // 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.evosuite.graphs.cfg.BytecodeInstruction} object. + * @return a boolean. + */ + public boolean isKnownAsBranch(BytecodeInstruction instruction) { + return isKnownAsNormalBranchInstruction(instruction) + || isKnownAsSwitchBranchInstruction(instruction); + } + + /** + *

+ * isKnownAsNormalBranchInstruction + *

+ * + * @param ins a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. + * @return a boolean. + */ + public boolean isKnownAsNormalBranchInstruction(BytecodeInstruction ins) { + + return registeredNormalBranches.containsKey(ins); + } + + /** + *

+ * isKnownAsSwitchBranchInstruction + *

+ * + * @param instruction a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. + * @return a boolean. + */ + public boolean isKnownAsSwitchBranchInstruction(BytecodeInstruction instruction) { + + return registeredSwitches.containsKey(instruction); + } + + /** + *

+ * getActualBranchIdForNormalBranchInstruction + *

+ * + * @param ins a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. + * @return a int. + */ + public int getActualBranchIdForNormalBranchInstruction(BytecodeInstruction ins) { + if (!isKnownAsNormalBranchInstruction(ins)) + throw new IllegalArgumentException( + "instruction not registered as a normal branch"); + + if (registeredNormalBranches.containsKey(ins)) + return registeredNormalBranches.get(ins); + + throw new IllegalStateException( + "expect registeredNormalBranches to contain a key for each known normal branch instruction"); + } + + /** + *

+ * getCaseBranchesForSwitch + *

+ * + * @param instruction a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. + * @return a {@link java.util.List} object. + */ + public List getCaseBranchesForSwitch(BytecodeInstruction instruction) { + if (instruction == null) + throw new IllegalArgumentException("null given"); + if (!instruction.isSwitch()) + throw new IllegalArgumentException("switch instruction expected"); + if (!isKnownAsSwitchBranchInstruction(instruction)) + throw new IllegalArgumentException("not registered as a switch instruction"); + + return registeredSwitches.get(instruction); + } + + /** + *

+ * getBranchForInstruction + *

+ * + * @param instruction a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. + * @return a {@link org.evosuite.coverage.branch.Branch} object. + */ + public Branch getBranchForInstruction(BytecodeInstruction instruction) { + if (instruction == null) + throw new IllegalArgumentException("null given"); + if (!isKnownAsNormalBranchInstruction(instruction)) + throw new IllegalArgumentException( + "expect given instruction to be known as a normal branch"); + + return getBranch(registeredNormalBranches.get(instruction)); + } + + /** + *

+ * getBranchForLabel + *

+ * + * @param label a {@link org.objectweb.asm.tree.LabelNode} object. + * @return a {@link java.util.List} object. + */ + public List getBranchForLabel(LabelNode label) { + + // TODO see registerSwitchLabel()! + + return switchLabels.get(label); + } + + /** + * Returns the number of known Branches for a given methodName in a given + * class. + * + * @param className a {@link java.lang.String} object. + * @param methodName a {@link java.lang.String} object. + * @return The number of currently known Branches inside the given method + */ + public int getBranchCountForMethod(String className, String methodName) { + if (branchMap.get(className) == null) + return 0; + if (branchMap.get(className).get(methodName) == null) + return 0; + + return branchMap.get(className).get(methodName).size(); + } + + public int getNonArtificialBranchCountForMethod(String className, + String methodName) { + if (branchMap.get(className) == null) + return 0; + if (branchMap.get(className).get(methodName) == null) + return 0; + + int num = 0; + for (Branch b : branchMap.get(className).get(methodName)) { + if (!b.isInstrumented()) + num++; + } + + return num; + } + + /** + * Returns the number of known Branches for a given class + * + * @param className a {@link java.lang.String} object. + * @return The number of currently known Branches inside the given class + */ + public int getBranchCountForClass(String className) { + if (branchMap.get(className) == null) + return 0; + int total = 0; + for (String method : branchMap.get(className).keySet()) { + total += branchMap.get(className).get(method).size(); + } + return total; + } + + /** + * Returns the number of known Branches for a given class + * + * @param prefix a {@link java.lang.String} object. + * @return The number of currently known Branches inside the given class + */ + public int getBranchCountForPrefix(String prefix) { + int num = 0; + for (String className : branchMap.keySet()) { + if (className.startsWith(prefix)) { + SimpleLogger.info("Found matching class for branch count: " + className + "/" + + prefix); + for (String method : branchMap.get(className).keySet()) { + num += branchMap.get(className).get(method).size(); + } + } + } + return num; + } + + /** + * Returns the number of known Branches for a given class + * + * @param prefix a {@link java.lang.String} object. + * @return The number of currently known Branches inside the given class + */ + public Set getBranchIdsForPrefix(String prefix) { + Set ids = new LinkedHashSet<>(); + Set sutBranches = new LinkedHashSet<>(); + for (String className : branchMap.keySet()) { + if (className.startsWith(prefix)) { + SimpleLogger.info("Found matching class for branch ids: " + className + "/" + + prefix); + for (String method : branchMap.get(className).keySet()) { + sutBranches.addAll(branchMap.get(className).get(method)); + } + } + } + + for (Integer id : branchIdMap.keySet()) { + if (sutBranches.contains(branchIdMap.get(id))) { + ids.add(id); + } + } + + return ids; + } + + /** + * Returns the number of known Branches for a given class + * + * @param prefix a {@link java.lang.String} object. + * @return The number of currently known Branches inside the given class + */ + public int getBranchCountForMemberClasses(String prefix) { + int num = 0; + for (String className : branchMap.keySet()) { + if (className.equals(prefix) || className.startsWith(prefix + "$")) { + SimpleLogger.info("Found matching class for branch count: " + className + "/" + + prefix); + for (String method : branchMap.get(className).keySet()) { + num += branchMap.get(className).get(method).size(); + } + } + } + return num; + } + + /** + * Returns the number of currently known Branches + * + * @return The number of currently known Branches + */ + public int getBranchCounter() { + return branchCounter; + } + + public int getNumArtificialBranches() { + int num = 0; + for (Branch b : branchIdMap.values()) { + if (b.isInstrumented()) + num++; + } + + return num; + } + + /** + * Returns the Branch object associated with the given branchID + * + * @param branchId The ID of a branch + * @return The branch, or null if it does not exist + */ + public Branch getBranch(int branchId) { + + return branchIdMap.get(branchId); + } + + public Collection getAllBranches() { + return branchIdMap.values(); + } + + /** + * Returns a set with all unique methodNames of methods without Branches. + * + * @param className a {@link java.lang.String} object. + * @return A set with all unique methodNames of methods without Branches. + */ + public Set getBranchlessMethods(String className) { + if (!branchlessMethods.containsKey(className)) + return new LinkedHashSet<>(); + + return branchlessMethods.get(className).keySet(); + } + + /** + * Returns a set with all unique methodNames of methods without Branches. + * + * @param className a {@link java.lang.String} object. + * @return A set with all unique methodNames of methods without Branches. + */ + public Set getBranchlessMethodsPrefix(String className) { + Set methods = new LinkedHashSet<>(); + + for (String name : branchlessMethods.keySet()) { + if (name.equals(className) || name.startsWith(className + "$")) { + methods.addAll(branchlessMethods.get(name).keySet()); + } + } + + return methods; + } + + /** + * Returns a set with all unique methodNames of methods without Branches. + * + * @param className a {@link java.lang.String} object. + * @return A set with all unique methodNames of methods without Branches. + */ + public Set getBranchlessMethodsMemberClasses(String className) { + Set methods = new LinkedHashSet<>(); + + for (String name : branchlessMethods.keySet()) { + if (name.equals(className) || name.startsWith(className + "$")) { + methods.addAll(branchlessMethods.get(name).keySet()); + } + } + + return methods; + } + + /** + * Returns a set with all unique methodNames of methods without Branches. + * + * @return A set with all unique methodNames of methods without Branches. + */ + public Set getBranchlessMethods() { + Set methods = new LinkedHashSet<>(); + + for (String name : branchlessMethods.keySet()) { + methods.addAll(branchlessMethods.get(name).keySet()); + } + + return methods; + } + + public boolean isBranchlessMethod(String className, String methodName) { + Map methodMap = branchlessMethods.get(className); + if (methodMap != null) { + return methodMap.containsKey(methodName); + } + return false; + } + + /** + * Returns the number of methods without Branches for class className + * + * @param className a {@link java.lang.String} object. + * @return The number of methods without Branches. + */ + public int getNumBranchlessMethods(String className) { + if (!branchlessMethods.containsKey(className)) + return 0; + return branchlessMethods.get(className).size(); + } + + /** + * Returns the number of methods without Branches for class className + * + * @param className a {@link java.lang.String} object. + * @return The number of methods without Branches. + */ + public int getNumBranchlessMethodsPrefix(String className) { + int num = 0; + for (String name : branchlessMethods.keySet()) { + if (name.startsWith(className)) + num += branchlessMethods.get(name).size(); + } + return num; + } + + /** + * Returns the number of methods without Branches for class className + * + * @param className a {@link java.lang.String} object. + * @return The number of methods without Branches. + */ + public int getNumBranchlessMethodsMemberClasses(String className) { + int num = 0; + for (String name : branchlessMethods.keySet()) { + if (name.equals(className) || name.startsWith(className + "$")) + num += branchlessMethods.get(name).size(); + } + return num; + } + + /** + * Returns the total number of methods without branches in the instrumented + * classes + * + * @return + */ + public int getNumBranchlessMethods() { + int num = 0; + for (String name : branchlessMethods.keySet()) { + num += branchlessMethods.get(name).size(); + } + return num; + } + + /** + * Returns a Set containing all classes for which this pool knows Branches + * for as Strings + * + * @return a {@link java.util.Set} object. + */ + public Set knownClasses() { + Set r = new LinkedHashSet<>(); + r.addAll(branchMap.keySet()); + r.addAll(branchlessMethods.keySet()); + + return r; + } + + /** + * Returns a Set containing all methods in the class represented by the + * given String for which this pool knows Branches for as Strings + * + * @param className a {@link java.lang.String} object. + * @return a {@link java.util.Set} object. + */ + public Set knownMethods(String className) { + Set r = new LinkedHashSet<>(); + Map> methods = branchMap.get(className); + if (methods != null) + r.addAll(methods.keySet()); + + return r; + } + + /** + * Returns a List containing all Branches in the given class and method + *

+ * Should no such Branch exist an empty List is returned + * + * @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 retrieveBranchesInMethod(String className, + String methodName) { + List r = new ArrayList<>(); + if (branchMap.get(className) == null) + return r; + List branches = branchMap.get(className).get(methodName); + if (branches != null) + r.addAll(branches); + return r; + } + + /** + *

+ * getDefaultBranchForSwitch + *

+ * + * @param v a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. + * @return a {@link org.evosuite.coverage.branch.Branch} object. + */ + public Branch getDefaultBranchForSwitch(BytecodeInstruction v) { + if (!v.isSwitch()) + throw new IllegalArgumentException("switch instruction expected"); + if (!isKnownAsSwitchBranchInstruction(v)) + throw new IllegalArgumentException( + "instruction not known to be a switch instruction"); + if (!registeredDefaultCases.containsKey(v)) + throw new IllegalArgumentException( + "there is no registered default case for this instruction"); + + return registeredDefaultCases.get(v); + } + + /** + * Reset all the data structures used to keep track of the branch + * information + */ + public void reset() { + branchCounter = 0; + branchMap.clear(); + branchlessMethods.clear(); + branchIdMap.clear(); + registeredNormalBranches.clear(); + registeredSwitches.clear(); + registeredDefaultCases.clear(); + switchLabels.clear(); + } + + /** + *

+ * clear + *

+ *

+ * TODO: One of these two methods should go + */ + public void clear() { + branchCounter = 0; + branchMap.clear(); + branchIdMap.clear(); + branchlessMethods.clear(); + switchLabels.clear(); + registeredDefaultCases.clear(); + registeredNormalBranches.clear(); + registeredSwitches.clear(); + } + + /** + *

+ * clear + *

+ * + * @param className a {@link java.lang.String} object. + */ + public void clear(String className) { + branchMap.remove(className); + branchlessMethods.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) { + int numBranches = 0; + + if (branchMap.containsKey(className)) { + if (branchMap.get(className).containsKey(methodName)) + numBranches = branchMap.get(className).get(methodName).size(); + branchMap.get(className).remove(methodName); + } + if (branchlessMethods.containsKey(className)) + branchlessMethods.get(className).remove(methodName); + SimpleLogger.info("Resetting branchCounter from " + branchCounter + " to " + + (branchCounter - numBranches)); + branchCounter -= numBranches; + } + +} 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..62bc8e004a 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,6 +4,7 @@ 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 java.io.IOException; import java.io.ObjectInputStream; @@ -65,6 +66,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); @@ -121,6 +126,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 && dto.enableGraphs != null) { + DynamosaConfig.setWriteCfgEnabled(dto.writeCfg); + } + } catch (Exception e) { + SimpleLogger.error("Failure in handling DYNAMOSA config: "+e.getMessage()); + } + } + private static void sendCommand(Command command){ try { 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..49d3a2ca12 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,6 @@ public enum Command implements Serializable { EXECUTING_ACTION, BOOT_TIME_INFO, EXTRACT_JVM_DTO, - BOOTING_SUT + BOOTING_SUT, + SET_DYNAMOSA_CONFIG } 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..c85695af0b --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/DynamosaConfigDto.java @@ -0,0 +1,10 @@ +package org.evomaster.client.java.instrumentation.external; + +import java.io.Serializable; + +public class DynamosaConfigDto implements Serializable { + public Boolean enableGraphs; + public Boolean writeCfg; +} + + 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..ccd5e6e4cf 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 @@ -216,6 +216,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) { diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index fbfdfa62ea..0a48850854 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1161,7 +1161,7 @@ class EMConfig { var avoidNonDeterministicLogs = false enum class Algorithm { - DEFAULT, SMARTS, MIO, RANDOM, WTS, MOSA, RW, + DEFAULT, SMARTS, MIO, RANDOM, WTS, MOSA, RW, DYNAMOSA, StandardGA, MonotonicGA, SteadyStateGA, BreederGA, CellularGA, OnePlusLambdaLambdaGA, MuLambdaEA, MuPlusLambdaEA, LIPS // GA variants still work-in-progress. } @@ -2349,6 +2349,13 @@ class EMConfig { " A negative value means all data in the collection will be asserted (i.e., no limit).") var maxAssertionForDataInCollection = 3 + /** + * Enable writing Control-Flow and related graphs (e.g., DOT/PNG) on the agent side. + * This controls only the persistence of graphs to disk; graph creation is controlled separately. + */ + @Cfg("Enable writing CFG/CDG graphs to disk on the agent side") + var writeCfg: Boolean = false + @Cfg("Specify whether to employ smart database clean to clear data in the database if the SUT has." + "`null` represents to employ the setting specified on the EM driver side") var employSmartDbClean: Boolean? = null diff --git a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt index 4d81a70f22..e6346c1666 100644 --- a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt +++ b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt @@ -258,6 +258,10 @@ class RemoteControllerImplementation() : RemoteController{ config.methodReplacementCategories() ) requestDto.advancedHeuristics = config.heuristicsForSQLAdvanced + + // Pass Dynamosa settings from core to controller/driver + requestDto.enableDynamosaGraphs = config.algorithm.toString() == "DYNAMOSA" + requestDto.writeCfg = config.writeCfg val response = try { makeHttpCall { From 26aa6435a2cfa31160b796a7f465887e07fed513 Mon Sep 17 00:00:00 2001 From: francastagna Date: Tue, 25 Nov 2025 08:13:09 -0300 Subject: [PATCH 02/21] wip: testing graph creation --- .../dynamosa/DynamosaConfig.java | 15 +- .../dynamosa/graphs/cdg/DominatorNode.java | 2 +- .../dynamosa/graphs/cfg/ASMWrapper.java | 2 +- .../cfg/BytecodeInstructionFactory.java | 48 -- .../cfg/BytecodeInstructionIdComparator.java | 42 -- .../graphs/cfg/BytecodeInstructionPool.java | 6 +- .../dynamosa/graphs/cfg/CFGClassAdapter.java | 2 +- .../dynamosa/graphs/cfg/CFGFrame.java | 2 +- .../dynamosa/graphs/cfg/EntryBlock.java | 2 +- .../dynamosa/graphs/cfg/ExitBlock.java | 2 +- .../graphs/cfg/RawControlFlowGraph.java | 133 +----- .../dynamosa/graphs/cfg/branch/Branch.java | 32 ++ .../graphs/cfg/branch/BranchPool.java | 424 +++--------------- .../external/AgentController.java | 2 +- .../kotlin/org/evomaster/core/EMConfig.kt | 2 +- .../main/kotlin/org/evomaster/core/Main.kt | 12 + .../search/algorithms/DynamosaAlgorithm.kt | 308 +++++++++++++ 17 files changed, 440 insertions(+), 596 deletions(-) delete mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionFactory.java delete mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionIdComparator.java create mode 100644 core/src/main/kotlin/org/evomaster/core/search/algorithms/DynamosaAlgorithm.kt 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 index 6b98bf172f..8c3d74bfa0 100644 --- 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 @@ -6,22 +6,33 @@ */ public class DynamosaConfig { - private static volatile boolean enableGraphs = false; - private static volatile boolean writeCfg = false; + private static volatile boolean enableGraphs = true; + private static volatile boolean writeCfg = true; + 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/cdg/DominatorNode.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorNode.java index 8b59cb4274..b0167840b7 100755 --- 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 @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with EvoSuite. If not, see . */ -package org.evomaster.client.java.graphs.cdg; +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg; import java.util.HashSet; import java.util.Set; diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ASMWrapper.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ASMWrapper.java index 9b0a1df0e6..39317b6929 100755 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ASMWrapper.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ASMWrapper.java @@ -153,7 +153,7 @@ public String getType() { * @return a boolean. */ public boolean isActualBranch() { - return isBranch() || isSwitch(); + return isBranch(); } /** diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionFactory.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionFactory.java deleted file mode 100755 index 24cb3abf9c..0000000000 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionFactory.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . - */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; - -import org.objectweb.asm.tree.AbstractInsnNode; - -public class BytecodeInstructionFactory { - - /** - *

- * createBytecodeInstruction - *

- * - * @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 node a {@link org.objectweb.asm.tree.AbstractInsnNode} object. - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. - */ - public static BytecodeInstruction createBytecodeInstruction(ClassLoader classLoader, - String className, String methodName, int instructionId, int bytecodeOffset, - AbstractInsnNode node) { - - BytecodeInstruction instruction = new BytecodeInstruction(classLoader, className, - methodName, instructionId, bytecodeOffset, node); - - return instruction; - } - -} diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionIdComparator.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionIdComparator.java deleted file mode 100755 index 86e4009c1f..0000000000 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionIdComparator.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . - */ -package org.evomaster.client.java.graphs.cfg; - -import java.util.Comparator; - -/** - * Orders CFGVertices according to their id - *

- * This is mainly used to put BytecodeInstructions into a PriorityQueue in - * ControlFlowGraph.getMaximalInitialDistance() - * - * @author Gordon Fraser - */ -public class BytecodeInstructionIdComparator implements Comparator { - - /** - * {@inheritDoc} - */ - @Override - public int compare(BytecodeInstruction arg0, BytecodeInstruction arg1) { - - return Integer.compare(arg0.getInstructionId(), arg1.getInstructionId()); - } -} 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 index 530667241a..aaaf94d59d 100755 --- 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 @@ -86,12 +86,14 @@ public List registerMethodNode(MethodNode node, for (int instructionId = 0; instructionId < node.instructions.size(); instructionId++) { AbstractInsnNode instructionNode = node.instructions.get(instructionId); - BytecodeInstruction instruction = BytecodeInstructionFactory.createBytecodeInstruction(classLoader, + BytecodeInstruction instruction = new BytecodeInstruction( + classLoader, className, methodName, instructionId, bytecodeOffset, - instructionNode); + instructionNode + ); if (instruction.isLineNumber()) lastLineNumber = instruction.getLineNumber(); diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGClassAdapter.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGClassAdapter.java index 97b38e4a96..6956826860 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGClassAdapter.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGClassAdapter.java @@ -1,4 +1,4 @@ -package org.evomaster.client.java.graphs.cfg; +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGFrame.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGFrame.java index 0e7ab6d5d1..c46cdf5cf4 100755 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGFrame.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGFrame.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with EvoSuite. If not, see . */ -package org.evomaster.client.java.graphs.cfg; +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; import org.objectweb.asm.tree.analysis.Frame; 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 index a5aa499deb..49d2ace11b 100755 --- 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 @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with EvoSuite. If not, see . */ -package org.evomaster.client.java.graphs.cfg; +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; public class EntryBlock extends BasicBlock { 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 index 52472c1f44..3c3bc08e73 100755 --- 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 @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with EvoSuite. If not, see . */ -package org.evomaster.client.java.graphs.cfg; +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; public class ExitBlock extends BasicBlock { 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 index 3a3d4bc93e..6acb78614f 100755 --- 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 @@ -22,7 +22,6 @@ import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch; import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.BranchPool; -import org.objectweb.asm.tree.LabelNode; import org.evomaster.client.java.utils.SimpleLogger; import java.util.*; @@ -112,11 +111,9 @@ protected ControlFlowEdge addEdge(BytecodeInstruction src, SimpleLogger.debug("Adding edge to RawCFG of " + className + "." + methodName + ": " + this.vertexCount()); - if (BranchPool.getInstance(classLoader).isKnownAsBranch(src)) - if (src.isBranch()) - return addBranchEdge(src, target, isExceptionEdge); - else if (src.isSwitch()) - return addSwitchBranchEdge(src, target, isExceptionEdge); + if (BranchPool.getInstance(classLoader).isKnownAsBranch(src) && src.isBranch()) { + return addBranchEdge(src, target, isExceptionEdge); + } return addUnlabeledEdge(src, target, isExceptionEdge); } @@ -138,69 +135,6 @@ private ControlFlowEdge addBranchEdge(BytecodeInstruction src, return internalAddEdge(src, target, e); } - private ControlFlowEdge addSwitchBranchEdge(BytecodeInstruction src, - BytecodeInstruction target, boolean isExceptionEdge) { - if (!target.isLabel()) - throw new IllegalStateException( - "expect control flow edges from switch statements to always target labelNodes"); - - LabelNode label = (LabelNode) target.getASMNode(); - - List switchCaseBranches = BranchPool.getInstance(classLoader).getBranchForLabel(label); - - if (switchCaseBranches == null) { - SimpleLogger.debug("not a switch case label: " + label.toString() + " " - + target); - return internalAddEdge(src, target, new ControlFlowEdge(isExceptionEdge)); - } - // throw new IllegalStateException( - // "expect BranchPool to contain a Branch for each switch-case-label"+src.toString()+" to "+target.toString()); - - // TODO there is an inconsistency when it comes to switches with - // empty case: blocks. they do not have their own label, so there - // can be multiple ControlFlowEdges from the SWITCH instruction to - // one LabelNode. - // But currently our RawCFG does not permit multiple edges between - // two nodes - - for (Branch switchCaseBranch : switchCaseBranches) { - - // TODO n^2 - Set soFar = incomingEdgesOf(target); - boolean handled = false; - for (ControlFlowEdge old : soFar) - if (switchCaseBranch.equals(old.getBranchInstruction())) - handled = true; - - if (handled) - continue; - /* - * previous try to add fake intermediate nodes for each empty case - * block to help the CDG - unsuccessful: - * if(switchCaseBranches.size()>1) { // // e = new - * ControlFlowEdge(isExceptionEdge); // - * e.setBranchInstruction(switchCaseBranch); // - * e.setBranchExpressionValue(true); // BytecodeInstruction - * fakeInstruction = - * BytecodeInstructionPool.createFakeInstruction(className - * ,methodName); // addVertex(fakeInstruction); // - * internalAddEdge(src,fakeInstruction,e); // // e = new - * ControlFlowEdge(isExceptionEdge); // - * e.setBranchInstruction(switchCaseBranch); // - * e.setBranchExpressionValue(true); // // e = - * internalAddEdge(fakeInstruction,target,e); // } else { - */ - - ControlDependency cd = new ControlDependency(switchCaseBranch, true); - ControlFlowEdge e = new ControlFlowEdge(cd, isExceptionEdge); - - e = internalAddEdge(src, target, e); - - } - - return new ControlFlowEdge(isExceptionEdge); - } - private ControlFlowEdge internalAddEdge(BytecodeInstruction src, BytecodeInstruction target, ControlFlowEdge e) { @@ -394,67 +328,6 @@ public int removeIsolatedNodes() { return removed; } - // control distance functionality - - /** - * Returns the Set of BytecodeInstructions that can potentially be executed - * from entering the method of this CFG until the given BytecodeInstruction - * is reached. - * - * @param v a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. - * @return a {@link java.util.Set} object. - */ - public Set getPreviousInstructionsInMethod(BytecodeInstruction v) { - Set visited = new HashSet<>(); - PriorityQueue queue = new PriorityQueue<>( - graph.vertexSet().size(), new BytecodeInstructionIdComparator()); - queue.add(v); - while (queue.peek() != null) { - BytecodeInstruction current = queue.poll(); - if (visited.contains(current)) - continue; - Set incomingEdges = graph.incomingEdgesOf(current); - for (ControlFlowEdge incomingEdge : incomingEdges) { - BytecodeInstruction source = graph.getEdgeSource(incomingEdge); - if (source.getInstructionId() >= current.getInstructionId()) - continue; - queue.add(source); - } - visited.add(current); - } - return visited; - } - - /** - * Returns the Set of BytecodeInstructions that can potentially be executed - * from passing the given BytecodeInstruction until the end of the method of - * this CFG is reached. - * - * @param v a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. - * @return a {@link java.util.Set} object. - */ - public Set getLaterInstructionsInMethod(BytecodeInstruction v) { - Set visited = new HashSet<>(); - Comparator reverseComp = new BytecodeInstructionIdComparator().reversed(); - PriorityQueue queue = new PriorityQueue<>( - graph.vertexSet().size(), reverseComp); - queue.add(v); - while (queue.peek() != null) { - BytecodeInstruction current = queue.poll(); - if (visited.contains(current)) - continue; - Set outgoingEdges = graph.outgoingEdgesOf(current); - for (ControlFlowEdge outgoingEdge : outgoingEdges) { - BytecodeInstruction target = graph.getEdgeTarget(outgoingEdge); - if (target.getInstructionId() < current.getInstructionId()) - continue; - queue.add(target); - } - visited.add(current); - } - return visited; - } - // miscellaneous /** 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 index 0610ae56da..ca6bc5a389 100755 --- 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 @@ -68,6 +68,13 @@ public class Branch implements Serializable, Comparable { */ private boolean isInstrumented = false; + /** + * Canonical identifiers matching the descriptive ids used in {@code ObjectiveNaming}. + */ + private String thenObjectiveId; + private String elseObjectiveId; + private Integer branchOrdinalInLine; + /** * Constructor for usual jump instruction Branches, that are not SWITCH * instructions. @@ -298,6 +305,31 @@ public void setInstrumented(boolean isInstrumented) { this.isInstrumented = isInstrumented; } + /** + * Store the descriptive identifiers associated with this branch. + * + * @param ordinalInLine index of this branch on its source line (0-based) + * @param thenId descriptive id for the "true" outcome + * @param elseId descriptive id for the "false" outcome + */ + public void setObjectiveIds(int ordinalInLine, String thenId, String elseId) { + this.branchOrdinalInLine = ordinalInLine; + this.thenObjectiveId = thenId; + this.elseObjectiveId = elseId; + } + + public Integer getBranchOrdinalInLine() { + return branchOrdinalInLine; + } + + public String getThenObjectiveId() { + return thenObjectiveId; + } + + public String getElseObjectiveId() { + return elseObjectiveId; + } + private boolean ignoreFalse = false; public boolean ignoreFalseBranch() { 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 index 8b5af7a40a..d26694b1b3 100755 --- 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 @@ -20,10 +20,7 @@ package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch; import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.LabelNode; -import org.objectweb.asm.tree.LookupSwitchInsnNode; -import org.objectweb.asm.tree.TableSwitchInsnNode; +import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming; import org.evomaster.client.java.utils.SimpleLogger; import java.util.*; @@ -64,19 +61,13 @@ public class BranchPool { // maps all known branch instructions to their branchId private final Map registeredNormalBranches = new HashMap<>(); - // maps all known switch instructions to a list containing all of their - // associated Branch objects - private final Map> registeredSwitches = new HashMap<>(); - - private final Map registeredDefaultCases = new HashMap<>(); - - private final Map> switchLabels = 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()); @@ -86,20 +77,7 @@ public static BranchPool getInstance(ClassLoader classLoader) { } // fill the pool - /** - * Gets called by the CFGMethodAdapter whenever it detects a method without - * any branches. - * - * @param methodName Unique methodName - consisting of . - - * of a method without Branches - * @param className a {@link java.lang.String} object. - */ - public void addBranchlessMethod(String className, String methodName, - int lineNumber) { - if (!branchlessMethods.containsKey(className)) - branchlessMethods.put(className, new HashMap<>()); - branchlessMethods.get(className).put(methodName, lineNumber); - } + /** * Called by the BytecodeInstructionPool whenever it detects an instruction @@ -123,13 +101,10 @@ private void registerInstruction(BytecodeInstruction v) { throw new IllegalStateException( "expect registerInstruction() to be called at most once for each instruction"); - if (v.isBranch()) - registerNormalBranchInstruction(v); - else if (v.isSwitch()) - registerSwitchInstruction(v); - else - throw new IllegalArgumentException( - "expect given instruction to be an actual branch"); + if (!v.isBranch()) + throw new IllegalArgumentException("expect given instruction to be a normal branch"); + + registerNormalBranchInstruction(v); } private void registerNormalBranchInstruction(BytecodeInstruction v) { @@ -144,144 +119,48 @@ private void registerNormalBranchInstruction(BytecodeInstruction v) { registeredNormalBranches.put(v, branchCounter); Branch b = new Branch(v, branchCounter); + attachObjectiveIds(b); addBranchToMap(b); branchIdMap.put(branchCounter, b); SimpleLogger.info("Branch " + branchCounter + " at line " + v.getLineNumber()); } - private void registerSwitchInstruction(BytecodeInstruction v) { - if (!v.isSwitch()) - throw new IllegalArgumentException("expect a switch instruction"); - - LabelNode defaultLabel; - - switch (v.getASMNode().getOpcode()) { - case Opcodes.TABLESWITCH: - TableSwitchInsnNode tableSwitchNode = (TableSwitchInsnNode) v.getASMNode(); - registerTableSwitchCases(v, tableSwitchNode); - defaultLabel = tableSwitchNode.dflt; - - break; - case Opcodes.LOOKUPSWITCH: - LookupSwitchInsnNode lookupSwitchNode = (LookupSwitchInsnNode) v.getASMNode(); - registerLookupSwitchCases(v, lookupSwitchNode); - defaultLabel = lookupSwitchNode.dflt; - break; - default: - throw new IllegalStateException( - "expect ASMNode of a switch to either be a LOOKUP- or TABLESWITCH"); - } - - registerDefaultCase(v, defaultLabel); - } - - private void registerDefaultCase(BytecodeInstruction v, LabelNode defaultLabel) { - - if (defaultLabel == null) - throw new IllegalStateException("expect variable to bet set"); - - Branch defaultBranch = createSwitchCaseBranch(v, null, defaultLabel); - if (!defaultBranch.isSwitchCaseBranch() || !defaultBranch.isDefaultCase()) - throw new IllegalStateException( - "expect created branch to be a default case branch of a switch"); - } - - private void registerTableSwitchCases(BytecodeInstruction v, - TableSwitchInsnNode tableSwitchNode) { - - int num = 0; - - for (int i = tableSwitchNode.min; i <= tableSwitchNode.max; i++) { - LabelNode targetLabel = tableSwitchNode.labels.get(num); - Branch switchBranch = createSwitchCaseBranch(v, i, targetLabel); - if (!switchBranch.isSwitchCaseBranch() || !switchBranch.isActualCase()) - throw new IllegalStateException( - "expect created branch to be an actual case branch of a switch"); - num++; + private void attachObjectiveIds(Branch branch) { + if (branch == null) { + return; } - } - - private void registerLookupSwitchCases(BytecodeInstruction v, - LookupSwitchInsnNode lookupSwitchNode) { - - for (int i = 0; i < lookupSwitchNode.keys.size(); i++) { - LabelNode targetLabel = lookupSwitchNode.labels.get(i); - Branch switchBranch = createSwitchCaseBranch(v, - lookupSwitchNode.keys.get(i), - targetLabel); - if (!switchBranch.isSwitchCaseBranch() || !switchBranch.isActualCase()) - throw new IllegalStateException( - "expect created branch to be an actual case branch of a switch"); + BytecodeInstruction instruction = branch.getInstruction(); + if (instruction == null || !instruction.isBranch()) { + return; } - } - - private Branch createSwitchCaseBranch(BytecodeInstruction v, - Integer caseValue, LabelNode targetLabel) { - - branchCounter++; - - Branch switchBranch = new Branch(v, caseValue, targetLabel, branchCounter); - registerSwitchBranch(v, switchBranch); - addBranchToMap(switchBranch); - branchIdMap.put(branchCounter, switchBranch); - - registerSwitchLabel(switchBranch, targetLabel); - - // default case - if (caseValue == null) { - if (registeredDefaultCases.containsKey(v)) - throw new IllegalStateException( - "instruction already registered as a branch"); - registeredDefaultCases.put(v, switchBranch); + 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(ordinal, thenId, elseId); + } catch (Exception e) { + SimpleLogger.warn("Failed to attach objective ids to branch " + branch + ": " + e.getMessage()); } - - if (!switchBranch.isSwitchCaseBranch()) - throw new IllegalStateException("expect created Branch to be a switch branch"); - - return switchBranch; - } - - private void registerSwitchLabel(Branch b, LabelNode targetLabel) { - - if (switchLabels.get(targetLabel) == null) - switchLabels.put(targetLabel, new ArrayList<>()); - - List oldList = switchLabels.get(targetLabel); - - if (oldList.contains(b)) - throw new IllegalStateException( - "branch already registered for this switch label"); - - oldList.add(b); - - // TODO several Branches can map to one Label, so switchLabels should - // either map from branches to labels, not the other way around. or it - // should map labels to a list of branches - // this stems from the fact that empty case: blocks do not have their - // own label - - // TODO STOPPED HERE - - switchLabels.put(targetLabel, oldList); } - private void registerSwitchBranch(BytecodeInstruction v, Branch switchBranch) { - if (!v.isSwitch()) - throw new IllegalArgumentException("switch instruction expected"); - - if (registeredSwitches.get(v) == null) - registeredSwitches.put(v, new ArrayList<>()); - - List oldList = registeredSwitches.get(v); - - if (oldList.contains(switchBranch)) - throw new IllegalArgumentException("switch branch already registered " - + switchBranch.toString()); - - oldList.add(switchBranch); - - registeredSwitches.put(v, oldList); + 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; } private void addBranchToMap(Branch b) { @@ -311,8 +190,7 @@ private void addBranchToMap(Branch b) { * @return a boolean. */ public boolean isKnownAsBranch(BytecodeInstruction instruction) { - return isKnownAsNormalBranchInstruction(instruction) - || isKnownAsSwitchBranchInstruction(instruction); + return isKnownAsNormalBranchInstruction(instruction); } /** @@ -328,19 +206,6 @@ public boolean isKnownAsNormalBranchInstruction(BytecodeInstruction ins) { return registeredNormalBranches.containsKey(ins); } - /** - *

- * isKnownAsSwitchBranchInstruction - *

- * - * @param instruction a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. - * @return a boolean. - */ - public boolean isKnownAsSwitchBranchInstruction(BytecodeInstruction instruction) { - - return registeredSwitches.containsKey(instruction); - } - /** *

* getActualBranchIdForNormalBranchInstruction @@ -361,25 +226,6 @@ public int getActualBranchIdForNormalBranchInstruction(BytecodeInstruction ins) "expect registeredNormalBranches to contain a key for each known normal branch instruction"); } - /** - *

- * getCaseBranchesForSwitch - *

- * - * @param instruction a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. - * @return a {@link java.util.List} object. - */ - public List getCaseBranchesForSwitch(BytecodeInstruction instruction) { - if (instruction == null) - throw new IllegalArgumentException("null given"); - if (!instruction.isSwitch()) - throw new IllegalArgumentException("switch instruction expected"); - if (!isKnownAsSwitchBranchInstruction(instruction)) - throw new IllegalArgumentException("not registered as a switch instruction"); - - return registeredSwitches.get(instruction); - } - /** *

* getBranchForInstruction @@ -398,21 +244,6 @@ public Branch getBranchForInstruction(BytecodeInstruction instruction) { return getBranch(registeredNormalBranches.get(instruction)); } - /** - *

- * getBranchForLabel - *

- * - * @param label a {@link org.objectweb.asm.tree.LabelNode} object. - * @return a {@link java.util.List} object. - */ - public List getBranchForLabel(LabelNode label) { - - // TODO see registerSwitchLabel()! - - return switchLabels.get(label); - } - /** * Returns the number of known Branches for a given methodName in a given * class. @@ -564,134 +395,6 @@ public Collection getAllBranches() { return branchIdMap.values(); } - /** - * Returns a set with all unique methodNames of methods without Branches. - * - * @param className a {@link java.lang.String} object. - * @return A set with all unique methodNames of methods without Branches. - */ - public Set getBranchlessMethods(String className) { - if (!branchlessMethods.containsKey(className)) - return new LinkedHashSet<>(); - - return branchlessMethods.get(className).keySet(); - } - - /** - * Returns a set with all unique methodNames of methods without Branches. - * - * @param className a {@link java.lang.String} object. - * @return A set with all unique methodNames of methods without Branches. - */ - public Set getBranchlessMethodsPrefix(String className) { - Set methods = new LinkedHashSet<>(); - - for (String name : branchlessMethods.keySet()) { - if (name.equals(className) || name.startsWith(className + "$")) { - methods.addAll(branchlessMethods.get(name).keySet()); - } - } - - return methods; - } - - /** - * Returns a set with all unique methodNames of methods without Branches. - * - * @param className a {@link java.lang.String} object. - * @return A set with all unique methodNames of methods without Branches. - */ - public Set getBranchlessMethodsMemberClasses(String className) { - Set methods = new LinkedHashSet<>(); - - for (String name : branchlessMethods.keySet()) { - if (name.equals(className) || name.startsWith(className + "$")) { - methods.addAll(branchlessMethods.get(name).keySet()); - } - } - - return methods; - } - - /** - * Returns a set with all unique methodNames of methods without Branches. - * - * @return A set with all unique methodNames of methods without Branches. - */ - public Set getBranchlessMethods() { - Set methods = new LinkedHashSet<>(); - - for (String name : branchlessMethods.keySet()) { - methods.addAll(branchlessMethods.get(name).keySet()); - } - - return methods; - } - - public boolean isBranchlessMethod(String className, String methodName) { - Map methodMap = branchlessMethods.get(className); - if (methodMap != null) { - return methodMap.containsKey(methodName); - } - return false; - } - - /** - * Returns the number of methods without Branches for class className - * - * @param className a {@link java.lang.String} object. - * @return The number of methods without Branches. - */ - public int getNumBranchlessMethods(String className) { - if (!branchlessMethods.containsKey(className)) - return 0; - return branchlessMethods.get(className).size(); - } - - /** - * Returns the number of methods without Branches for class className - * - * @param className a {@link java.lang.String} object. - * @return The number of methods without Branches. - */ - public int getNumBranchlessMethodsPrefix(String className) { - int num = 0; - for (String name : branchlessMethods.keySet()) { - if (name.startsWith(className)) - num += branchlessMethods.get(name).size(); - } - return num; - } - - /** - * Returns the number of methods without Branches for class className - * - * @param className a {@link java.lang.String} object. - * @return The number of methods without Branches. - */ - public int getNumBranchlessMethodsMemberClasses(String className) { - int num = 0; - for (String name : branchlessMethods.keySet()) { - if (name.equals(className) || name.startsWith(className + "$")) - num += branchlessMethods.get(name).size(); - } - return num; - } - - /** - * Returns the total number of methods without branches in the instrumented - * classes - * - * @return - */ - public int getNumBranchlessMethods() { - int num = 0; - for (String name : branchlessMethods.keySet()) { - num += branchlessMethods.get(name).size(); - } - return num; - } - /** * Returns a Set containing all classes for which this pool knows Branches * for as Strings @@ -742,27 +445,6 @@ public List retrieveBranchesInMethod(String className, return r; } - /** - *

- * getDefaultBranchForSwitch - *

- * - * @param v a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. - * @return a {@link org.evosuite.coverage.branch.Branch} object. - */ - public Branch getDefaultBranchForSwitch(BytecodeInstruction v) { - if (!v.isSwitch()) - throw new IllegalArgumentException("switch instruction expected"); - if (!isKnownAsSwitchBranchInstruction(v)) - throw new IllegalArgumentException( - "instruction not known to be a switch instruction"); - if (!registeredDefaultCases.containsKey(v)) - throw new IllegalArgumentException( - "there is no registered default case for this instruction"); - - return registeredDefaultCases.get(v); - } - /** * Reset all the data structures used to keep track of the branch * information @@ -773,9 +455,7 @@ public void reset() { branchlessMethods.clear(); branchIdMap.clear(); registeredNormalBranches.clear(); - registeredSwitches.clear(); - registeredDefaultCases.clear(); - switchLabels.clear(); + branchOrdinalCounters.clear(); } /** @@ -790,10 +470,8 @@ public void clear() { branchMap.clear(); branchIdMap.clear(); branchlessMethods.clear(); - switchLabels.clear(); - registeredDefaultCases.clear(); registeredNormalBranches.clear(); - registeredSwitches.clear(); + branchOrdinalCounters.clear(); } /** @@ -806,6 +484,7 @@ public void clear() { public void clear(String className) { branchMap.remove(className); branchlessMethods.remove(className); + removeOrdinalCountersForClass(className); } /** @@ -826,9 +505,26 @@ public void clear(String className, String methodName) { } if (branchlessMethods.containsKey(className)) branchlessMethods.get(className).remove(methodName); + removeOrdinalCountersForMethod(className, methodName); SimpleLogger.info("Resetting branchCounter from " + branchCounter + " to " + (branchCounter - numBranches)); branchCounter -= numBranches; } + private void removeOrdinalCountersForClass(String className) { + if (className == null) { + return; + } + String prefix = className + "#"; + branchOrdinalCounters.keySet().removeIf(key -> key.startsWith(prefix)); + } + + private void removeOrdinalCountersForMethod(String className, String methodName) { + if (className == null || methodName == null) { + return; + } + String prefix = className + "#" + methodName + "#"; + branchOrdinalCounters.keySet().removeIf(key -> key.startsWith(prefix)); + } + } 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 62bc8e004a..053b34fa02 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 @@ -133,7 +133,7 @@ private static void handleDynamosaConfig() { if (dto.enableGraphs != null) { DynamosaConfig.setEnableGraphs(dto.enableGraphs); } - if (dto.writeCfg != null && dto.enableGraphs != null) { + if (dto.writeCfg != null) { DynamosaConfig.setWriteCfgEnabled(dto.writeCfg); } } catch (Exception e) { diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 0a48850854..87f880fdbb 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -2354,7 +2354,7 @@ class EMConfig { * This controls only the persistence of graphs to disk; graph creation is controlled separately. */ @Cfg("Enable writing CFG/CDG graphs to disk on the agent side") - var writeCfg: Boolean = false + var writeCfg: Boolean = true @Cfg("Specify whether to employ smart database clean to clear data in the database if the SUT has." + "`null` represents to employ the setting specified on the EM driver side") diff --git a/core/src/main/kotlin/org/evomaster/core/Main.kt b/core/src/main/kotlin/org/evomaster/core/Main.kt index 4f1f698c80..e5d32f5a70 100644 --- a/core/src/main/kotlin/org/evomaster/core/Main.kt +++ b/core/src/main/kotlin/org/evomaster/core/Main.kt @@ -639,6 +639,9 @@ class Main { EMConfig.Algorithm.MOSA -> Key.get(object : TypeLiteral>() {}) + + EMConfig.Algorithm.DYNAMOSA -> + Key.get(object : TypeLiteral>() {}) EMConfig.Algorithm.RW -> Key.get(object : TypeLiteral>() {}) @@ -684,6 +687,9 @@ class Main { EMConfig.Algorithm.MOSA -> Key.get(object : TypeLiteral>() {}) + EMConfig.Algorithm.DYNAMOSA -> + Key.get(object : TypeLiteral>() {}) + EMConfig.Algorithm.RW -> Key.get(object : TypeLiteral>() {}) EMConfig.Algorithm.LIPS -> @@ -724,6 +730,9 @@ class Main { EMConfig.Algorithm.MOSA -> Key.get(object : TypeLiteral>() {}) + EMConfig.Algorithm.DYNAMOSA -> + Key.get(object : TypeLiteral>() {}) + EMConfig.Algorithm.RW -> Key.get(object : TypeLiteral>() {}) EMConfig.Algorithm.LIPS -> @@ -764,6 +773,9 @@ class Main { EMConfig.Algorithm.MOSA -> Key.get(object : TypeLiteral>() {}) + EMConfig.Algorithm.DYNAMOSA -> + Key.get(object : TypeLiteral>() {}) + EMConfig.Algorithm.StandardGA -> Key.get(object : TypeLiteral>() {}) diff --git a/core/src/main/kotlin/org/evomaster/core/search/algorithms/DynamosaAlgorithm.kt b/core/src/main/kotlin/org/evomaster/core/search/algorithms/DynamosaAlgorithm.kt new file mode 100644 index 0000000000..530c694ca6 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/search/algorithms/DynamosaAlgorithm.kt @@ -0,0 +1,308 @@ +package org.evomaster.core.search.algorithms + +import org.evomaster.core.EMConfig +import org.evomaster.core.search.EvaluatedIndividual +import org.evomaster.core.search.Individual +import org.evomaster.core.search.service.SearchAlgorithm +import org.evomaster.core.logging.LoggingUtil +import java.util.ArrayList + + + +/** + * Implementation of DYMOSA + */ +class DynaMosaAlgorithm : SearchAlgorithm() where T : Individual { + + private class Data(val ind: EvaluatedIndividual<*>) { + + var rank = -1 + var crowdingDistance = -1 + } + + private var population: MutableList = mutableListOf() + + override fun getType(): EMConfig.Algorithm { + return EMConfig.Algorithm.DYNAMOSA + } + + override fun setupBeforeSearch() { + + population.clear() + + initPopulation() + sortPopulation() + + } + + override fun searchOnce() { + + + val n = config.populationSize + + + //new generation + + val nextPop: MutableList = mutableListOf() + + while (nextPop.size < n-1) { + + var ind = selection() + + getMutatator().mutateAndSave(ind, archive) + ?.let{nextPop.add(Data(it))} + + if (!time.shouldContinueSearch()) { + break + } + } + // generate one random solution + var ie = sampleIndividual() + nextPop.add(Data(ie as EvaluatedIndividual)) + + population.addAll(nextPop) + sortPopulation() + } + + + private fun sortPopulation() { + + val notCovered = archive.notCoveredTargets() + + if(notCovered.isEmpty()){ + //Trivial problem: everything covered in first population + return + } + + val fronts = preferenceSorting(notCovered, population) + + var remain: Int = config.populationSize + var index = 0 + population.clear() + + // Obtain the next front + var front = fronts[index] + + while (front!=null && remain > 0 && remain >= front.size && front.isNotEmpty()) { + // Assign crowding distance to individuals + subvectorDominance(notCovered, front) + // Add the individuals of this front + for (d in front) { + population.add(d) + } + + // Decrement remain + remain = remain - front.size + + // Obtain the next front + index += 1 + if (remain > 0) { + front = fronts[index] + } // if + } // while + + // Remain is less than front(index).size, insert only the best one + if (remain > 0 && front!=null && front.isNotEmpty()) { + subvectorDominance(notCovered, front) + var front2 = front.sortedWith(compareBy { - it.crowdingDistance }) + .toMutableList() + for (k in 0..remain - 1) { + population.add(front2[k]) + } // for + + } // if + + } + + private fun subvectorDominance(notCovered: Set, list: List){ + /* + see: + Substitute Distance Assignments in NSGA-II for + Handling Many-Objective Optimization Problems + */ + + list.forEach { i -> + i.crowdingDistance = 0 + list.filter { j -> j!=i }.forEach { j -> + val v = svd(notCovered, i, j) + if(v > i.crowdingDistance){ + i.crowdingDistance = v + } + } + } + } + + + private fun svd(notCovered: Set, i: Data, j: Data) : Int{ + var cnt = 0 + for(t in notCovered){ + if(i.ind.fitness.getHeuristic(t) > j.ind.fitness.getHeuristic(t)){ + cnt++ + } + } + return cnt + } + + + /* + See: Preference sorting as discussed in the TSE paper for DynaMOSA + */ + private fun preferenceSorting(notCovered: Set, list: List): HashMap> { + + val fronts = HashMap>() + + // compute the first front using the Preference Criteria + val frontZero = mosaPreferenceCriterion(notCovered, list) + fronts.put(0, ArrayList(frontZero)) + LoggingUtil.getInfoLogger().apply { + debug("First front size : ${frontZero.size}") + } + + // compute the remaining non-dominated Fronts + val remaining_solutions: MutableList = mutableListOf() + remaining_solutions.addAll(list) + remaining_solutions.removeAll(frontZero) + + var selected_solutions = frontZero.size + var front_index = 1 + + while (selected_solutions < config.populationSize && remaining_solutions.isNotEmpty()){ + var front: MutableList = getNonDominatedFront(notCovered, remaining_solutions) + fronts.put(front_index, front) + for (sol in front){ + sol.rank = front_index + } + remaining_solutions.removeAll(front) + + selected_solutions += front.size + + front_index += 1 + + LoggingUtil.getInfoLogger().apply { + debug("Selected Solutions : ${selected_solutions}") + } + } + return fronts + } + + /** + * It retrieves the front of non-dominated solutions from a list + */ + private fun getNonDominatedFront(notCovered: Set, remaining_sols: List): MutableList{ + var front: MutableList = mutableListOf() + var isDominated: Boolean + + for (p in remaining_sols) { + isDominated = false + val dominatedSolutions = ArrayList(remaining_sols.size) + for (best in front) { + val flag = compare(p, best, notCovered) + if (flag == -1) { + dominatedSolutions.add(best) + } + if (flag == +1) { + isDominated = true + } + } + + if (isDominated) + continue + + front.removeAll(dominatedSolutions) + front.add(p) + + } + return front + } + + /** + * Fast routine based on the Dominance Comparator discussed in + * "Automated Test Case Generation as a Many-Objective Optimisation Problem with Dynamic + * Selection of the Targets" + */ + private fun compare(x: Data, y: Data, notCovered: Set): Int { + var dominatesX = false + var dominatesY = false + + for (index in 1..notCovered.size) { + if (x.ind.fitness.getHeuristic(index) > y.ind.fitness.getHeuristic(index)) + dominatesX = true + if (y.ind.fitness.getHeuristic(index) > x.ind.fitness.getHeuristic(index)) + dominatesY = true + + // if the both do not dominates each other, we don't + // need to iterate over all the other targets + if (dominatesX && dominatesY) + return 0 + } + + if (dominatesX == dominatesY) + return 0 + + else if (dominatesX) + return -1 + + else (dominatesY) + return +1 + } + + private fun mosaPreferenceCriterion(notCovered: Set, list: List): HashSet { + var frontZero: HashSet = HashSet() + + notCovered.forEach { t -> + var chosen = list[0] + list.forEach { data -> + if (data.ind.fitness.getHeuristic(t) > chosen.ind.fitness.getHeuristic(t)) { + // recall: maximization problem + chosen = data + } else if (data.ind.fitness.getHeuristic(t) == chosen.ind.fitness.getHeuristic(t) + && data.ind.individual.size() < chosen.ind.individual.size()){ + // Secondary criterion based on tests lengths + chosen = data + } + } + // MOSA preference criterion: the best for a target gets Rank 0 + chosen.rank = 0 + frontZero.add(chosen) + } + return frontZero + } + + private fun selection(): EvaluatedIndividual { + + // the population is not fully sorted + var min = randomness.nextInt(population.size) + + (0 until config.tournamentSize-1).forEach { + val sel = randomness.nextInt(population.size) + if (population[sel].rank < population[min].rank) { + min = sel + } else if (population[sel].rank == population[min].rank){ + if (population[sel].crowdingDistance < population[min].crowdingDistance) + min = sel + } + } + + return (population[min].ind as EvaluatedIndividual).copy() + } + + + private fun initPopulation() { + + val n = config.populationSize + + for (i in 1..n) { + sampleIndividual()?.run { population.add(Data(this)) } + + if (!time.shouldContinueSearch()) { + break + } + } + } + + private fun sampleIndividual(): EvaluatedIndividual? { + + return ff.calculateCoverage(sampler.sample(), modifiedSpec = null) + ?.also { archive.addIfNeeded(it) } + } +} \ No newline at end of file From 18b4f1eecb7507191c7e50cd097910a824fb7339 Mon Sep 17 00:00:00 2001 From: francastagna Date: Tue, 2 Dec 2025 15:40:52 -0300 Subject: [PATCH 03/21] added algoritm logic --- client-java/controller-api/pom.xml | 4 + .../controller/api/ControllerConstants.java | 1 + .../java/controller/api/dto/SutInfoDto.java | 1 - .../controller/api/dto/TestResultsDto.java | 7 + .../controller/EmbeddedSutController.java | 21 + .../controller/ExternalSutController.java | 10 + .../controller/internal/EMController.java | 8 +- .../controller/internal/SutController.java | 5 + .../InstrumentationController.java | 17 +- .../java/instrumentation/Instrumentator.java | 15 +- .../dynamosa/DynamosaConfig.java | 4 + ...EvoSuiteGraph.java => EvoMasterGraph.java} | 71 +- .../dynamosa/graphs/GraphPool.java | 208 +++- .../graphs/cdg/ControlDependenceGraph.java | 393 +------- .../dynamosa/graphs/cdg/DominatorNode.java | 20 +- .../dynamosa/graphs/cdg/DominatorTree.java | 24 +- .../dynamosa/graphs/cfg/ASMWrapper.java | 905 ------------------ .../graphs/cfg/ActualControlFlowGraph.java | 328 +------ .../dynamosa/graphs/cfg/BasicBlock.java | 132 +-- .../dynamosa/graphs/cfg/BytecodeAnalyzer.java | 136 --- .../graphs/cfg/BytecodeInstruction.java | 703 +++----------- .../graphs/cfg/BytecodeInstructionPool.java | 216 +---- .../dynamosa/graphs/cfg/CFGFrame.java | 49 - .../dynamosa/graphs/cfg/CFGGenerator.java | 38 +- .../graphs/cfg/ControlDependency.java | 77 +- .../dynamosa/graphs/cfg/ControlFlowEdge.java | 51 +- .../dynamosa/graphs/cfg/ControlFlowGraph.java | 117 +-- .../dynamosa/graphs/cfg/EntryBlock.java | 21 +- .../dynamosa/graphs/cfg/ExitBlock.java | 21 +- .../graphs/cfg/RawControlFlowGraph.java | 63 +- .../dynamosa/graphs/cfg/branch/Branch.java | 203 +--- .../graphs/cfg/branch/BranchPool.java | 404 +------- .../CFGClassVisitor.java} | 14 +- .../CFGMethodVisitor.java} | 57 +- .../external/AgentController.java | 23 + .../instrumentation/external/Command.java | 3 +- .../external/DynamosaConfigDto.java | 4 +- .../DynamosaControlDependenceSnapshot.java | 36 + .../external/ServerController.java | 33 + .../core/remote/service/RemoteController.kt | 3 + .../service/RemoteControllerImplementation.kt | 23 +- .../algorithms/BranchDependencyGraph.kt | 108 +++ .../search/algorithms/DynamosaAlgorithm.kt | 59 +- .../core/search/algorithms/MosaAlgorithm.kt | 3 +- .../search/algorithms/MulticriteriaManager.kt | 122 +++ .../algorithms/BranchDependencyGraphTest.kt | 111 +++ .../algorithms/DynaMosaAlgorithmTest.kt | 99 ++ .../algorithms/MulticriteriaManagerTest.kt | 130 +++ 48 files changed, 1304 insertions(+), 3797 deletions(-) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/{EvoSuiteGraph.java => EvoMasterGraph.java} (88%) delete mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ASMWrapper.java delete mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeAnalyzer.java delete mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGFrame.java rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/{graphs/cfg/CFGClassAdapter.java => visitor/CFGClassVisitor.java} (71%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/{graphs/cfg/CFGMethodAdapter.java => visitor/CFGMethodVisitor.java} (69%) create mode 100644 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/DynamosaControlDependenceSnapshot.java create mode 100644 core/src/main/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraph.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManager.kt create mode 100644 core/src/test/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraphTest.kt create mode 100644 core/src/test/kotlin/org/evomaster/core/search/algorithms/DynaMosaAlgorithmTest.kt create mode 100644 core/src/test/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManagerTest.kt diff --git a/client-java/controller-api/pom.xml b/client-java/controller-api/pom.xml index c7fab551a0..3120ab2676 100644 --- a/client-java/controller-api/pom.xml +++ b/client-java/controller-api/pom.xml @@ -22,6 +22,10 @@ org.evomaster evomaster-client-java-sql-dto + + org.evomaster + evomaster-client-java-instrumentation-shared + org.evomaster evomaster-test-utils-java diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/ControllerConstants.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/ControllerConstants.java index 95a1ee6017..c58e8cf8ff 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/ControllerConstants.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/ControllerConstants.java @@ -33,4 +33,5 @@ public class ControllerConstants { public static final String POST_SEARCH_ACTION = "/postSearchAction"; public static final String DERIVE_PARAMS = "/deriveParams"; + } diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutInfoDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutInfoDto.java index ea50070eea..774bf6edc1 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutInfoDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutInfoDto.java @@ -6,7 +6,6 @@ import org.evomaster.client.java.controller.api.dto.problem.RestProblemDto; import org.evomaster.client.java.controller.api.dto.problem.GraphQLProblemDto; import org.evomaster.client.java.controller.api.dto.problem.WebProblemDto; - import java.util.List; public class SutInfoDto { diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/TestResultsDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/TestResultsDto.java index 5f34446961..8f8a6a1efb 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/TestResultsDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/TestResultsDto.java @@ -1,5 +1,7 @@ package org.evomaster.client.java.controller.api.dto; +import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto; + import java.util.ArrayList; import java.util.List; @@ -14,4 +16,9 @@ public class TestResultsDto { public List additionalInfoList = new ArrayList<>(); public List extraHeuristics = new ArrayList<>(); + + /** + * Incremental DynaMOSA control-dependence graphs discovered since the last handshake. + */ + public List dynamosaCdgs = new ArrayList<>(); } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java index 9b8128dc49..22d3606268 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java @@ -6,6 +6,8 @@ import org.evomaster.client.java.controller.api.dto.problem.rpc.ScheduleTaskInvocationDto; import org.evomaster.client.java.controller.internal.SutController; import org.evomaster.client.java.instrumentation.*; +import org.evomaster.client.java.instrumentation.external.DynamosaControlDependenceSnapshot; +import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto; import org.evomaster.client.java.instrumentation.object.ClassToSchema; import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; import org.evomaster.client.java.instrumentation.staticstate.ObjectiveRecorder; @@ -31,6 +33,8 @@ */ public abstract class EmbeddedSutController extends SutController { + private int dynamosaCdgIndex = 0; + @Override public final void setupForGeneratedTest(){ //In the past, we configured P6Spy here @@ -138,4 +142,21 @@ public final void getJvmDtoSchema(List dtoNames) { public final void bootingSut(boolean bootingSut) { ObjectiveRecorder.setBooting(bootingSut); } + + @Override + public List getDynamosaControlDependenceGraphs() { + DynamosaControlDependenceSnapshot snapshot = InstrumentationController.getControlDependenceSnapshot(dynamosaCdgIndex); + dynamosaCdgIndex = snapshot.getNextIndex(); + return snapshot.getGraphs(); + } + + @Override + public void setDynamosaGraphsEnabled(boolean enableGraphs) { + InstrumentationController.setDynamosaGraphsEnabled(enableGraphs); + } + + @Override + public void setWriteCfgEnabled(boolean writeCfg) { + InstrumentationController.setWriteCfgEnabled(writeCfg); + } } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java index 7a4a13d76b..e077b78222 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java @@ -13,6 +13,7 @@ import org.evomaster.client.java.instrumentation.external.JarAgentLocator; import org.evomaster.client.java.instrumentation.external.ServerController; import org.evomaster.client.java.instrumentation.external.DynamosaConfigDto; +import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto; import java.io.BufferedReader; import java.io.IOException; @@ -448,6 +449,15 @@ public BootTimeInfoDto getBootTimeInfoDto() { return getBootTimeInfoDto(serverController.handleBootTimeObjectiveInfo()); } + @Override + public List getDynamosaControlDependenceGraphs() { + if (!isInstrumentationActivated()){ + return Collections.emptyList(); + } + List graphs = serverController.getDynamosaControlDependenceGraphs(); + return graphs != null ? graphs : Collections.emptyList(); + } + @Override public final void newActionSpecificHandler(ActionDto dto) { if (isInstrumentationActivated()) { diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java index 39293a790c..6a474f5f75 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java @@ -19,6 +19,7 @@ import org.evomaster.client.java.sql.SqlScriptRunner; import org.evomaster.client.java.controller.problem.rpc.schema.LocalAuthSetupSchema; import org.evomaster.client.java.instrumentation.*; +import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto; import org.evomaster.client.java.instrumentation.shared.StringSpecializationInfo; import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; import org.evomaster.client.java.utils.SimpleLogger; @@ -254,7 +255,6 @@ public Response getSutInfo(@Context HttpServletRequest httpServletRequest) { return Response.status(500).entity(WrappedResponseDto.withError(msg)).build(); } - return Response.status(200).entity(WrappedResponseDto.withData(dto)).build(); } @@ -593,6 +593,12 @@ public Response getTestResults( SimpleLogger.error(msg); return Response.status(500).entity(WrappedResponseDto.withError(msg)).build(); } + + List dynamosaCdgs = + noKillSwitch(() -> sutController.getDynamosaControlDependenceGraphs()); + if (dynamosaCdgs != null && !dynamosaCdgs.isEmpty()) { + dto.dynamosaCdgs.addAll(dynamosaCdgs); + } // } // else { // // there is still some data that we need during minimization diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index 0a610bc74f..bfa81cb1e7 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -49,6 +49,7 @@ import org.evomaster.client.java.instrumentation.AdditionalInfo; import org.evomaster.client.java.instrumentation.BootTimeObjectiveInfo; import org.evomaster.client.java.instrumentation.TargetInfo; +import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto; import org.evomaster.client.java.instrumentation.staticstate.UnitsInfoRecorder; import org.evomaster.client.java.utils.SimpleLogger; import org.glassfish.jersey.jackson.JacksonFeature; @@ -1592,6 +1593,10 @@ public void setWriteCfgEnabled(boolean writeCfg){ public abstract BootTimeInfoDto getBootTimeInfoDto(); + public List getDynamosaControlDependenceGraphs(){ + return Collections.emptyList(); + } + protected BootTimeInfoDto getBootTimeInfoDto(BootTimeObjectiveInfo info){ if (info == null) return null; diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java index 26ba74454e..ccaafe3da5 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java @@ -1,9 +1,11 @@ package org.evomaster.client.java.instrumentation; -import org.evomaster.client.java.instrumentation.object.ClassToSchema; +import org.evomaster.client.java.instrumentation.dynamosa.graphs.GraphPool; import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; import org.evomaster.client.java.instrumentation.staticstate.ObjectiveRecorder; import org.evomaster.client.java.instrumentation.staticstate.UnitsInfoRecorder; +import org.evomaster.client.java.instrumentation.dynamosa.DynamosaConfig; +import org.evomaster.client.java.instrumentation.external.DynamosaControlDependenceSnapshot; import java.util.ArrayList; import java.util.Collection; @@ -16,6 +18,7 @@ public class InstrumentationController { public static void resetForNewSearch(){ ExecutionTracer.reset(); ObjectiveRecorder.reset(false); + GraphPool.refreshAllCdgs(); } /* @@ -146,4 +149,16 @@ public static void extractSpecifiedDto(List 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 6e92256bec..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 @@ -8,7 +8,7 @@ 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.graphs.cfg.CFGClassAdapter; +import org.evomaster.client.java.instrumentation.dynamosa.visitor.CFGClassVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; @@ -67,16 +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); - } - - boolean dynamosaGraphs = DynamosaConfig.isGraphsEnabled(); - if (dynamosaGraphs) { - cv = new CFGClassAdapter(classLoader, cv); - } + } try { cn.accept(cv); 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 index 8c3d74bfa0..91c4f2e71d 100644 --- 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 @@ -1,3 +1,7 @@ +/* + * 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; /** diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoSuiteGraph.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoMasterGraph.java similarity index 88% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoSuiteGraph.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoMasterGraph.java index 15a2f8d826..92be370ade 100755 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoSuiteGraph.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoMasterGraph.java @@ -1,21 +1,6 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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; @@ -34,31 +19,7 @@ import static java.util.stream.Collectors.toCollection; -/** - * Supposed to become the super class of all kinds of graphs used within - * EvoSuite Examples are the raw and minimal Control Flow Graph and hopefully at - * one point the Control Dependency Tree - *

- * This class is supposed to hide the jGraph library from the rest of EvoSuite - * and is supposed to serve as an interface for all kinds of primitive graph- - * functionality such as asking for information about the nodes and edges of the - * graph and the relations between them. - *

- * Hopefully at some point only this class and it's sub classes are the only - * files in EvoSuite that import anything from the jGraph library - at least - * that's the idea This is very similar to the way cfg.ASMWrapper is supposed to - * hide the ASM library and serve as an interface for BytecodeInstrucions - *

- * So most of this class' methods are just wrappers that redirect the specific - * call to the corresponding jGraph-method - *

- * For now an EvoSuiteGraph can always be represented by a DefaultDirectedGraph - * from the jGraph library - that is a directed graph not allowed to contain - * multiple edges between to nodes but allowed to contain cycles - * - * @author Andre Mis - */ -public abstract class EvoSuiteGraph { +public abstract class EvoMasterGraph { private static int evoSuiteGraphs = 0; protected int graphId; @@ -66,17 +27,10 @@ public abstract class EvoSuiteGraph { protected DirectedGraph graph; protected Class edgeClass; - // for .dot functionality - // TODO need jgrapht-0.8.3 ComponentAttributeProvider vertexAttributeProvider = null; ComponentAttributeProvider edgeAttributeProvider = null; - /** - *

Constructor for EvoSuiteGraph.

- * - * @param edgeClass a {@link java.lang.Class} object. - */ - protected EvoSuiteGraph(Class edgeClass) { + protected EvoMasterGraph(Class edgeClass) { graph = new DefaultDirectedGraph<>(edgeClass); this.edgeClass = edgeClass; @@ -84,13 +38,7 @@ protected EvoSuiteGraph(Class edgeClass) { setId(); } - /** - *

Constructor for EvoSuiteGraph.

- * - * @param graph a {@link org.jgrapht.DirectedGraph} object. - * @param edgeClass a {@link java.lang.Class} object. - */ - protected EvoSuiteGraph(DirectedGraph graph, Class edgeClass) { + protected EvoMasterGraph(DirectedGraph graph, Class edgeClass) { if (graph == null || edgeClass == null) throw new IllegalArgumentException("null given"); @@ -268,12 +216,7 @@ public V getSingleChild(V node) { // building the graph - /** - *

addVertices

- * - * @param other a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.EvoSuiteGraph} object. - */ - protected void addVertices(EvoSuiteGraph other) { + protected void addVertices(EvoMasterGraph other) { addVertices(other.vertexSet()); } @@ -810,7 +753,7 @@ private void createToPNGScript(String filename) { * @return a {@link java.lang.String} object. */ public String getName() { - return "EvoSuiteGraph_" + graphId; + return "EvoMasterGraph_" + graphId; } /** 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 index d046220912..64ccdd0ce4 100755 --- 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 @@ -1,38 +1,36 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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 org.evomaster.client.java.instrumentation.shared.ClassName; +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.DynamosaConfig; +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 CFGMethodAdapter + * 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 @@ -40,11 +38,12 @@ *

* This pool stores per-method CFGs and CDGs computed during analysis. * - * @author Andre Mis */ 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; @@ -120,24 +119,6 @@ public RawControlFlowGraph getRawCFG(String className, String methodName) { return rawCFGs.get(className).get(methodName); } - /** - *

- * Getter for the field rawCFGs. - *

- * - * @param className a {@link java.lang.String} object. - * @return a {@link java.util.Map} object. - */ - public Map getRawCFGs(String className) { - if (rawCFGs.get(className) == null) { - SimpleLogger.warn("Class unknown: " + className); - SimpleLogger.warn(rawCFGs.keySet().toString()); - return null; - } - - return rawCFGs.get(className); - } - /** *

* getActualCFG @@ -172,6 +153,14 @@ public ControlDependenceGraph getCDG(String className, String methodName) { 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 /** @@ -223,7 +212,6 @@ public void registerActualCFG(ActualControlFlowGraph cfg) { } Map methods = actualCFGs.get(className); SimpleLogger.debug("Added CFG for class " + className + " and method " + methodName); - cfg.finalise(); methods.put(methodName, cfg); if (isWriteCfgEnabled()) @@ -257,6 +245,11 @@ private void createAndRegisterControlDependence(ActualControlFlowGraph cfg) { cds.put(methodName, cd); if (isWriteCfgEnabled()) cd.toDot(); + + ControlDependenceGraphDto dto = buildControlDependenceDto(className, methodName, cd); + if (dto != null) { + appendExportLog(dto); + } } /** @@ -310,6 +303,141 @@ public static void clearAll(String className, String 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 index fd6fe9b751..242e8800c4 100755 --- 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 @@ -1,33 +1,17 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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.cfg.branch.Branch; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.EvoSuiteGraph; +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 EvoSuiteGraph { +public class ControlDependenceGraph extends EvoMasterGraph { private final ActualControlFlowGraph cfg; @@ -47,95 +31,9 @@ public ControlDependenceGraph(ActualControlFlowGraph cfg) { this.methodName = cfg.getMethodName(); computeGraph(); - // TODO check sanity - } - - /** - * Convenience method redirecting to getControlDependentBranches(BasicBlock) - * if the given instruction is known to this CDG. Otherwise an - * IllegalArgumentException will be thrown. - * - * Should no longer be used: rather ask a BasicBlock for its CDs, so it can - * cache it. - */ - // public Set - // getControlDependentBranches(BytecodeInstruction ins) { - // if (ins == null) - // throw new IllegalArgumentException("null not accepted"); - // if (!knowsInstruction(ins)) - // throw new IllegalArgumentException( - // "instruction not known to this CDG: " + methodName - // + ins.toString()); - // - // BasicBlock insBlock = ins.getBasicBlock(); - // - // return getControlDependentBranches(insBlock); - // } - - /** - * Checks whether this graph knows the given instruction. That is there is a - * BasicBlock in this graph's vertexSet containing the given instruction. - * - * @param ins a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. - * @return a boolean. - */ - public boolean knowsInstruction(BytecodeInstruction ins) { - return cfg.knowsInstruction(ins); - } - - /** - *

getControlDependenceDepth

- * - * @param dependence a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlDependency} object. - * @return a int. - */ - public int getControlDependenceDepth(ControlDependency dependence) { - int min = Integer.MAX_VALUE; - for (BasicBlock root : determineEntryPoints()) { - int distance = getDistance(root, - dependence.getBranch().getInstruction().getBasicBlock()); - if (distance < min) - min = distance; - } - return min; - } - - /** - *

getAlternativeBlocks

- * - * @param dependency a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlDependency} object. - * @return a {@link java.util.Set} object. - */ - public Set getAlternativeBlocks(ControlDependency dependency) { - Set blocks = new LinkedHashSet<>(); - Branch branch = dependency.getBranch(); - - BasicBlock block = branch.getInstruction().getBasicBlock(); - for (ControlFlowEdge e : outgoingEdgesOf(block)) { - // TODO: Why can this be null? - if (e.getControlDependency() == null - || e.getControlDependency().equals(dependency)) - continue; - BasicBlock next = getEdgeTarget(e); - blocks.add(next); - getReachableBasicBlocks(blocks, next); - // blocks.addAll(getReachableBasicBlocks(next)); - } - return blocks; - } - - private void getReachableBasicBlocks(Set blocks, BasicBlock start) { - for (ControlFlowEdge e : outgoingEdgesOf(start)) { - BasicBlock next = getEdgeTarget(e); - if (!blocks.contains(next)) { - blocks.add(next); - getReachableBasicBlocks(blocks, next); - // blocks.addAll(getReachableBasicBlocks(next)); - } - } - // return blocks; } + /** * Returns a Set containing all Branches the given BasicBlock is control * dependent on. @@ -183,286 +81,9 @@ private Set retrieveControlDependencies(BasicBlock insBlock, } - // TODO need RootBranch Object!!! - // TODO the following does not hold! a node can be dependent on the root - // branch AND another branch! TODO !!! - // // sanity check - // if (r.isEmpty()) { - // Set insParents = getParents(insBlock); - // if (insParents.size() != 1) { - // - // for (BasicBlock b : insParents) - // logger.error(b.toString()); - // - // throw new IllegalStateException( - // "expect instruction dependent on root branch to have exactly one parent in it's CDG namely the EntryBlock: " - // + insBlock.toString()); - // } - // - // for (BasicBlock b : insParents) - // if (!b.isEntryBlock() && !getControlDependentBranches(b).isEmpty()) - // throw new IllegalStateException( - // "expect instruction dependent on root branch to have exactly one parent in it's CDG namely the EntryBlock" - // + insBlock.toString() + methodName); - // } - return r; } - /** - *

getControlDependentBranchIds

- * - * @param ins a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. - * @return a {@link java.util.Set} object. - */ - public Set getControlDependentBranchIds(BasicBlock ins) { - - Set dependentBranches = getControlDependentBranches(ins); - - Set r = new LinkedHashSet<>(); - - for (ControlDependency cd : dependentBranches) { - if (cd == null) - throw new IllegalStateException( - "expect set returned by getControlDependentBranches() not to contain null"); - - r.add(cd.getBranch().getActualBranchId()); - } - - // to indicate this is only dependent on root branch, - // meaning entering the method - if (isRootDependent(ins)) - r.add(-1); - - return r; - } - - // /** - // * Determines whether the given Branch has to be evaluated to true or to - // * false in order to reach the given BytecodeInstruction - given the - // * instruction is directly control dependent on the given Branch. - // * - // * In other words this method checks whether there is an incoming - // * ControlFlowEdge to the given instruction's BasicBlock containing the - // * given Branch as it's BranchInstruction and if so, that edges - // * branchExpressionValue is returned. If the given instruction is directly - // * control dependent on the given branch such a ControlFlowEdge must - // exist. - // * Should this assumption be violated an IllegalStateException is thrown. - // * - // * If the given instruction is not known to this CDG or not directly - // control - // * dependent on the given Branch an IllegalArgumentException is thrown. - // */ - // public boolean getBranchExpressionValue(BytecodeInstruction ins, Branch - // b) { - // if (ins == null) - // throw new IllegalArgumentException("null given"); - // if (!ins.isDirectlyControlDependentOn(b)) - // throw new IllegalArgumentException( - // "only allowed to call this method for instructions and their directly control dependent branches"); - // if (b == null) - // return true; // root branch special case - // - // BasicBlock insBlock = ins.getBasicBlock(); - // - // for (ControlFlowEdge e : incomingEdgesOf(insBlock)) { - // if (e.isExceptionEdge() && !e.hasControlDependency()) - // continue; - // - // Branch current = e.getBranchInstruction(); - // if (current == null) { - // try { - // BasicBlock in = getEdgeSource(e); - // return getBranchExpressionValue(in.getFirstInstruction(), b); - // } catch (Exception ex) { - // continue; - // } - // } else if (current.equals(b)) - // return e.getBranchExpressionValue(); - // } - // - // throw new IllegalStateException( - // "expect CDG to contain an incoming edge to the given instructions basic block containing the given branch if isControlDependent() returned true on those two "); - // } - - // initialization - - /** - * Determines whether the given BytecodeInstruction is directly control - * dependent on the given Branch. It's BasicBlock is control dependent on - * the given Branch. - *

- * If b is null, it is assumed to be the root branch. - *

- * If the given instruction is not known to this CDG an - * IllegalArgumentException is thrown. - * - * @param ins a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. - * @param b a {@link org.evomaster.client.java.coverage.branch.Branch} object. - * @return a boolean. - */ - public boolean isDirectlyControlDependentOn(BytecodeInstruction ins, Branch b) { - if (ins == null) - throw new IllegalArgumentException("null given"); - - BasicBlock insBlock = ins.getBasicBlock(); - - return isDirectlyControlDependentOn(insBlock, b); - } - - /** - * Determines whether the given BasicBlock is directly control dependent on - * the given Branch. Meaning within this CDG there is an incoming - * ControlFlowEdge to this instructions BasicBlock holding the given Branch - * as it's branchInstruction. - *

- * If b is null, it is assumed to be the root branch. - *

- * If the given instruction is not known to this CDG an - * IllegalArgumentException is thrown. - * - * @param insBlock a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. - * @param b a {@link org.evomaster.client.java.coverage.branch.Branch} object. - * @return a boolean. - */ - public boolean isDirectlyControlDependentOn(BasicBlock insBlock, Branch b) { - Set incomming = incomingEdgesOf(insBlock); - - if (incomming.size() == 1) { - // in methods with a try-catch-block it is possible that there - // are nodes in the CDG that have exactly one parent with an - // edge without a branchInstruction that is a non exceptional - // edge - // should the given instruction be such a node, follow the parents - // until - // you reach one where the above conditions are not met - - for (ControlFlowEdge e : incomming) { - if (!e.hasControlDependency() && !e.isExceptionEdge()) { - return isDirectlyControlDependentOn(getEdgeSource(e), b); - } - } - } - - boolean isRootDependent = isRootDependent(insBlock); - if (b == null) - return isRootDependent; - if (isRootDependent && b != null) - return false; - - for (ControlFlowEdge e : incomming) { - Branch current = e.getBranchInstruction(); - - if (e.isExceptionEdge()) { - if (current != null) - throw new IllegalStateException( - "expect exception edges to have no BranchInstruction set"); - else - continue; - } - - if (current == null) - continue; - // throw new IllegalStateException( - // "expect non exceptional ControlFlowEdges whithin the CDG that don't come from EntryBlock to have branchInstructions set " - // + insBlock.toString() + methodName); - - if (current.equals(b)) - return true; - } - - return false; - - } - - /** - * Checks whether the given instruction is dependent on the root branch of - * it's method - *

- * This is the case if the BasicBlock of the given instruction is directly - * adjacent to the EntryBlock - * - * @param ins a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. - * @return a boolean. - */ - public boolean isRootDependent(BytecodeInstruction ins) { - - return isRootDependent(ins.getBasicBlock()); - } - - /** - * Checks whether the given basicBlock is dependent on the root branch of - * it's method - *

- * This is the case if the BasicBlock of the given instruction is directly - * adjacent to the EntryBlock - * - * @param insBlock a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. - * @return a boolean. - */ - public boolean isRootDependent(BasicBlock insBlock) { - if (isAdjacentToEntryBlock(insBlock)) - return true; - - for (ControlFlowEdge in : incomingEdgesOf(insBlock)) { - if (in.hasControlDependency()) - continue; - BasicBlock inBlock = getEdgeSource(in); - if (inBlock.equals(insBlock)) - continue; - - if (isRootDependent(inBlock)) - return true; - } - - return false; - - } - - /** - * Returns true if the given BasicBlock has an incoming edge from this CDG's - * EntryBlock or is itself the EntryBlock - * - * @param insBlock a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. - * @return a boolean. - */ - public boolean isAdjacentToEntryBlock(BasicBlock insBlock) { - - if (insBlock.isEntryBlock()) - return true; - - Set parents = getParents(insBlock); - for (BasicBlock parent : parents) - if (parent.isEntryBlock()) - return true; - - return false; - } - - // /** - // * If the given instruction is known to this graph, the BasicBlock holding - // * that instruction is returned. Otherwise an IllegalArgumentException - // will - // * be thrown. - // * - // * Just a convenience method that more or less just redirects the call to - // * the CFG - // */ - // public BasicBlock getBlockOf(BytecodeInstruction ins) { - // if (ins == null) - // throw new IllegalArgumentException("null given"); - // if (!cfg.knowsInstruction(ins)) - // throw new IllegalArgumentException("unknown instruction"); - // - // BasicBlock insBlock = cfg.getBlockOf(ins); - // if (insBlock == null) - // throw new IllegalStateException( - // "expect CFG to return non-null BasicBlock for instruction it knows"); - // - // return insBlock; - // } - // init private void computeGraph() { @@ -473,7 +94,7 @@ private void computeGraph() { private void createGraphNodes() { // copy CFG nodes - addVertices(cfg); + addVertices(cfg.vertexSet()); for (BasicBlock b : vertexSet()) if (b.isExitBlock() && !graph.removeVertex(b)) // TODO refactor @@ -504,7 +125,7 @@ private void computeControlDependence() { // 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. TODO TRY! + // followed in that way. SimpleLogger.debug("cd: " + cd); SimpleLogger.debug("b: " + b); 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 index b0167840b7..8d4ba72c45 100755 --- 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 @@ -1,21 +1,6 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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; @@ -34,7 +19,6 @@ *

* Look at cfg.DominatorTree for more detailed information * - * @author Andre Mis */ class DominatorNode { 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 index a53308e503..ec16a547bc 100755 --- 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 @@ -1,25 +1,10 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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.EvoSuiteGraph; +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; @@ -52,9 +37,8 @@ * Dependence Graph" RON CYTRON, JEANNE FERRANTE, BARRY K. ROSEN, and MARK N. * WEGMAN IBM Research Division and F. KENNETH ZADECK Brown University 1991 * - * @author Andre Mis */ -public class DominatorTree extends EvoSuiteGraph, DefaultEdge> { +public class DominatorTree extends EvoMasterGraph, DefaultEdge> { private int nodeCount = 0; private final ControlFlowGraph cfg; diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ASMWrapper.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ASMWrapper.java deleted file mode 100755 index 39317b6929..0000000000 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ASMWrapper.java +++ /dev/null @@ -1,905 +0,0 @@ -/* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . - */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.*; -import org.objectweb.asm.util.Printer; - -// TODO: the following methods about control dependence are flawed right now: -// - the BytecodeInstruction of a Branch does not have it's control dependent -// branchId -// but it's own branchId set -// - this seems to be OK for ChromosomeRecycling as it stands, but -// especially getControlDependentBranch() will fail hard when called on a Branch -// the same may hold for the other ones as well. -// - look at BranchCoverageGoal and Branch for more information - -/** - * Wrapper class for the underlying byteCode instruction library ASM - *

- * Gives access to a lot of methods that interpret the raw information in - * AbstractInsnNode to usable chunks of information, inside EvoSuite - *

- * This class is supposed to hide the ASM library from the rest of EvoSuite as - * much as possible - *

- * After initialization, all information about byteCode instructions should be - * accessible via the BytecodeInstruction-, DefUse- and BranchPool. Each of - * those has data structures holding all BytecodeInstruction, DefUse and Branch - * objects respectively created during initialization. - *

- * BytecodeInstruction directly extends ASMWrapper and is the first way to - * instantiate an ASMWrapper. Branch and DefUse extend BytecodeInstruction, - * where DefUse is abstract and Branch is not ("concrete"?) DefUse is further - * extended by Definition and Use - * - * @author Andre Mis - */ -public abstract class ASMWrapper { - - // from ASM library - protected AbstractInsnNode asmNode; - protected CFGFrame frame; - - /** - *

- * 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(); - } - - public String getMethodCallDescriptor() { - if (!isMethodCall()) - return null; - MethodInsnNode meth = (MethodInsnNode) asmNode; - return meth.desc; - } - - /** - *

- * 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; - } - - /** - *

- * getInstructionId - *

- * - * @return a int. - */ - public abstract int getInstructionId(); - - /** - *

- * getMethodName - *

- * - * @return a {@link java.lang.String} object. - */ - public abstract String getMethodName(); - - /** - *

- * getClassName - *

- * - * @return a {@link java.lang.String} object. - */ - public abstract String getClassName(); - - public abstract boolean isMethodCallOfField(); - - public abstract String getFieldMethodCallName(); - - // methods for branch analysis - - /** - *

- * isActualBranch - *

- * - * @return a boolean. - */ - public boolean isActualBranch() { - return isBranch(); - } - - /** - *

- * isSwitch - *

- * - * @return a boolean. - */ - public boolean isSwitch() { - return isLookupSwitch() || isTableSwitch(); - } - - /** - *

- * 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; - } - - /** - *

- * isTableSwitch - *

- * - * @return a boolean. - */ - public boolean isTableSwitch() { - return (asmNode instanceof TableSwitchInsnNode); - } - - /** - *

- * isLookupSwitch - *

- * - * @return a boolean. - */ - public boolean isLookupSwitch() { - return (asmNode instanceof LookupSwitchInsnNode); - } - - /** - *

- * isBranchLabel - *

- * - * @return a boolean. - */ - public boolean isBranchLabel() { - return asmNode instanceof LabelNode - && ((LabelNode) asmNode).getLabel().info instanceof Integer; - } - - /** - *

- * isJump - *

- * - * @return a boolean. - */ - public boolean isJump() { - return (asmNode instanceof JumpInsnNode); - } - - /** - *

- * isInvokeStatic - *

- * - * @return a boolean representing whether the instruction is a static method - * call. - */ - public boolean isInvokeStatic() { - if (asmNode instanceof MethodInsnNode) { - return (asmNode.getOpcode() == Opcodes.INVOKESTATIC); - } - return false; - } - - /** - *

- * 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()); - } - - // public int getBranchId() { - // // return ((Integer)((LabelNode)node).getLabel().info).intValue(); - // return line_no; - // } - - /** - *

- * isIfNull - *

- * - * @return a boolean. - */ - public boolean isIfNull() { - if (asmNode instanceof JumpInsnNode) { - return (asmNode.getOpcode() == Opcodes.IFNULL); - } - return false; - } - - /** - *

- * isFrame - *

- * - * @return a boolean. - */ - public boolean isFrame() { - return asmNode instanceof FrameNode; - } - - /** - *

- * isMethodCall - *

- * - * @return a boolean. - */ - public boolean isMethodCall() { - return asmNode instanceof MethodInsnNode; - } - - /** - * Returns the conjunction of the name and method descriptor of the method - * called by this instruction - * - * @return a {@link java.lang.String} object. - */ - public String getCalledMethod() { - if (!isMethodCall()) - return null; - MethodInsnNode meth = (MethodInsnNode) asmNode; - return meth.name + meth.desc; - } - - /** - * Returns the conjunction of the name of the method called by this - * instruction - * - * @return a {@link java.lang.String} object. - */ - public String getCalledMethodName() { - if (!isMethodCall()) - return null; - MethodInsnNode meth = (MethodInsnNode) asmNode; - return meth.name; - } - - /** - * Returns true if and only if the class of the method called by this - * instruction is the same as the given className - * - * @param className a {@link java.lang.String} object. - * @return a boolean. - */ - public boolean isMethodCallForClass(String className) { - if (isMethodCall()) { - // System.out.println("in isMethodCallForClass of "+toString()+" for arg "+className+" calledMethodsClass: "+getCalledMethodsClass()+" calledMethod "+getCalledMethod()); - return getCalledMethodsClass().equals(className); - } - return false; - } - - /** - * Returns the class of the method called by this instruction - * - * @return a {@link java.lang.String} object. - */ - public String getCalledMethodsClass() { - if (isMethodCall()) { - MethodInsnNode mn = (MethodInsnNode) asmNode; - return mn.owner.replaceAll("/", "\\."); - } - return null; - } - - /** - * Returns the number of arguments of the method called by this instruction - * - * @return a int. - */ - public int getCalledMethodsArgumentCount() { - if (isMethodCall()) { - // int r = 0; - MethodInsnNode mn = (MethodInsnNode) asmNode; - Type[] argTypes = Type.getArgumentTypes(mn.desc); - - return argTypes.length; - // for(Type argType : argTypes) { - // r+=argType.getSize(); - // } - // return r; - } - return -1; - } - - /** - *

- * isLoadConstant - *

- * - * @return a boolean. - */ - public boolean isLoadConstant() { - return asmNode.getOpcode() == Opcodes.LDC; - } - - /** - *

- * isConstant - *

- * - * @return a boolean. - */ - public boolean isConstant() { - switch (asmNode.getOpcode()) { - case Opcodes.LDC: - case Opcodes.ICONST_0: - case Opcodes.ICONST_1: - case Opcodes.ICONST_2: - case Opcodes.ICONST_3: - case Opcodes.ICONST_4: - case Opcodes.ICONST_5: - case Opcodes.ICONST_M1: - case Opcodes.LCONST_0: - case Opcodes.LCONST_1: - case Opcodes.DCONST_0: - case Opcodes.DCONST_1: - case Opcodes.FCONST_0: - case Opcodes.FCONST_1: - case Opcodes.FCONST_2: - case Opcodes.BIPUSH: - case Opcodes.SIPUSH: - return true; - default: - return false; - } - } - - // methods for defUse analysis - - /** - *

- * isDefUse - *

- * - * @return a boolean. - */ - public boolean isDefUse() { - return isDefinition() || isUse(); - } - - /** - *

- * isFieldDU - *

- * - * @return a boolean. - */ - public boolean isFieldDU() { - return isFieldDefinition() || isFieldUse(); - } - - public boolean isFieldNodeDU() { - return isFieldNodeDefinition() || isFieldNodeUse(); - } - - /** - *

- * isLocalDU - *

- * - * @return a boolean. - */ - public boolean isLocalDU() { - return isLocalVariableDefinition() || isLocalVariableUse(); - } - - /** - *

- * isDefinition - *

- * - * @return a boolean. - */ - public boolean isDefinition() { - return isFieldDefinition() || isLocalVariableDefinition(); - } - - /** - *

- * isUse - *

- * - * @return a boolean. - */ - public boolean isUse() { - return isFieldUse() || isLocalVariableUse() || isArrayLoadInstruction(); - } - - /** - *

- * isFieldDefinition - *

- * - * @return a boolean. - */ - public boolean isFieldDefinition() { - return asmNode.getOpcode() == Opcodes.PUTFIELD - || asmNode.getOpcode() == Opcodes.PUTSTATIC - || isFieldArrayDefinition(); - } - - /** - *

- * isFieldDefinition - *

- * - * @return a boolean. - */ - public boolean isFieldNodeDefinition() { - return asmNode.getOpcode() == Opcodes.PUTFIELD - || asmNode.getOpcode() == Opcodes.PUTSTATIC || isFieldArrayDefinition(); - } - - /** - *

- * isFieldUse - *

- * - * @return a boolean. - */ - public boolean isFieldUse() { - return asmNode.getOpcode() == Opcodes.GETFIELD - || asmNode.getOpcode() == Opcodes.GETSTATIC; - } - - /** - *

- * isFieldUse - *

- * - * @return a boolean. - */ - public boolean isFieldNodeUse() { - return asmNode.getOpcode() == Opcodes.GETFIELD - || asmNode.getOpcode() == Opcodes.GETSTATIC; - } - - /** - *

- * isStaticDefUse - *

- * - * @return a boolean. - */ - public boolean isStaticDefUse() { - return asmNode.getOpcode() == Opcodes.PUTSTATIC - || asmNode.getOpcode() == Opcodes.GETSTATIC || isStaticArrayUsage(); - } - - // retrieving information about variable names from ASM - - /** - *

- * getDUVariableName - *

- * - * @return a {@link java.lang.String} object. - */ - public String getVariableName() { - if (isArrayStoreInstruction()) { - return getArrayVariableName(); - } else if (isArrayLoadInstruction()) { - return getArrayVariableName(); - } else if (isLocalDU()) { - return getLocalVariableName(); - } else if (isMethodCallOfField()) { - return getFieldMethodCallName(); - } else if (isFieldDU()) { - return getFieldName(); - } else { - return null; - } - } - - /** - *

- * getFieldName - *

- * - * @return a {@link java.lang.String} object. - */ - protected String getFieldSimpleName() { - FieldInsnNode fieldNode = (FieldInsnNode) asmNode; - return fieldNode.name; - // return fieldNode.name; - } - - /** - *

- * getFieldName - *

- * - * @return a {@link java.lang.String} object. - */ - protected String getFieldName() { - FieldInsnNode fieldNode = (FieldInsnNode) asmNode; - return fieldNode.owner + "." + fieldNode.name; - // return fieldNode.name; - } - - /** - *

- * getFieldName - *

- * - * @return a {@link java.lang.String} object. - */ - public String getFieldType() { - FieldInsnNode fieldNode = (FieldInsnNode) asmNode; - return fieldNode.desc; - // return fieldNode.name; - } - - /** - *

- * getLocalVarName - *

- * - * @return a {@link java.lang.String} object. - */ - protected String getLocalVariableName() { - String ret = getMethodName() + "_LV_" + getLocalVariableSlot(); - return ret; - } - - // TODO unsafe - - /** - *

- * getLocalVar - *

- * - * @return a int. - */ - public int getLocalVariableSlot() { - if (asmNode instanceof VarInsnNode) - return ((VarInsnNode) asmNode).var; - else if (asmNode instanceof IincInsnNode) - return ((IincInsnNode) asmNode).var; - else - return -1; - } - - /** - *

- * isLocalVarDefinition - *

- * - * @return a boolean. - */ - public boolean isLocalVariableDefinition() { - return asmNode.getOpcode() == Opcodes.ISTORE - || asmNode.getOpcode() == Opcodes.LSTORE - || asmNode.getOpcode() == Opcodes.FSTORE - || asmNode.getOpcode() == Opcodes.DSTORE - || asmNode.getOpcode() == Opcodes.ASTORE - || asmNode.getOpcode() == Opcodes.IINC || isLocalArrayDefinition(); - } - - /** - *

- * isLocalVarUse - *

- * - * @return a boolean. - */ - public boolean isLocalVariableUse() { - return asmNode.getOpcode() == Opcodes.ILOAD - || asmNode.getOpcode() == Opcodes.LLOAD - || asmNode.getOpcode() == Opcodes.FLOAD - || asmNode.getOpcode() == Opcodes.DLOAD - || asmNode.getOpcode() == Opcodes.IINC - || (asmNode.getOpcode() == Opcodes.ALOAD && !loadsReferenceToThis()); - } - - public boolean isIINC() { - return asmNode.getOpcode() == Opcodes.IINC; - } - - /** - * Determines whether this is the special ALOAD that pushes 'this' onto the - * stack - *

- * In non static methods the variable slot 0 holds the reference to "this". - * Loading this reference is not seen as a Use for defuse purposes. This - * method checks if this is the case - * - * @return a boolean. - */ - public boolean loadsReferenceToThis() { - if (getRawCFG().isStaticMethod()) { - return false; - } - - return asmNode.getOpcode() == Opcodes.ALOAD && getLocalVariableSlot() == 0; - } - - public abstract RawControlFlowGraph getRawCFG(); - - public boolean isLocalArrayDefinition() { - if (!isArrayStoreInstruction()) - return false; - - // only local var if arrayref on stack is from local var use - BytecodeInstruction arrayLoader = getSourceOfArrayReference(); - if (arrayLoader == null) - return false; - - return arrayLoader.isLocalVariableUse(); - } - - public boolean isFieldArrayDefinition() { - if (!isArrayStoreInstruction()) - return false; - - // only field var if arrayref on stack is from field var use - BytecodeInstruction arrayLoader = getSourceOfArrayReference(); - if (arrayLoader == null) - return false; - - return arrayLoader.isFieldUse(); - } - - public boolean isStaticArrayUsage() { - if (!isArrayStoreInstruction()) - return false; - - BytecodeInstruction arrayLoader = getSourceOfArrayReference(); - if (arrayLoader == null) - return false; - - return arrayLoader.isStaticDefUse(); - } - - public boolean isArrayStoreInstruction() { - return asmNode.getOpcode() == Opcodes.IASTORE - || asmNode.getOpcode() == Opcodes.LASTORE - || asmNode.getOpcode() == Opcodes.FASTORE - || asmNode.getOpcode() == Opcodes.DASTORE - || asmNode.getOpcode() == Opcodes.AASTORE; - } - - public boolean isArrayLoadInstruction() { - return asmNode.getOpcode() == Opcodes.IALOAD - || asmNode.getOpcode() == Opcodes.LALOAD - || asmNode.getOpcode() == Opcodes.FALOAD - || asmNode.getOpcode() == Opcodes.DALOAD - || asmNode.getOpcode() == Opcodes.AALOAD; - } - - protected String getArrayVariableName() { - BytecodeInstruction arrayLoader = getSourceOfArrayReference(); - if (arrayLoader == null) - return null; - - return arrayLoader.getVariableName(); - } - - public abstract BytecodeInstruction getSourceOfArrayReference(); - - /** - *

- * isDefinitionForVariable - *

- * - * @param var a {@link java.lang.String} object. - * @return a boolean. - */ - public boolean isDefinitionForVariable(String var) { - return (isDefinition() && getVariableName().equals(var)); - } - - /** - *

- * isInvokeSpecial - *

- * - * @return a boolean. - */ - public boolean isInvokeSpecial() { - return asmNode.getOpcode() == Opcodes.INVOKESPECIAL; - } - - /** - * Checks whether this instruction is an INVOKESPECIAL instruction that - * calls a constructor. - * - * @return a boolean. - */ - public boolean isConstructorInvocation() { - if (!isInvokeSpecial()) - return false; - - MethodInsnNode invoke = (MethodInsnNode) asmNode; - // if (!invoke.owner.equals(className.replaceAll("\\.", "/"))) - // return false; - - return invoke.name.equals(""); - } - - /** - * Checks whether this instruction is an INVOKESPECIAL instruction that - * calls a constructor of the given class. - * - * @param className a {@link java.lang.String} object. - * @return a boolean. - */ - public boolean isConstructorInvocation(String className) { - if (!isInvokeSpecial()) - return false; - - MethodInsnNode invoke = (MethodInsnNode) asmNode; - if (!invoke.owner.equals(className.replaceAll("\\.", "/"))) - return false; - - return invoke.name.equals(""); - } - - // other classification methods - - /** - * 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); - } - - /** - *

- * getLineNumber - *

- * - * @return a int. - */ - public int getLineNumber() { - if (!isLineNumber()) - return -1; - - return ((LineNumberNode) asmNode).line; - } - - /** - *

- * isLabel - *

- * - * @return a boolean. - */ - public boolean isLabel() { - return asmNode instanceof LabelNode; - } - - // sanity checks - - /** - *

- * sanityCheckAbstractInsnNode - *

- * - * @param node a {@link org.objectweb.asm.tree.AbstractInsnNode} object. - */ - public boolean sanityCheckAbstractInsnNode(AbstractInsnNode node) { - if (node == null) - return false; //throw new IllegalArgumentException("null given"); - return node.equals(this.asmNode); - // - // throw new IllegalStateException("sanity check failed for " - // + node.toString() + " on " + getMethodName() + toString()); - } - -} 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 index 3e3c2fa4a9..07ee96ad97 100755 --- 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 @@ -1,21 +1,6 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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; @@ -24,48 +9,6 @@ import java.util.HashSet; import java.util.Set; -/** - * Supposed to become the new implementation of a control flow graph inside - * EvoSuite - *

- * The "actual" CFG does not contain single cfg.BytecodeInstructions as nodes, - * but contains cfg.BasicBlocks - look at that class for more information - *

- * Simply put this is a minimized version of the complete/raw CFG the - * cfg.BytecodeAnalyzer and cfg.CFGGenerator produce - which holds a node for - * every BytecodeInstruction - *

- * Out of the above described raw CFG the following "pin-points" are extracted: - *

- * 1) the entryNode (first instruction in the method) - *

- * 2) all exitNodes (outDegree 0) - *

- * 3.1) all branches (outDegree>1) 3.2) in a subsequent step all targets of all - * branches - *

- * 4.1) all joins (inDegree>1) 4.2) in a subsequent step all sources of all - * joins - *

- * All those "pin-points" are put into a big set (some of the above categories - * may overlap) and for all those single BytecodeInstrucions their corresponding - * BasicBlock is computed and added to this CFGs vertexSet. After that the raw - * CFG is asked for the parents of the first instruction of each BasicBlock and - * the children of that blocks last instruction. Then the edges to their - * corresponding BasicBlocks are added to this CFG - *

- * TODO: verify that this method works :D - *

- *

- * cfg.EvoSuiteGraph is used as the underlying data structure holding the - * graphical representation of the CFG - *

- * WORK IN PROGRESS - *

- * TODO implement - * - * @author Andre Mis - */ public class ActualControlFlowGraph extends ControlFlowGraph { private RawControlFlowGraph rawGraph; @@ -94,8 +37,6 @@ public ActualControlFlowGraph(RawControlFlowGraph rawGraph) { computeGraph(); } - // "revert" constructor ... for now ... TODO - /** *

* Constructor for ActualControlFlowGraph. @@ -117,8 +58,8 @@ protected ActualControlFlowGraph(ActualControlFlowGraph toRevert) { * @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 EvoSuiteGraph for - // all sub class of EvoSuiteGraph + // TODO: this must be possible to "pre implement" in EvoMasterGraph for + // all sub class of EvoMasterGraph return new ActualControlFlowGraph(this); } @@ -202,25 +143,6 @@ private void setBranches(Set branches) { if (!belongsToMethod(branch)) throw new IllegalArgumentException( "branch does not belong to this CFGs method"); - // if (!branch.isActualBranch()) // TODO this happens if your in a - // try-catch ... handle! - // throw new IllegalArgumentException( - // "unexpected branch byteCode instruction type: " - // + branch.getInstructionType()); - - // TODO the following doesn't work at this point - // because the BranchPool is not yet filled yet - // BUT one could fill the pool right now and drop further analysis - // later - // way cooler, because then filling of the BranchPool is unrelated - // to - // BranchInstrumentation - then again that instrumentation is needed - // anyways i guess - - // if (!BranchPool.isKnownAsBranch(instruction)) - // throw new IllegalStateException( - // "expect BranchPool to know all branching instructions: " - // + instruction.toString()); this.branches.add(branch); } @@ -259,9 +181,6 @@ private void computeGraph() { computeNodes(); computeEdges(); - // TODO: Need to make that compatible with Testability Transformation - // checkSanity(); - addAuxiliaryBlocks(); } @@ -356,29 +275,6 @@ protected void addBlock(BasicBlock nodeBlock) { throw new IllegalStateException( "internal error while addind basic block to CFG"); - // for (BasicBlock test : graph.vertexSet()) { - // logger.debug("experimental self-equals on " + test.getName()); - // if (nodeBlock.equals(test)) - // logger.debug("true"); - // else - // logger.debug("false"); - // if (!containsBlock(test)) - // throw new IllegalStateException("wtf"); - // - // logger.debug("experimental equals on " + test.getName() + " with " - // + nodeBlock.getName()); - // if (test.equals(nodeBlock)) - // logger.debug("true"); - // else - // logger.debug("false"); - // - // logger.debug("experimental dual-equal"); - // if (nodeBlock.equals(test)) - // logger.debug("true"); - // else - // logger.debug("false"); - // - // } if (!containsVertex(nodeBlock)) throw new IllegalStateException( @@ -527,49 +423,6 @@ public boolean knowsInstruction(BytecodeInstruction instruction) { * @param v2 a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. * @return a int. */ - public int getDistance(BytecodeInstruction v1, BytecodeInstruction v2) { - if (v1 == null || v2 == null) - throw new IllegalArgumentException("null given"); - if (!knowsInstruction(v1) || !knowsInstruction(v2)) - throw new IllegalArgumentException("instructions not contained in this CFG"); - - BasicBlock b1 = v1.getBasicBlock(); - BasicBlock b2 = v2.getBasicBlock(); - - if (b1 == null || b2 == null) - throw new IllegalStateException( - "expect CFG to contain the BasicBlock for each instruction knowsInstruction() returns true on"); - - return getDistance(b1, b2); - } - - /** - *

- * isDirectSuccessor - *

- * - * @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 boolean. - */ - public boolean isDirectSuccessor(BytecodeInstruction v1, BytecodeInstruction v2) { - if (v1 == null || v2 == null) - throw new IllegalArgumentException("null given"); - if (!knowsInstruction(v1) || !knowsInstruction(v2)) - throw new IllegalArgumentException("instructions not contained in this CFG"); - - BasicBlock b1 = v1.getBasicBlock(); - BasicBlock b2 = v2.getBasicBlock(); - - if (b1 == null || b2 == null) - throw new IllegalStateException( - "expect CFG to contain the BasicBlock for each instruction knowsInstruction() returns true on"); - - return isDirectSuccessor(b1, b2); - } - - // retrieve information about the CFG - /** *

* isEntryPoint @@ -578,17 +431,10 @@ public boolean isDirectSuccessor(BytecodeInstruction v1, BytecodeInstruction v2) * @param block a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. * @return a boolean. */ - public boolean isEntryPoint(BasicBlock block) { + private boolean isEntryPoint(BasicBlock block) { if (block == null) throw new IllegalArgumentException("null given"); - // // sanity check - // if (!block.getFirstInstruction().equals(entryPoint)) { - // logger.error("entryPoint: "+entryPoint.toString()); - // logger.error("current block: "+block.explain()); - // throw new IllegalStateException( - // "expect entryPoint of a method to be the first instruction from the entryBlock of that method"); - // } return block.containsInstruction(entryPoint); } @@ -600,16 +446,12 @@ public boolean isEntryPoint(BasicBlock block) { * @param block a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. * @return a boolean. */ - public boolean isExitPoint(BasicBlock block) { + private boolean isExitPoint(BasicBlock block) { if (block == null) throw new IllegalArgumentException("null given"); for (BytecodeInstruction exitPoint : exitPoints) if (block.containsInstruction(exitPoint)) { - // // sanity check - // if (!block.getLastInstruction().equals(exitPoint)) - // throw new IllegalStateException( - // "expect exitPoints of a method to be the last instruction from an exitBlock of that method"); return true; } @@ -624,7 +466,7 @@ public boolean isExitPoint(BasicBlock block) { * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. * @return a boolean. */ - public boolean belongsToMethod(BytecodeInstruction instruction) { + private boolean belongsToMethod(BytecodeInstruction instruction) { if (instruction == null) throw new IllegalArgumentException("null given"); @@ -633,114 +475,6 @@ public boolean belongsToMethod(BytecodeInstruction instruction) { return methodName.equals(instruction.getMethodName()); } - // sanity checks - - /** - *

- * checkSanity - *

- */ - public void checkSanity() { - - SimpleLogger.debug("checking sanity of CFG for " + methodName); - - if (isEmpty()) - throw new IllegalStateException("a CFG must contain at least one element"); - - for (BytecodeInstruction initInstruction : getInitiallyKnownInstructions()) { - if (!knowsInstruction(initInstruction)) - throw new IllegalStateException( - "expect CFG to contain all initially known instructions"); - } - - SimpleLogger.debug(".. all initInstructions contained"); - - // checkNodeSanity(); - - checkInstructionsContainedOnceConstraint(); - - SimpleLogger.debug(".. CFG sanity ensured"); - } - - private void checkInstructionsContainedOnceConstraint() { - - for (BytecodeInstruction ins : rawGraph.vertexSet()) { - if (!knowsInstruction(ins)) - throw new IllegalStateException( - "expect all instructions ins underlying RawCFG to be known by Actual CFG"); - - BasicBlock insBlock = ins.getBasicBlock(); - if (insBlock == null) - throw new IllegalStateException( - "expect ActualCFG.getBlockOf() to return non-null BasicBlocks for all instructions it knows"); - - for (BasicBlock block : vertexSet()) { - if (!block.equals(insBlock) && block.containsInstruction(ins)) - throw new IllegalStateException( - "expect ActualCFG to contain exactly one BasicBlock for each original bytecode instruction, not more!"); - } - } - - } - - void checkNodeSanity() { - // ensure graph is connected and isEntry and isExitBlock() work as - // expected - for (BasicBlock node : vertexSet()) { - - checkEntryExitPointConstraint(node); - - checkSingleCFGNodeConstraint(node); - - checkNodeMinimalityConstraint(node); - } - SimpleLogger.debug("..all node constraints ensured"); - } - - void checkEntryExitPointConstraint(BasicBlock node) { - // exit point constraint - int out = outDegreeOf(node); - if (!isExitPoint(node) && out == 0) - throw new IllegalStateException( - "expect nodes without outgoing edges to be exitBlocks: " - + node.toString()); - // entry point constraint - int in = inDegreeOf(node); - if (!isEntryPoint(node) && in == 0) - throw new IllegalStateException( - "expect nodes without incoming edges to be the entryBlock: " - + node.toString()); - } - - void checkSingleCFGNodeConstraint(BasicBlock node) { - int in = inDegreeOf(node); - int out = outDegreeOf(node); - if (in + out == 0 && vertexCount() != 1) - throw new IllegalStateException( - "node with neither child nor parent only allowed if CFG consists of a single block: " - + node.toString()); - - if (vertexCount() == 1 && !(isEntryPoint(node) && isExitPoint(node))) - throw new IllegalStateException( - "if a CFG consists of a single basic block that block must be both entry and exitBlock: " - + node.toString()); - } - - void checkNodeMinimalityConstraint(BasicBlock node) { - - if (hasNPartentsMChildren(node, 1, 1)) { - for (BasicBlock child : getChildren(node)) - if (hasNPartentsMChildren(child, 1, 1)) - throw new IllegalStateException( - "whenever a node has exactly one child and one parent, it is expected that the same is true for either of those"); - - for (BasicBlock parent : getParents(node)) - if (hasNPartentsMChildren(parent, 1, 1)) - throw new IllegalStateException( - "whenever a node has exactly one child and one parent, it is expected that the same is true for either of those"); - } - } - // inherited from ControlFlowGraph /** @@ -774,43 +508,6 @@ public BytecodeInstruction getInstruction(int instructionId) { return null; } - // @Override - // public BytecodeInstruction getBranch(int branchId) { - // - // Branch searchedFor = BranchPool.getBranch(branchId); - // if (searchedFor == null) - // return null; - // - // if (containsInstruction(searchedFor.getInstruction())) - // return searchedFor.getInstruction(); - // - // // TODO more sanity checks? - // - // return null; - // } - - /** - *

- * Getter for the field entryPoint. - *

- * - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. - */ - public BytecodeInstruction getEntryPoint() { - return entryPoint; - } - - /** - *

- * Getter for the field exitPoints. - *

- * - * @return a {@link java.util.Set} object. - */ - public Set getExitPoints() { - return new HashSet<>(exitPoints); - } - /** *

* Getter for the field branches. @@ -822,17 +519,6 @@ public Set getBranches() { return new HashSet<>(branches); } - /** - *

- * Getter for the field joins. - *

- * - * @return a {@link java.util.Set} object. - */ - public Set getJoins() { - return new HashSet<>(joins); - } - /** * {@inheritDoc} */ 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 index 20e1cfc0fe..b0251a5125 100755 --- 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 @@ -1,32 +1,14 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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 org.objectweb.asm.tree.AbstractInsnNode; -import org.evomaster.client.java.utils.SimpleLogger; import java.io.Serializable; import java.util.*; -import java.util.stream.Collectors; /** * This class is used to represent basic blocks in the control flow graph. @@ -52,8 +34,7 @@ * Dependence Graph" RON CYTRON, JEANNE FERRANTE, BARRY K. ROSEN, and MARK N. * WEGMAN IBM Research Division and F. KENNETH ZADECK Brown University 1991 * - * @author Andre Mis - * @see cfg.ActualControlFlowGraph + * @see org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph */ public class BasicBlock implements Serializable, Iterable { @@ -66,21 +47,12 @@ public class BasicBlock implements Serializable, Iterable { protected String className; protected String methodName; - // experiment: since finding the control dependent branches in the CDG might - // take a little to long, we might want to remember them private Set controlDependencies; - private Set controlDependentBranchIDs; protected boolean isAuxiliaryBlock = false; private final List instructions = new ArrayList<>(); - // DONE reference each BytecodeInstruction's BasicBlock at the instruction - // DONE determine ControlDependentBranches once for each BasicBlock, then - // ask BasicBloc, whenever instruction is asked - // TODO remember distance to each control dependent Branch in order to speed - // up ControlFlowDistance calculation even more - /** *

* Constructor for BasicBlock. @@ -101,8 +73,6 @@ public BasicBlock(ClassLoader classLoader, String className, String methodName, setId(); setInstructions(blockNodes); - - checkSanity(); } /** @@ -140,29 +110,6 @@ public ControlDependenceGraph getCDG() { return myCDG; } - /** - * Returns all branchIds of Branches this instruction is directly control - * dependent on as determined by the ControlDependenceGraph for this - * instruction's method. - *

- * If this instruction is control dependent on the root branch the id -1 - * will be contained in this set - * - * @return a {@link java.util.Set} object. - */ - public Set getControlDependentBranchIds() { - - ControlDependenceGraph myDependence = getCDG(); - - if (controlDependentBranchIDs == null) { - controlDependentBranchIDs = myDependence.getControlDependentBranchIds(this); - //be sure we can iterate over it deterministically - controlDependentBranchIDs = - controlDependentBranchIDs.stream().sorted().collect(Collectors.toCollection(LinkedHashSet::new)); - } - return controlDependentBranchIDs; - } - /** * Returns a cfg.Branch object for each branch this instruction is control * dependent on as determined by the ControlDependenceGraph. If this @@ -225,17 +172,6 @@ private boolean appendInstruction(BytecodeInstruction instruction) { throw new IllegalArgumentException( "a basic block can not contain the same element twice"); - // not sure if this holds: - // .. apparently it doesn't. at least check - // fails for java2.util2.Pattern TODO - - // BytecodeInstruction previousInstruction = getLastInstruction(); - // if (previousInstruction != null - // && instruction.getInstructionId() < previousInstruction - // .getInstructionId()) - // throw new IllegalStateException( - // "expect instructions in a basic block to be ordered by their instructionId"); - instruction.setBasicBlock(this); return instructions.add(instruction); @@ -263,22 +199,6 @@ public boolean containsInstruction(BytecodeInstruction instruction) { return instructions.contains(instruction); } - /** - *

- * constainsInstruction - *

- * - * @param insnNode a {@link org.objectweb.asm.tree.AbstractInsnNode} object. - * @return a boolean. - */ - public boolean constainsInstruction(AbstractInsnNode insnNode) { - for (BytecodeInstruction instruction : instructions) { - if (instruction.getASMNode().equals(insnNode)) - return true; - } - return false; - } - /** *

* getFirstInstruction @@ -372,26 +292,6 @@ public String getMethodName() { return methodName; } - /** - *

- * explain - *

- * - * @return a {@link java.lang.String} object. - */ - public String explain() { - StringBuilder r = new StringBuilder(); - r.append(getName() + ":\n"); - - int i = 0; - for (BytecodeInstruction instruction : instructions) { - i++; - r.append("\t" + i + ")\t" + instruction.toString() + "\n"); - } - - return r.toString(); - } - // inherited from Object /** @@ -466,32 +366,6 @@ public boolean equals(Object obj) { return isExitBlock() == other.isExitBlock(); } - /** - *

- * checkSanity - *

- */ - public void checkSanity() { - - SimpleLogger.debug("checking sanity of " + this); - - // TODO - - // not true, there are branches that don't really jump - // for example if you have no statement in a then-part: - // if (exp) { ; } - // you will not have a second outgoing edge for that if - - // for(BytecodeInstruction instruction : instructions) { - // if (!instruction.equals(getLastInstruction()) - // && instruction.isActualBranch()) - // throw new IllegalStateException( - // "expect actual branches to always end a basic block: "+instruction.toString()+" \n"+explain()); - // } - - // TODO handle specialBlocks - } - /** *

* isEntryBlock diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeAnalyzer.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeAnalyzer.java deleted file mode 100755 index 1c7036c2fa..0000000000 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeAnalyzer.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . - */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; - -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.Frame; -import org.objectweb.asm.tree.analysis.SourceInterpreter; - -/** - * This class analyzes the byteCode from a method in the CUT and generates it's - * CFG using a cfg.CFGGenerator - *

- * This is done using the ASM library, extending from it's asm.Analyzer and - * redirecting the calls to newControlFlowEdge() to an instance of - * cfg.CFGGenerator which in turn builds up a graph representation of the CFG, - * which later is used to build a "smaller" CFG containing not - * BytecodeInstructions but BasicBlocks of BytecodeInstructions which are always - * executed successively - * - * @author Gordon Fraser, Andre Mis - */ -public class BytecodeAnalyzer extends Analyzer { - - CFGGenerator cfgGenerator; - - /** - *

- * Constructor for BytecodeAnalyzer. - *

- */ - public BytecodeAnalyzer() { - super(new SourceInterpreter()); - } - - // main interface - - /** - * Analyzes the method corresponding to the given Strings and initializes - * the CFGGenerator and thus the BytecodeInstructionPool for the given - * method in the given class. - * - * @param owner a {@link java.lang.String} object. - * @param method a {@link java.lang.String} object. - * @param node a {@link org.objectweb.asm.tree.MethodNode} object. - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.CFGFrame} object. - * @throws org.objectweb.asm.tree.analysis.AnalyzerException if any. - */ - public CFGFrame analyze(ClassLoader classLoader, String owner, String method, - MethodNode node) throws AnalyzerException { - - cfgGenerator = new CFGGenerator(classLoader, owner, method, node); - this.analyze(owner, node); - - Frame[] frames = getFrames(); - - if (frames.length == 0) - return null; - - return (CFGFrame) getFrames()[0]; - } - - /** - * After running analyze() this method yields the filled CFGGenerator for - * further processing of the gathered information from analyze() within the - * ByteCode representation of EvoSuite - * - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.CFGGenerator} object. - */ - public CFGGenerator retrieveCFGGenerator() { - if (cfgGenerator == null) - throw new IllegalStateException( - "you have to call analyze() first before retrieving the CFGGenerator"); - return cfgGenerator; - } - - // inherited from asm.Analyzer - - /** - * {@inheritDoc} - *

- * Called for each non-exceptional cfg edge - */ - @Override - protected void newControlFlowEdge(int src, int dst) { - - cfgGenerator.registerControlFlowEdge(src, dst, getFrames(), false); - } - - /** - * {@inheritDoc} - *

- * We also need to keep track of exceptional edges - they are also branches - */ - @Override - protected boolean newControlFlowExceptionEdge(int src, int dst) { - // TODO: Make use of information that this is an exception edge? - cfgGenerator.registerControlFlowEdge(src, dst, getFrames(), true); - - return true; - } - - /** - * {@inheritDoc} - */ - @Override - protected Frame newFrame(int nLocals, int nStack) { - return new CFGFrame(nLocals, nStack); - } - - /** - * {@inheritDoc} - */ - @Override - protected Frame newFrame(Frame src) { - return new CFGFrame(src); - } -} 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 index 0c9e57132a..43cb8ff21b 100755 --- 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 @@ -1,21 +1,6 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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; @@ -26,28 +11,22 @@ import org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg.ControlDependenceGraph; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.*; -import org.objectweb.asm.tree.analysis.SourceValue; +import org.objectweb.asm.util.Printer; import java.io.Serializable; import java.util.Set; /** * Internal representation of a BytecodeInstruction - *

- * Extends ASMWrapper which serves as an interface to the ASM library. - *

- * Known super classes are DefUse and Branch which yield specific functionality - * needed to achieve theirs respective coverage criteria - *

- * Old: Node of the control flow graph - * - * @author Gordon Fraser, Andre Mis */ -public class BytecodeInstruction extends ASMWrapper implements Serializable, +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; @@ -102,7 +81,6 @@ public BytecodeInstruction(BytecodeInstruction wrap) { this(wrap.classLoader, wrap.className, wrap.methodName, wrap.instructionId, wrap.bytecodeOffset, wrap.asmNode, wrap.lineNumber, wrap.basicBlock); - this.frame = wrap.frame; } /** @@ -166,23 +144,11 @@ private void setClassName(String className) { this.className = className; } - /** - *

- * setCFGFrame - *

- * - * @param frame a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.CFGFrame} object. - */ - public void setCFGFrame(CFGFrame frame) { - this.frame = frame; - } - // --- Field Management --- /** * {@inheritDoc} */ - @Override public int getInstructionId() { return instructionId; } @@ -201,7 +167,6 @@ public int getBytecodeOffset() { /** * {@inheritDoc} */ - @Override public String getMethodName() { return methodName; } @@ -213,22 +178,10 @@ public String getMethodName() { * * @return a {@link java.lang.String} object. */ - @Override public String getClassName() { return className; } - /** - *

- * getName - *

- * - * @return a {@link java.lang.String} object. - */ - public String getName() { - return "BytecodeInstruction " + instructionId + " in " + methodName; - } - /** * Return's the BasicBlock that contain's this instruction in it's CFG. *

@@ -283,7 +236,6 @@ public boolean hasBasicBlockSet() { /** * {@inheritDoc} */ - @Override public int getLineNumber() { if (lineNumber == -1 && isLineNumber()) @@ -308,7 +260,7 @@ public void setLineNumber(int lineNumber) { return; if (isLineNumber()) { - int asmLine = super.getLineNumber(); + int asmLine = getASMLineNumber(); // sanity check if (lineNumber != -1 && asmLine != lineNumber) throw new IllegalStateException( @@ -347,7 +299,7 @@ public boolean hasLineNumberSet() { */ private void retrieveLineNumber() { if (isLineNumber()) { - int asmLine = super.getLineNumber(); + int asmLine = getASMLineNumber(); // sanity check if (this.lineNumber != -1 && asmLine != this.lineNumber) throw new IllegalStateException( @@ -436,20 +388,7 @@ public Set getControlDependencies() { return myBlock.getControlDependencies(); } - /** - * This method returns a random Branch among all Branches this instruction - * is control dependent on - *

- * If this instruction is only dependent on the root branch, this method - * returns null - *

- * Since EvoSuite was previously unable to detect multiple control - * dependencies for one instruction this method serves as a backwards - * compatibility bridge - * - * @return a {@link org.evomaster.client.java.coverage.branch.Branch} object. - */ - public Branch getControlDependentBranch() { + public Branch getControlDependentBranch() { Set controlDependentBranches = getControlDependencies(); @@ -459,216 +398,6 @@ public Branch getControlDependentBranch() { return null; // root branch } - /** - * Returns all branchIds of Branches this instruction is directly control - * dependent on as determined by the ControlDependenceGraph for this - * instruction's method. - *

- * If this instruction is control dependent on the root branch the id -1 - * will be contained in this set - * - * @return a {@link java.util.Set} object. - */ - public Set getControlDependentBranchIds() { - - BasicBlock myBlock = getBasicBlock(); - - return myBlock.getControlDependentBranchIds(); - } - - /** - * Determines whether or not this instruction is control dependent on the - * root branch of it's method by calling getControlDependentBranchIds() to - * see if the return contains -1. - * - * @return a boolean. - */ - public boolean isRootBranchDependent() { - return getControlDependencies().isEmpty(); - } - - /** - * This method returns a random branchId among all branchIds this - * instruction is control dependent on. - *

- * This method returns -1 if getControlDependentBranch() returns null, - * otherwise that Branch's branchId is returned - *

- * Note: The returned branchExpressionValue comes from the same Branch - * getControlDependentBranch() and getControlDependentBranchId() return - *

- * Since EvoSuite was previously unable to detect multiple control - * dependencies for one instruction this method serves as a backwards - * compatibility bridge - * - * @return a int. - */ - public int getControlDependentBranchId() { - - Branch b = getControlDependentBranch(); - if (b == null) - return -1; - - return b.getActualBranchId(); - } - - /** - * This method returns the branchExpressionValue from a random Branch among - * all Branches this instruction is control dependent on. - *

- * This method returns true if getControlDependentBranch() returns null, - * otherwise that Branch's branchExpressionValue is returned - *

- * Note: The returned branchExpressionValue comes from the same Branch - * getControlDependentBranch() and getControlDependentBranchId() return - *

- * Since EvoSuite was previously unable to detect multiple control - * dependencies for one instruction this method serves as a backwards - * compatibility bridge - * - * @return a boolean. - */ - public boolean getControlDependentBranchExpressionValue() { - - Branch b = getControlDependentBranch(); - return getBranchExpressionValue(b); - } - - /** - *

- * getBranchExpressionValue - *

- * - * @param b a {@link org.evomaster.client.java.coverage.branch.Branch} object. - * @return a boolean. - */ - public boolean getBranchExpressionValue(Branch b) { - if (!isDirectlyControlDependentOn(b)) - throw new IllegalArgumentException( - "this method can only be called for branches that this instruction is directly control dependent on."); - - if (b == null) - return true; // root branch special case - - return getControlDependency(b).getBranchExpressionValue(); - } - - /** - * Determines whether this BytecodeInstruction is directly control dependent - * on the given Branch. Meaning within this instruction CDG there is an - * incoming ControlFlowEdge to this instructions BasicBlock holding the - * given Branch as it's branchInstruction. - *

- * If the given Branch is null, this method checks whether the this - * instruction is control dependent on the root branch of it's method. - * - * @param branch a {@link org.evomaster.client.java.coverage.branch.Branch} object. - * @return a boolean. - */ - public boolean isDirectlyControlDependentOn(Branch branch) { - if (branch == null) - return getControlDependentBranchIds().contains(-1); - - for (ControlDependency cd : getControlDependencies()) - if (cd.getBranch().equals(branch)) - return true; - - return false; - } - - /** - *

- * getControlDependency - *

- * - * @param branch a {@link org.evomaster.client.java.coverage.branch.Branch} object. - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlDependency} object. - */ - public ControlDependency getControlDependency(Branch branch) { - if (!isDirectlyControlDependentOn(branch)) - throw new IllegalArgumentException( - "instruction not directly control dependent on given branch"); - - for (ControlDependency cd : getControlDependencies()) - if (cd.getBranch().equals(branch)) - return cd; - - throw new IllegalStateException( - "expect getControlDependencies() to contain a CD for each branch that isDirectlyControlDependentOn() returns true on"); - } - - // /** - // * WARNING: better don't user this method right now TODO - // * - // * Determines whether the CFGVertex is transitively control dependent on - // the - // * given Branch - // * - // * A CFGVertex is transitively control dependent on a given Branch if the - // * Branch and the vertex are in the same method and the vertex is either - // * directly control dependent on the Branch - look at - // * isDirectlyControlDependentOn(Branch) - or the CFGVertex of the control - // * dependent branch of this CFGVertex is transitively control dependent on - // * the given branch. - // * - // */ - // public boolean isTransitivelyControlDependentOn(Branch branch) { - // if (!getClassName().equals(branch.getClassName())) - // return false; - // if (!getMethodName().equals(branch.getMethodName())) - // return false; - // - // // TODO: this method does not take into account, that there might be - // // multiple branches this instruction is control dependent on - // - // BytecodeInstruction vertexHolder = this; - // do { - // if (vertexHolder.isDirectlyControlDependentOn(branch)) - // return true; - // vertexHolder = vertexHolder.getControlDependentBranch() - // .getInstruction(); - // } while (vertexHolder != null); - // - // return false; - // } - - // /** - // * WARNING: better don't user this method right now TODO - // * - // * Determines the number of branches that have to be passed in order to - // pass - // * this CFGVertex - // * - // * Used to determine TestFitness difficulty - // */ - - /** - *

- * getCDGDepth - *

- * - * @return a int. - */ - public int getCDGDepth() { - int min = Integer.MAX_VALUE; - Set dependencies = getControlDependencies(); - if (dependencies.isEmpty()) - min = 1; - for (ControlDependency dependency : dependencies) { - int depth = getCDG().getControlDependenceDepth(dependency); - if (depth < min) - min = depth; - } - return min; - /* - * // TODO: this method does not take into account, that there might be - * // multiple branches this instruction is control dependent on Branch - * current = getControlDependentBranch(); int r = 1; while (current != - * null) { r++; current = - * current.getInstruction().getControlDependentBranch(); } return r; - */ - } - // String methods /** @@ -709,13 +438,7 @@ public String getASMNodeString() { String type = getType(); String opcode = getInstructionType(); - String stack = ""; - if (frame == null) - stack = "null"; - else - for (int i = 0; i < frame.getStackSize(); i++) { - stack += frame.getStack(i) + ","; - } + String stack = "n/a"; if (asmNode instanceof LabelNode) { return "LABEL " + ((LabelNode) asmNode).getLabel().toString(); @@ -778,41 +501,6 @@ else if (asmNode instanceof VarInsnNode) return "Unknown node" + " Type=" + type + ", Opcode=" + opcode; } - /** - *

- * printFrameInformation - *

- */ - public void printFrameInformation() { - System.out.println("Frame STACK:"); - for (int i = 0; i < frame.getStackSize(); i++) { - SourceValue v = (SourceValue) frame.getStack(i); - System.out.print(" " + i + "(" + v.insns.size() + "): "); - for (Object n : v.insns) { - AbstractInsnNode node = (AbstractInsnNode) n; - BytecodeInstruction ins = BytecodeInstructionPool.getInstance(classLoader).getInstruction(className, - methodName, - node); - System.out.print(ins.toString() + ", "); - } - System.out.println(); - } - - System.out.println("Frame LOCALS:"); - for (int i = 1; i < frame.getLocals(); i++) { - SourceValue v = (SourceValue) frame.getLocal(i); - System.out.print(" " + i + "(" + v.insns.size() + "): "); - for (Object n : v.insns) { - AbstractInsnNode node = (AbstractInsnNode) n; - BytecodeInstruction ins = BytecodeInstructionPool.getInstance(classLoader).getInstruction(className, - methodName, - node); - System.out.print(ins.toString() + ", "); - } - System.out.println(); - } - } - // --- Inherited from Object --- /** @@ -841,7 +529,7 @@ public String toString() { *

* Otherwise this method will return null; * - * @return a {@link org.evomaster.client.java.coverage.branch.Branch} object. + * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch} object. */ public Branch toBranch() { @@ -854,340 +542,237 @@ public Branch toBranch() { /** *

- * proceedsOwnConstructorInvocation + * isLastInstructionInMethod *

* * @return a boolean. */ - public boolean proceedsOwnConstructorInvocation() { - - RawControlFlowGraph cfg = getRawCFG(); - for (BytecodeInstruction other : cfg.vertexSet()) - if (other.isConstructorInvocation() - && other.isMethodCallOnSameObject()) - if (getInstructionId() < other.getInstructionId()) - return true; - - return false; + public boolean isLastInstructionInMethod() { + return equals(getRawCFG().getInstructionWithBiggestId()); } /** *

- * isWithinConstructor + * canBeExitPoint *

* * @return a boolean. */ - public boolean isWithinConstructor() { - return getMethodName().startsWith(""); + public boolean canBeExitPoint() { + return canReturnFromMethod() || isLastInstructionInMethod(); } /** - *

- * isLastInstructionInMethod - *

- * - * @return a boolean. + * {@inheritDoc} */ - public boolean isLastInstructionInMethod() { - return equals(getRawCFG().getInstructionWithBiggestId()); + @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; } /** - *

- * canBeExitPoint - *

- * - * @return a boolean. + * {@inheritDoc} */ - public boolean canBeExitPoint() { - return canReturnFromMethod() || isLastInstructionInMethod(); + @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); } - /** - * Returns the RawCFG of the method called by this instruction + // inherited from Object + + /* + * (non-Javadoc) * - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph} object. + * @see java.lang.Comparable#compareTo(java.lang.Object) */ - public RawControlFlowGraph getCalledCFG() { - if (!isMethodCall()) - return null; - - return GraphPool.getInstance(classLoader).getRawCFG(getCalledMethodsClass(), - getCalledMethod()); + @Override + public int compareTo(BytecodeInstruction o) { + return getLineNumber() - o.getLineNumber(); } /** - * Determines whether this instruction calls a method on its own Object - * ('this') *

- * This is done using the getSourceOfMethodInvocationInstruction() method - * and checking if the return of that method loads this using loadsReferenceToThis() + * getASMNode + *

* - * @return a boolean. + * @return a {@link org.objectweb.asm.tree.AbstractInsnNode} object. */ - public boolean isMethodCallOnSameObject() { - BytecodeInstruction srcInstruction = getSourceOfMethodInvocationInstruction(); - if (srcInstruction == null) - return false; - return srcInstruction.loadsReferenceToThis(); + public AbstractInsnNode getASMNode() { + return asmNode; } /** - * Determines whether this instruction calls a method on a field variable *

- * This is done using the getSourceOfMethodInvocationInstruction() method - * and checking if the return of that method is a field use instruction + * getInstructionType + *

* - * @return a boolean. + * @return a {@link java.lang.String} object. */ + public String getInstructionType() { + if (asmNode.getOpcode() >= 0 && asmNode.getOpcode() < Printer.OPCODES.length) + return Printer.OPCODES[asmNode.getOpcode()]; - public boolean isMethodCallOfField() { - if (!this.isMethodCall()) - return false; - if (this.isInvokeStatic()) - return false; - // If the instruction belongs to static initialization block of the - // class, then the method call cannot be done on a fields. - if (this.methodName.contains("")) - return false; - BytecodeInstruction srcInstruction = getSourceOfMethodInvocationInstruction(); - if (srcInstruction == null) - return false; - - //is a field use? But field uses are also "GETSTATIC" - if (srcInstruction.isFieldNodeUse()) { - - //is static? if not, return yes. This control is not necessary in theory, but you never know... - if (srcInstruction.isStaticDefUse()) { - //is static, check if the name of the class that contain the static field is equals to the current class name - //if is equals, return true, otherwise we are in a case where we are calling a field over an external static class - //e.g. System.out - if (srcInstruction.asmNode instanceof FieldInsnNode) { - String classNameField = ((FieldInsnNode) srcInstruction.asmNode).owner; - classNameField = classNameField.replace('/', '.'); - return classNameField.equals(className); - } - } else { - return true; - } - } - return false; + if (isLineNumber()) + return "LINE " + this.getLineNumber(); + return getType(); } /** - * Determines the name of the field variable this method call is invoked on *

- * This is done using the getSourceOfMethodInvocationInstruction() method - * and returning its variable name + * getType + *

* * @return a {@link java.lang.String} object. */ - @Override - public String getFieldMethodCallName() { - BytecodeInstruction srcInstruction = getSourceOfMethodInvocationInstruction(); - if (srcInstruction == null) - return null; - return srcInstruction.getVariableName(); + public String getType() { + // TODO explain + String type = ""; + if (asmNode.getType() >= 0 && asmNode.getType() < Printer.TYPES.length) + type = Printer.TYPES[asmNode.getType()]; + + return type; } /** - * If this is a method call instruction this method will return the - * instruction that loaded the reference of the Object the method is invoked - * onto the stack. *

- * This is done using getSourceOfStackInstruction() - *

- * The reference is found on top of the stack minus the number of the called - * methods argument + * canReturnFromMethod + *

+ * + * @return a boolean. */ - public BytecodeInstruction getSourceOfMethodInvocationInstruction() { - if (!isMethodCall()) - return null; - - // the object on which this method is called is on top of the stack - // minus the number of arguments the called method has - return getSourceOfStackInstruction(getCalledMethodsArgumentCount()); + public boolean canReturnFromMethod() { + return isReturn() || isThrow(); } /** - * If this instruction is an array instruction this method will return the - * BytecodeInstruction that loaded the reference of the array onto the - * stack. - *

- * This is done using getSourceOfStackMethod() *

- * The reference is found on top of the stack minus two + * isReturn + *

+ * + * @return a boolean. */ - public BytecodeInstruction getSourceOfArrayReference() { - if (isArrayStoreInstruction()) { - // when reaching an array store instruction the stack should end in - // ,,. so the array reference is on top of the - // stack minus two - return getSourceOfStackInstruction(2); - - } else if (isArrayLoadInstruction()) { - // when reaching an array store instruction the stack should end in - // ,. so the array reference is on top of the - // stack minus one - return getSourceOfStackInstruction(1); - - } else { - return null; + 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; } - } /** - * This method returns the BytecodeInstruction that loaded the reference - * which is located on top of the stack minus positionFromTop when this - * instruction is executed. - *

- * This is done using the CFGFrame created by the SourceInterpreter() of the - * BytecodeAnalyzer via the CFGGenerator - *

- * Note that this method may return null. This can happen when aliasing is - * involved. For example for method invocations on objects this can happen - * when you first store the object in a local variable and then call a - * method on that variable *

- * see PairTestClass.sourceCallerTest() for an even worse example. - *

- * TODO: this could be done better by following the SourceValues even - * further. + * isThrow + *

+ * + * @return a boolean. */ - public BytecodeInstruction getSourceOfStackInstruction(int positionFromTop) { - if (frame == null) - throw new IllegalStateException( - "expect each BytecodeInstruction to have its CFGFrame set"); - - int stackPos = frame.getStackSize() - (1 + positionFromTop); - if (stackPos < 0) { - StackTraceElement[] se = new Throwable().getStackTrace(); - int t = 0; - System.out.println("Stack trace: "); - while (t < se.length) { - System.out.println(se[t]); - t++; - } - return null; - } - SourceValue source = (SourceValue) frame.getStack(stackPos); - if (source.insns.size() != 1) { - // we don't know for sure, let's be conservative - return null; - } - Object sourceIns = source.insns.iterator().next(); - AbstractInsnNode sourceInstruction = (AbstractInsnNode) sourceIns; - BytecodeInstruction src = BytecodeInstructionPool.getInstance(classLoader).getInstruction(className, - methodName, - sourceInstruction); - return src; + public boolean isThrow() { + // TODO: Need to check if this is a caught exception? + return asmNode.getOpcode() == Opcodes.ATHROW; } /** *

- * isCallToPublicMethod + * isJump *

* * @return a boolean. */ - public boolean isCallToPublicMethod() { - if (!isMethodCall()) - return false; - - if (getCalledCFG() == null) { - // TODO not sure if I am supposed to throw an Exception at this - // point - return false; - } - - return getCalledCFG().isPublicMethod(); + public boolean isJump() { + return (asmNode instanceof JumpInsnNode); } /** *

- * isCallToStaticMethod + * isGoto *

* * @return a boolean. */ - public boolean isCallToStaticMethod() { - if (!isMethodCall()) - return false; - - if (getCalledCFG() == null) { - // TODO not sure if I am supposed to throw an Exception at this - // point - return false; + public boolean isGoto() { + if (asmNode instanceof JumpInsnNode) { + return (asmNode.getOpcode() == Opcodes.GOTO); } - - return getCalledCFG().isStaticMethod(); + return false; } /** *

- * canBeInstrumented + * isBranch *

* * @return a boolean. */ - public boolean canBeInstrumented() { - // System.out.println("i cant be instrumented "+toString()); - return !isWithinConstructor() || !proceedsOwnConstructorInvocation(); + public boolean isBranch() { + return (isJump() && !isGoto()); } /** - * {@inheritDoc} + * Determines if this instruction is a line number instruction + *

+ * More precisely this method checks if the underlying asmNode is a + * LineNumberNode + * + * @return a boolean. */ - @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; + public boolean isLineNumber() { + return (asmNode instanceof LineNumberNode); } /** - * {@inheritDoc} + *

+ * getASMLineNumber + *

+ * + * @return a int. */ - @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); - } + public int getASMLineNumber() { + if (!isLineNumber()) + return -1; - // inherited from Object + return ((LineNumberNode) asmNode).line; + } - /* - * (non-Javadoc) + /** + *

+ * isLabel + *

* - * @see java.lang.Comparable#compareTo(java.lang.Object) + * @return a boolean. */ - @Override - public int compareTo(BytecodeInstruction o) { - return getLineNumber() - o.getLineNumber(); + 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 index aaaf94d59d..939784decf 100755 --- 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 @@ -1,21 +1,6 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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; @@ -32,7 +17,6 @@ * BytecodeInstructionPool class. *

* - * @author Andre Mis */ public class BytecodeInstructionPool { @@ -56,8 +40,6 @@ public static BytecodeInstructionPool getInstance(ClassLoader classLoader) { // BytecodeInstructions private final Map>> instructionMap = new LinkedHashMap<>(); - private final List knownMethodNodes = new ArrayList<>(); - // fill the pool /** @@ -78,8 +60,6 @@ public static BytecodeInstructionPool getInstance(ClassLoader classLoader) { */ public List registerMethodNode(MethodNode node, String className, String methodName) { - registerMethodNode(node); - int lastLineNumber = -1; int bytecodeOffset = 0; @@ -103,7 +83,7 @@ else if (lastLineNumber != -1) bytecodeOffset += getBytecodeIncrement(instructionNode); if (!instruction.isLabel() && !instruction.isLineNumber() - && !instruction.isFrame()) { + && !(instruction.getASMNode() instanceof FrameNode)) { bytecodeOffset++; } @@ -208,14 +188,6 @@ private int getBytecodeIncrement(AbstractInsnNode instructionNode) { return 0; } - private void registerMethodNode(MethodNode node) { - for (MethodNode mn : knownMethodNodes) - if (mn == node) - SimpleLogger.debug("CFGGenerator.analyze() apparently got called for the same MethodNode twice"); - - knownMethodNodes.add(node); - } - /** *

* registerInstruction @@ -253,7 +225,7 @@ public void registerInstruction(BytecodeInstruction instruction) { } } - if (instruction.isActualBranch()) { + if (instruction.isBranch()) { BranchPool.getInstance(classLoader).registerAsBranch(instruction); } } @@ -276,7 +248,7 @@ public BytecodeInstruction getInstruction(String className, String methodName, BytecodeInstruction r = getInstruction(className, methodName, instructionId); - assert r == null || (r.sanityCheckAbstractInsnNode(asmNode)); + assert r == null || (asmNode != null && asmNode.equals(r.getASMNode())); return r; } @@ -354,41 +326,6 @@ public BytecodeInstruction getInstruction(String className, String methodName, return null; } - /** - *

- * knownClasses - *

- * - * @return a {@link java.util.Set} object. - */ - public Set knownClasses() { - return new LinkedHashSet<>(instructionMap.keySet()); - } - - /** - *

- * knownMethods - *

- * - * @param className a {@link java.lang.String} object. - * @return a {@link java.util.Set} object. - */ - public Set knownMethods(String className) { - Set r = new LinkedHashSet<>(); - - if (instructionMap.get(className) != null) - r.addAll(instructionMap.get(className).keySet()); - - return r; - } - - public boolean hasMethod(String className, String methodName) { - if (instructionMap.get(className) != null) - return instructionMap.get(className).containsKey(methodName); - - return false; - } - /** *

* getInstructionsIn @@ -421,115 +358,6 @@ public List getInstructionsIn(String className) { return r; } - public List getAllInstructions() { - List r = new ArrayList<>(); - for (String className : instructionMap.keySet()) { - Map> methodMap = instructionMap.get(className); - for (List methodInstructions : methodMap.values()) { - r.addAll(methodInstructions); - } - } - - return r; - } - - /** - *

- * logInstructionsIn - *

- * - * @param className a {@link java.lang.String} object. - * @param methodName a {@link java.lang.String} object. - */ - public void logInstructionsIn(String className, String methodName) { - - SimpleLogger.debug("Printing instructions in " + className + "." + methodName + ":"); - - List instructions = getInstructionsIn(className, methodName); - if (instructions == null) { - SimpleLogger.debug("..unknown method"); - } else { - for (BytecodeInstruction instruction : instructions) { - SimpleLogger.debug("\t" + instruction); - } - } - - } - - /** - *

- * createFakeInstruction - *

- * - * @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.BytecodeInstruction} object. - */ - public BytecodeInstruction createFakeInstruction(String className, String methodName) { - - AbstractInsnNode fakeNode = new InsnNode(Opcodes.NOP); - - int instructionId = getInstructionsIn(className, methodName).size(); - - BytecodeInstruction instruction = new BytecodeInstruction(classLoader, className, - methodName, instructionId, -1, fakeNode); - - registerInstruction(instruction); - - return instruction; - - } - - /** - *

- * clear - *

- */ - public void clear() { - instructionMap.clear(); - knownMethodNodes.clear(); - } - - public static void clearAll() { - BytecodeInstructionPool.instanceMap.clear(); - } - - /** - *

- * clear - *

- * - * @param className a {@link java.lang.String} object. - */ - public void clear(String className) { - instructionMap.remove(className); - } - - public static void clearAll(String className) { - for (BytecodeInstructionPool pool : instanceMap.values()) { - pool.clear(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 (instructionMap.containsKey(className)) - instructionMap.get(className).remove(methodName); - } - - public static void clearAll(String className, String methodName) { - for (BytecodeInstructionPool pool : instanceMap.values()) { - pool.clear(className, methodName); - } - } - /** *

* forgetInstruction @@ -547,37 +375,5 @@ public boolean forgetInstruction(BytecodeInstruction ins) { return instructionMap.get(ins.getClassName()).get(ins.getMethodName()).remove(ins); } - public int getFirstLineNumberOfMethod(String className, String methodName) { - if (instructionMap.get(className) == null) - throw new IllegalArgumentException("unknown class " + className); - if (instructionMap.get(className).get(methodName) == null) - throw new IllegalArgumentException("unknown method " + methodName - + " in class " + className); - if (instructionMap.get(className).get(methodName).isEmpty()) - throw new IllegalArgumentException("no instructions in method " + methodName - + " in class " + className); - - int r = Integer.MAX_VALUE; - for (BytecodeInstruction ins : instructionMap.get(className).get(methodName)) { - if (ins.getLineNumber() < r) - r = ins.getLineNumber(); - } - return r; - } - - public BytecodeInstruction getFirstInstructionAtLineNumber(String className, String methodName, int lineNumber) { - // TODO - if (instructionMap.get(className) == null) - return null; - if (instructionMap.get(className).get(methodName) == null) - return null; - if (instructionMap.get(className).get(methodName).isEmpty()) - return null; - - for (BytecodeInstruction ins : instructionMap.get(className).get(methodName)) { - if (ins.getLineNumber() == lineNumber) - return ins; - } - return null; - } } + diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGFrame.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGFrame.java deleted file mode 100755 index c46cdf5cf4..0000000000 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGFrame.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . - */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; - -import org.objectweb.asm.tree.analysis.Frame; - -import java.util.HashMap; -import java.util.Map; - -public class CFGFrame extends Frame { - Map successors = new HashMap<>(); - - /** - *

Constructor for CFGFrame.

- * - * @param nLocals a int. - * @param nStack a int. - */ - public CFGFrame(int nLocals, int nStack) { - super(nLocals, nStack); - } - - /** - *

Constructor for CFGFrame.

- * - * @param src a {@link org.objectweb.asm.tree.analysis.Frame} object. - */ - public CFGFrame(Frame src) { - super(src); - } - -} 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 index 56038a6ff9..b541ba60e7 100755 --- 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 @@ -1,21 +1,6 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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; @@ -50,7 +35,6 @@ * It should contain a Vertex for each BytecodeInstruction inside the specified * method and an edge for every possible transition between these instructions * - * @author Andre Mis */ public class CFGGenerator { @@ -145,19 +129,14 @@ private void registerMethodNode(MethodNode currentMethod, String className, * objects. * @param isExceptionEdge a boolean. */ - public void registerControlFlowEdge(int src, int dst, Frame[] frames, + 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"); - CFGFrame srcFrame = (CFGFrame) frames[src]; - Frame dstFrame = frames[dst]; - - if (srcFrame == null) - throw new IllegalArgumentException( - "expect given frames to know srcFrame for " + src); + Frame dstFrame = frames[dst]; if (dstFrame == null) { // documentation of getFrames() tells us the following: @@ -175,8 +154,6 @@ public void registerControlFlowEdge(int src, int dst, Frame[] frames, return; } - srcFrame.successors.put(dst, (CFGFrame) dstFrame); - AbstractInsnNode srcNode = currentMethod.instructions.get(src); AbstractInsnNode dstNode = currentMethod.instructions.get(dst); @@ -190,8 +167,6 @@ public void registerControlFlowEdge(int src, int dst, Frame[] frames, dst, dstNode); - srcInstruction.setCFGFrame(srcFrame); - if (dstInstruction == null) throw new IllegalStateException( "expect BytecodeInstructionPool to know the instructions in the method of this edge"); @@ -209,9 +184,6 @@ public void registerControlFlowEdge(int src, int dst, Frame[] frames, * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph} object. */ public ActualControlFlowGraph computeActualCFG() { - BytecodeInstructionPool.getInstance(classLoader).logInstructionsIn(className, - methodName); - return new ActualControlFlowGraph(rawGraph); } @@ -224,7 +196,7 @@ public ActualControlFlowGraph computeActualCFG() { * * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph} object. */ - protected RawControlFlowGraph getRawGraph() { + public RawControlFlowGraph getRawGraph() { return rawGraph; } 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 index 615b3fab1e..a6d567f048 100755 --- 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 @@ -1,32 +1,14 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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.io.Serializable; import java.util.Objects; -public class ControlDependency implements Serializable, Comparable { - - private static final long serialVersionUID = 6288839964561655730L; +public class ControlDependency { private final Branch branch; private final boolean branchExpressionValue; @@ -64,34 +46,6 @@ public boolean getBranchExpressionValue() { return branchExpressionValue; } -// @Override -// public int hashCode() { -// final int prime = 31; -// int result = 1; -// result = prime * result + ((branch == null) ? 0 : branch.hashCode()); -// result = prime * result + (branchExpressionValue ? 1231 : 1237); -// return result; -// } -// -// @Override -// public boolean equals(Object obj) { -// if (this == obj) -// return true; -// if (obj == null) -// return false; -// if (getClass() != obj.getClass()) -// return false; -// ControlDependency other = (ControlDependency) obj; -// if (branch == null) { -// if (other.branch != null) -// return false; -// } else if (!branch.equals(other.branch)) -// return false; -// if (branchExpressionValue != other.branchExpressionValue) -// return false; -// return true; -// } - /** * {@inheritDoc} */ @@ -100,12 +54,10 @@ public String toString() { String r = "CD " + branch; - if (!branch.isSwitchCaseBranch()) { - if (branchExpressionValue) - r += " - TRUE"; - else - r += " - FALSE"; - } + if (branchExpressionValue) + r += " - TRUE"; + else + r += " - FALSE"; return r; } @@ -121,22 +73,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(branch, branchExpressionValue); } - @Override - public int compareTo(ControlDependency o) { - int x = branch.compareTo(o.branch); - if (x != 0) - return x; - - if (branchExpressionValue == o.branchExpressionValue) { - return 0; - } else if (branchExpressionValue) { - return 1; - } else { - return -1; - } - } } 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 index 3c06a60c99..beaa8b3412 100755 --- 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 @@ -1,21 +1,6 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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; @@ -24,8 +9,6 @@ public class ControlFlowEdge extends DefaultEdge { - private static final long serialVersionUID = -5009449930477928101L; - private ControlDependency cd; private boolean isExceptionEdge; @@ -91,7 +74,7 @@ public boolean hasControlDependency() { /** *

getBranchInstruction

* - * @return a {@link org.evomaster.client.java.coverage.branch.Branch} object. + * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch} object. */ public Branch getBranchInstruction() { if (cd == null) @@ -121,34 +104,6 @@ public boolean getBranchExpressionValue() { return true; } -// @Override -// public int hashCode() { -// final int prime = 31; -// int result = 1; -// result = prime * result + ((cd == null) ? 0 : cd.hashCode()); -// result = prime * result + (isExceptionEdge ? 1231 : 1237); -// return result; -// } -// -// @Override -// public boolean equals(Object obj) { -// if (this == obj) -// return true; -// if (obj == null) -// return false; -// if (getClass() != obj.getClass()) -// return false; -// ControlFlowEdge other = (ControlFlowEdge) obj; -// if (cd == null) { -// if (other.cd != null) -// return false; -// } else if (!cd.equals(other.cd)) -// return false; -// if (isExceptionEdge != other.isExceptionEdge) -// return false; -// return true; -// } - /** * {@inheritDoc} */ 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 index 66fd5e5f28..07ac2deb19 100755 --- 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 @@ -1,28 +1,12 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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.EvoSuiteGraph; +import org.evomaster.client.java.instrumentation.dynamosa.graphs.EvoMasterGraph; import org.jgrapht.graph.DefaultDirectedGraph; import org.objectweb.asm.Opcodes; -import org.evomaster.client.java.utils.SimpleLogger; import java.util.HashSet; import java.util.LinkedList; @@ -30,31 +14,13 @@ import java.util.Set; -/** - * Abstract base class for both forms of CFGs inside EvoSuite - *

- * One implementation of this is cfg.RawControlFlowGraph, which is also known as - * the complete CFG The other implementation of this is - * cfg.ActualControlFlowGraph which is also known as the minimal CFG Look at the - * respective classes for more detailed information - *

- * The CFGs can be accessed via the GraphPool which holds for each CUT and each - * of their methods a complete and a minimal CFG - *

- * CFGs are created by the CFGGenerator during the analysis of the CUTs' - * byteCode performed by the BytecodeAnalyzer - * - * @author Gordon Fraser, Andre Mis - */ public abstract class ControlFlowGraph extends - EvoSuiteGraph { + EvoMasterGraph { protected String className; protected String methodName; protected int access; - private int diameter = -1; - /** * Creates a fresh and empty CFG for the given class and method * @@ -151,58 +117,6 @@ public boolean leadsToNode(ControlFlowEdge e, V b) { */ public abstract boolean containsInstruction(BytecodeInstruction instruction); - /** - * Computes the diameter of this CFG and the mutation distances - *

- * Since both takes some time this is not automatically done on each CFG - *

- * GraphPool will automatically call this immediately after the - * instantiation of an ActualControlFlowGraph, but not after the creation of - * a RawControlFlowGraph - */ - public void finalise() { - computeDiameter(); - // TODO: call this! - // and sanity check with a flag whenever a call - // to this method is assumed to have been made - } - - /** - * Returns the Diameter of this CFG - *

- * If the diameter of this graph was not computed previously it is computed - * first - * - * @return a int. - */ - public int getDiameter() { - if (diameter == -1) { - SimpleLogger.debug("diameter not computed yet. calling computeDiameter() first!"); - computeDiameter(); - } - - return diameter; - } - - public int getCyclomaticComplexity() { - // E = the number of edges of the graph. - // N = the number of nodes of the graph. - // M = E − N + 2 - int E = this.edgeCount(); - int N = this.vertexCount(); - return E - N + 2; - } - - /** - *

computeDiameter

- */ - protected void computeDiameter() { - // The diameter is just an upper bound for the approach level - // Let's try to use something that's easier to compute than - // FLoydWarshall - diameter = this.edgeCount(); - } - /** *

determineEntryPoint

* @@ -221,9 +135,6 @@ public V determineEntryPoint() { // there was a back loop to the first instruction within this CFG, so no // candidate - // TODO for now return null and handle in super class - // RawControlFlowGraph separately by overwriting this method - // can also happen in empty methods return null; } @@ -247,7 +158,7 @@ public String getMethodName() { } /** - *

getMethodAccess

+ *

Getter for the field access.

* * @return a int. */ @@ -255,24 +166,6 @@ public int getMethodAccess() { return access; } - /** - *

isPublicMethod

- * - * @return a boolean. - */ - public boolean isPublicMethod() { - return (access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC; - } - - /** - *

isStaticMethod

- * - * @return a boolean. - */ - public boolean isStaticMethod() { - return (access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC; - } - /** * {@inheritDoc} */ 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 index 49d2ace11b..f7ba45042a 100755 --- 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 @@ -1,28 +1,11 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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 { - private static final long serialVersionUID = -4279595207017734232L; - /** *

Constructor for EntryBlock.

* 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 index 3c3bc08e73..8e9f34b1d3 100755 --- 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 @@ -1,28 +1,11 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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 { - private static final long serialVersionUID = 970110985248711972L; - /** *

Constructor for ExitBlock.

* 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 index 6acb78614f..3798ce58dc 100755 --- 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 @@ -1,25 +1,9 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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.utils.SimpleLogger; @@ -35,7 +19,6 @@ * nodes. From each such instruction there is an edge to each possible * instruction the control flow can reach immediately after that instruction. * - * @author Andre Mis */ public class RawControlFlowGraph extends ControlFlowGraph { @@ -86,16 +69,6 @@ public BytecodeInstruction getInstruction(int instructionId) { .orElse(null); } - // @Override - // public BytecodeInstruction getBranch(int branchId) { - // for (BytecodeInstruction v : vertexSet()) { - // if (v.isBranch() && v.getControlDependentBranchId() == branchId) { - // return v; - // } - // } - // return null; - // } - /** *

* addEdge @@ -112,7 +85,7 @@ protected ControlFlowEdge addEdge(BytecodeInstruction src, 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 addBranchEdge(src, target, isExceptionEdge); } return addUnlabeledEdge(src, target, isExceptionEdge); @@ -277,6 +250,36 @@ public Set determineExitPoints() { } + /** + *

+ * 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 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 index ca6bc5a389..9ec1297d34 100755 --- 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 @@ -1,26 +1,10 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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 org.objectweb.asm.tree.LabelNode; import java.io.Serializable; @@ -28,58 +12,35 @@ * An object of this class corresponds to a Branch inside the class under test. * *

- * Branches are created by the {@code CFGMethodAdapter} via the {@code BranchPool}. Each Branch + * 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()} - which will be called normal branches - or it - * can be associated with a case: of a switch statement as defined in - * {@code BytecodeInstruction.isSwitch()} - which will be called switch case branches. + * {@code BytecodeInstruction.isBranch()}. * Only {@code BytecodeInstructions} satisfying {@code BytecodeInstruction.isActualbranch()} are * expected to be associated with a {@code Branch} object. * - *

- * For SWITCH statements each case : block corresponds to a {@code Branch} that can - * be created by constructing a {@code Branch} with the SWITCH statement and the - * as the targetCaseValue. The default: case of switch statement can also be - * modeled this way - it has the {@code targetCaseValue} set to {@code null}. - * - * @author Andre Mis */ -public class Branch implements Serializable, Comparable { +public class Branch implements Serializable { private static final long serialVersionUID = -4732587925060748263L; private final int actualBranchId; - private boolean isSwitch = false; - - // for switch branches this value indicates to which case of the switch this - // branch belongs. if this value is null and this is in fact a switch this - // means this branch is the default: case of that switch - private Integer targetCaseValue = null; - private final BytecodeInstruction instruction; - /** - * Keep track of branches that were introduced as part of TT - */ - private boolean isInstrumented = false; - /** * Canonical identifiers matching the descriptive ids used in {@code ObjectiveNaming}. */ private String thenObjectiveId; private String elseObjectiveId; - private Integer branchOrdinalInLine; /** - * Constructor for usual jump instruction Branches, that are not SWITCH - * instructions. + * Constructor for usual jump instruction Branches. * - * @param branchInstruction a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. + * @param branchInstruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. * @param actualBranchId a int. */ public Branch(BytecodeInstruction branchInstruction, int actualBranchId) { @@ -94,34 +55,6 @@ public Branch(BytecodeInstruction branchInstruction, int actualBranchId) { "expect branch to have actualBranchId set to positive value"); } - /** - * Constructor for switch case branches - * - * @param switchInstruction a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. - * @param targetCaseValue a {@link java.lang.Integer} object. - * @param targetLabel a {@link org.objectweb.asm.tree.LabelNode} object. - * @param actualBranchId a int. - */ - public Branch(BytecodeInstruction switchInstruction, Integer targetCaseValue, - LabelNode targetLabel, int actualBranchId) { - if (!switchInstruction.isSwitch()) - throw new IllegalArgumentException("switch instruction expected"); - if (targetLabel == null) - throw new IllegalArgumentException( - "expect targetLabel to not be null for case branches"); - - this.instruction = switchInstruction; - this.actualBranchId = actualBranchId; - - // this.targetLabel = targetLabel; - this.targetCaseValue = targetCaseValue; - this.isSwitch = true; - - if (this.actualBranchId < 1) - throw new IllegalStateException( - "expect branch to have actualBranchId set to positive value"); - } - /** *

* Getter for the field actualBranchId. @@ -133,49 +66,12 @@ public int getActualBranchId() { return actualBranchId; } - /** - * Tells whether this branch corresponds to the {@code default} case within a Java - * {@code switch} statement. - * - * @return {@code true} if this branch represents the {@code default} case - */ - public boolean isDefaultCase() { - return isSwitch && targetCaseValue == null; - } - - /** - * Tells whether this branch corresponds to a statement labeled with {@code case} within a Java - * {@code switch} statement. - * - * @return {@code true} if the statement represented by this branch is labeled with {@code case} - * @see Branch#isDefaultCase() - */ - public boolean isActualCase() { - return isSwitch && targetCaseValue != null; - } - - /** - *

- * Getter for the field targetCaseValue. - *

- * - * @return a {@link java.lang.Integer} object. - */ - public Integer getTargetCaseValue() { - // in order to avoid confusion when targetCaseValue is null - if (!isSwitch) - throw new IllegalStateException( - "method only allowed to be called on non-switch-Branches"); - - return targetCaseValue; // null for default case - } - /** *

* Getter for the field instruction. *

* - * @return a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. + * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. */ public BytecodeInstruction getInstruction() { return instruction; @@ -203,17 +99,6 @@ public String getMethodName() { return instruction.getMethodName(); } - /** - *

- * isSwitchCaseBranch - *

- * - * @return a boolean. - */ - public boolean isSwitchCaseBranch() { - return isSwitch; - } - /** * {@inheritDoc} */ @@ -223,9 +108,6 @@ public int hashCode() { int result = 1; result = prime * result + actualBranchId; result = prime * result + ((instruction == null) ? 0 : instruction.hashCode()); - result = prime * result + (isSwitch ? 1231 : 1237); - result = prime * result - + ((targetCaseValue == null) ? 0 : targetCaseValue.hashCode()); return result; } @@ -248,19 +130,7 @@ public boolean equals(Object obj) { return false; } else if (!instruction.equals(other.instruction)) return false; - if (isSwitch != other.isSwitch) - return false; - if (targetCaseValue == null) { - return other.targetCaseValue == null; - } else return targetCaseValue.equals(other.targetCaseValue); - } - - /* (non-Javadoc) - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ - @Override - public int compareTo(Branch other) { - return instruction.getLineNumber() - other.getInstruction().getLineNumber(); + return true; } /** @@ -271,57 +141,27 @@ public String toString() { String r = "I" + instruction.getInstructionId(); r += " Branch " + getActualBranchId(); r += " " + instruction.getInstructionType(); - if (isSwitch) { - r += " L" + instruction.getLineNumber(); - if (targetCaseValue != null) - r += " Case " + targetCaseValue; - else - r += " Default-Case"; - } else r += " L" + instruction.getLineNumber(); - return r; - } - - /** - *

- * isInstrumented - *

- * - * @return a boolean. - */ - public boolean isInstrumented() { - return isInstrumented; - } + if (thenObjectiveId != null || elseObjectiveId != null) { + r += " [" + (thenObjectiveId == null ? "null" : thenObjectiveId) + + ", " + (elseObjectiveId == null ? "null" : elseObjectiveId) + "]"; + } - /** - *

- * setInstrumented - *

- * - * @param isInstrumented a boolean. - */ - public void setInstrumented(boolean isInstrumented) { - this.isInstrumented = isInstrumented; + return r; } /** * Store the descriptive identifiers associated with this branch. * - * @param ordinalInLine index of this branch on its source line (0-based) - * @param thenId descriptive id for the "true" outcome - * @param elseId descriptive id for the "false" outcome + * @param thenId descriptive id for the "true" outcome + * @param elseId descriptive id for the "false" outcome */ - public void setObjectiveIds(int ordinalInLine, String thenId, String elseId) { - this.branchOrdinalInLine = ordinalInLine; + public void setObjectiveIds(String thenId, String elseId) { this.thenObjectiveId = thenId; this.elseObjectiveId = elseId; } - public Integer getBranchOrdinalInLine() { - return branchOrdinalInLine; - } - public String getThenObjectiveId() { return thenObjectiveId; } @@ -330,13 +170,4 @@ public String getElseObjectiveId() { return elseObjectiveId; } - private boolean ignoreFalse = false; - - public boolean ignoreFalseBranch() { - return ignoreFalse; - } - - public void setIgnoreFalse(boolean value) { - ignoreFalse = value; - } } 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 index d26694b1b3..1cbb5ef5af 100755 --- 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 @@ -1,21 +1,6 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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; @@ -23,11 +8,8 @@ import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming; import org.evomaster.client.java.utils.SimpleLogger; -import java.util.*; - -// TODO: root branches should not be special cases -// every root branch should be a branch just -// like every other branch with it's own branchId and all +import java.util.HashMap; +import java.util.Map; /** * This class is supposed to hold all the available information concerning @@ -40,26 +22,12 @@ * of this class which in turn properly registers the instruction within this * pool. *

- * There are two kinds of Branch objects: normal branches and switch case - * branches. For more details about the difference between these two look at the - * Branch class. * - * @author Andre Mis */ public class BranchPool { - // maps className -> method inside that class -> list of branches inside - // that method - private final Map>> branchMap = new HashMap<>(); - - // set of all known methods without a Branch - private final Map> branchlessMethods = new HashMap<>(); - - // maps the branchIDs assigned by this pool to their respective Branches - private final Map branchIdMap = new HashMap<>(); - // maps all known branch instructions to their branchId - private final Map registeredNormalBranches = new HashMap<>(); + private final Map registeredNormalBranches = new HashMap<>(); // number of known Branches - used for actualBranchIds private int branchCounter = 0; @@ -84,10 +52,10 @@ public static BranchPool getInstance(ClassLoader classLoader) { * that corresponds to a Branch in the class under test as defined by * BytecodeInstruction.isActualBranch(). * - * @param instruction a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. + * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. */ public void registerAsBranch(BytecodeInstruction instruction) { - if (!(instruction.isActualBranch())) + if (!(instruction.isBranch())) throw new IllegalArgumentException("CFGVertex of a branch expected"); if (isKnownAsBranch(instruction)) return; @@ -104,7 +72,7 @@ private void registerInstruction(BytecodeInstruction v) { if (!v.isBranch()) throw new IllegalArgumentException("expect given instruction to be a normal branch"); - registerNormalBranchInstruction(v); + registerNormalBranchInstruction(v); } private void registerNormalBranchInstruction(BytecodeInstruction v) { @@ -116,13 +84,9 @@ private void registerNormalBranchInstruction(BytecodeInstruction v) { "instruction already registered as a normal branch"); branchCounter++; - registeredNormalBranches.put(v, branchCounter); - Branch b = new Branch(v, branchCounter); attachObjectiveIds(b); - addBranchToMap(b); - branchIdMap.put(branchCounter, b); - + registeredNormalBranches.put(v, b); SimpleLogger.info("Branch " + branchCounter + " at line " + v.getLineNumber()); } @@ -133,12 +97,12 @@ private void attachObjectiveIds(Branch branch) { 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); @@ -150,7 +114,7 @@ private void attachObjectiveIds(Branch branch) { 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(ordinal, thenId, elseId); + branch.setObjectiveIds(thenId, elseId); } catch (Exception e) { SimpleLogger.warn("Failed to attach objective ids to branch " + branch + ": " + e.getMessage()); } @@ -163,20 +127,6 @@ private int nextOrdinalForLine(String className, String methodName, int lineNumb return ordinal; } - private void addBranchToMap(Branch b) { - - SimpleLogger.info("Adding to map the branch " + b.toString()); - - String className = b.getClassName(); - String methodName = b.getMethodName(); - - if (!branchMap.containsKey(className)) - branchMap.put(className, new HashMap<>()); - if (!branchMap.get(className).containsKey(methodName)) - branchMap.get(className).put(methodName, new ArrayList<>()); - branchMap.get(className).get(methodName).add(b); - } - // retrieve information from the pool /** @@ -186,44 +136,11 @@ private void addBranchToMap(Branch b) { * Returns true if the given BytecodeInstruction previously passed a call to * registerAsBranch(instruction), false otherwise * - * @param instruction a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. + * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. * @return a boolean. */ public boolean isKnownAsBranch(BytecodeInstruction instruction) { - return isKnownAsNormalBranchInstruction(instruction); - } - - /** - *

- * isKnownAsNormalBranchInstruction - *

- * - * @param ins a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. - * @return a boolean. - */ - public boolean isKnownAsNormalBranchInstruction(BytecodeInstruction ins) { - - return registeredNormalBranches.containsKey(ins); - } - - /** - *

- * getActualBranchIdForNormalBranchInstruction - *

- * - * @param ins a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. - * @return a int. - */ - public int getActualBranchIdForNormalBranchInstruction(BytecodeInstruction ins) { - if (!isKnownAsNormalBranchInstruction(ins)) - throw new IllegalArgumentException( - "instruction not registered as a normal branch"); - - if (registeredNormalBranches.containsKey(ins)) - return registeredNormalBranches.get(ins); - - throw new IllegalStateException( - "expect registeredNormalBranches to contain a key for each known normal branch instruction"); + return registeredNormalBranches.containsKey(instruction); } /** @@ -231,300 +148,21 @@ public int getActualBranchIdForNormalBranchInstruction(BytecodeInstruction ins) * getBranchForInstruction *

* - * @param instruction a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. - * @return a {@link org.evosuite.coverage.branch.Branch} object. + * @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 (!isKnownAsNormalBranchInstruction(instruction)) + if (!isKnownAsBranch(instruction)) throw new IllegalArgumentException( "expect given instruction to be known as a normal branch"); - return getBranch(registeredNormalBranches.get(instruction)); - } - - /** - * Returns the number of known Branches for a given methodName in a given - * class. - * - * @param className a {@link java.lang.String} object. - * @param methodName a {@link java.lang.String} object. - * @return The number of currently known Branches inside the given method - */ - public int getBranchCountForMethod(String className, String methodName) { - if (branchMap.get(className) == null) - return 0; - if (branchMap.get(className).get(methodName) == null) - return 0; - - return branchMap.get(className).get(methodName).size(); - } - - public int getNonArtificialBranchCountForMethod(String className, - String methodName) { - if (branchMap.get(className) == null) - return 0; - if (branchMap.get(className).get(methodName) == null) - return 0; - - int num = 0; - for (Branch b : branchMap.get(className).get(methodName)) { - if (!b.isInstrumented()) - num++; - } - - return num; - } - - /** - * Returns the number of known Branches for a given class - * - * @param className a {@link java.lang.String} object. - * @return The number of currently known Branches inside the given class - */ - public int getBranchCountForClass(String className) { - if (branchMap.get(className) == null) - return 0; - int total = 0; - for (String method : branchMap.get(className).keySet()) { - total += branchMap.get(className).get(method).size(); - } - return total; - } - - /** - * Returns the number of known Branches for a given class - * - * @param prefix a {@link java.lang.String} object. - * @return The number of currently known Branches inside the given class - */ - public int getBranchCountForPrefix(String prefix) { - int num = 0; - for (String className : branchMap.keySet()) { - if (className.startsWith(prefix)) { - SimpleLogger.info("Found matching class for branch count: " + className + "/" - + prefix); - for (String method : branchMap.get(className).keySet()) { - num += branchMap.get(className).get(method).size(); - } - } - } - return num; - } - - /** - * Returns the number of known Branches for a given class - * - * @param prefix a {@link java.lang.String} object. - * @return The number of currently known Branches inside the given class - */ - public Set getBranchIdsForPrefix(String prefix) { - Set ids = new LinkedHashSet<>(); - Set sutBranches = new LinkedHashSet<>(); - for (String className : branchMap.keySet()) { - if (className.startsWith(prefix)) { - SimpleLogger.info("Found matching class for branch ids: " + className + "/" - + prefix); - for (String method : branchMap.get(className).keySet()) { - sutBranches.addAll(branchMap.get(className).get(method)); - } - } - } - - for (Integer id : branchIdMap.keySet()) { - if (sutBranches.contains(branchIdMap.get(id))) { - ids.add(id); - } - } - - return ids; - } - - /** - * Returns the number of known Branches for a given class - * - * @param prefix a {@link java.lang.String} object. - * @return The number of currently known Branches inside the given class - */ - public int getBranchCountForMemberClasses(String prefix) { - int num = 0; - for (String className : branchMap.keySet()) { - if (className.equals(prefix) || className.startsWith(prefix + "$")) { - SimpleLogger.info("Found matching class for branch count: " + className + "/" - + prefix); - for (String method : branchMap.get(className).keySet()) { - num += branchMap.get(className).get(method).size(); - } - } - } - return num; - } - - /** - * Returns the number of currently known Branches - * - * @return The number of currently known Branches - */ - public int getBranchCounter() { - return branchCounter; - } - - public int getNumArtificialBranches() { - int num = 0; - for (Branch b : branchIdMap.values()) { - if (b.isInstrumented()) - num++; - } - - return num; - } - - /** - * Returns the Branch object associated with the given branchID - * - * @param branchId The ID of a branch - * @return The branch, or null if it does not exist - */ - public Branch getBranch(int branchId) { - - return branchIdMap.get(branchId); - } - - public Collection getAllBranches() { - return branchIdMap.values(); - } - - /** - * Returns a Set containing all classes for which this pool knows Branches - * for as Strings - * - * @return a {@link java.util.Set} object. - */ - public Set knownClasses() { - Set r = new LinkedHashSet<>(); - r.addAll(branchMap.keySet()); - r.addAll(branchlessMethods.keySet()); - - return r; - } - - /** - * Returns a Set containing all methods in the class represented by the - * given String for which this pool knows Branches for as Strings - * - * @param className a {@link java.lang.String} object. - * @return a {@link java.util.Set} object. - */ - public Set knownMethods(String className) { - Set r = new LinkedHashSet<>(); - Map> methods = branchMap.get(className); - if (methods != null) - r.addAll(methods.keySet()); - - return r; - } - - /** - * Returns a List containing all Branches in the given class and method - *

- * Should no such Branch exist an empty List is returned - * - * @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 retrieveBranchesInMethod(String className, - String methodName) { - List r = new ArrayList<>(); - if (branchMap.get(className) == null) - return r; - List branches = branchMap.get(className).get(methodName); - if (branches != null) - r.addAll(branches); - return r; - } - - /** - * Reset all the data structures used to keep track of the branch - * information - */ - public void reset() { - branchCounter = 0; - branchMap.clear(); - branchlessMethods.clear(); - branchIdMap.clear(); - registeredNormalBranches.clear(); - branchOrdinalCounters.clear(); - } - - /** - *

- * clear - *

- *

- * TODO: One of these two methods should go - */ - public void clear() { - branchCounter = 0; - branchMap.clear(); - branchIdMap.clear(); - branchlessMethods.clear(); - registeredNormalBranches.clear(); - branchOrdinalCounters.clear(); - } - - /** - *

- * clear - *

- * - * @param className a {@link java.lang.String} object. - */ - public void clear(String className) { - branchMap.remove(className); - branchlessMethods.remove(className); - removeOrdinalCountersForClass(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) { - int numBranches = 0; - - if (branchMap.containsKey(className)) { - if (branchMap.get(className).containsKey(methodName)) - numBranches = branchMap.get(className).get(methodName).size(); - branchMap.get(className).remove(methodName); - } - if (branchlessMethods.containsKey(className)) - branchlessMethods.get(className).remove(methodName); - removeOrdinalCountersForMethod(className, methodName); - SimpleLogger.info("Resetting branchCounter from " + branchCounter + " to " - + (branchCounter - numBranches)); - branchCounter -= numBranches; - } - - private void removeOrdinalCountersForClass(String className) { - if (className == null) { - return; - } - String prefix = className + "#"; - branchOrdinalCounters.keySet().removeIf(key -> key.startsWith(prefix)); - } - - private void removeOrdinalCountersForMethod(String className, String methodName) { - if (className == null || methodName == null) { - return; + Branch branch = registeredNormalBranches.get(instruction); + if (branch == null) { + throw new IllegalStateException("Branch not found for instruction"); } - String prefix = className + "#" + methodName + "#"; - branchOrdinalCounters.keySet().removeIf(key -> key.startsWith(prefix)); + return branch; } } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGClassAdapter.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGClassVisitor.java similarity index 71% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGClassAdapter.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGClassVisitor.java index 6956826860..676da385bf 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGClassAdapter.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGClassVisitor.java @@ -1,19 +1,23 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +/* + * 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 adapter that wraps each method with CFGMethodAdapter to build graphs, + * Minimal class visitor that wraps each method with CFGMethodVisitor to build graphs, * while delegating to the downstream visitor chain unchanged. */ -public class CFGClassAdapter extends ClassVisitor { +public class CFGClassVisitor extends ClassVisitor { private final ClassLoader classLoader; private String classNameWithDots; - public CFGClassAdapter(ClassLoader classLoader, ClassVisitor cv) { + public CFGClassVisitor(ClassLoader classLoader, ClassVisitor cv) { super(Opcodes.ASM9, cv); this.classLoader = classLoader; } @@ -27,7 +31,7 @@ public void visit(int version, int access, String name, String signature, String @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 CFGMethodAdapter( + return new CFGMethodVisitor( classLoader, classNameWithDots, access, diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGMethodAdapter.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGMethodVisitor.java similarity index 69% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGMethodAdapter.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGMethodVisitor.java index 795cd5c43f..21bdf874e8 100755 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGMethodAdapter.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGMethodVisitor.java @@ -1,28 +1,18 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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; +package org.evomaster.client.java.instrumentation.dynamosa.visitor; + +import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.CFGGenerator; import org.evomaster.client.java.instrumentation.AnnotatedMethodNode; 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; @@ -30,15 +20,14 @@ /** * Create a minimized control flow graph for the method and store it. In - * addition, this adapter also adds instrumentation for branch distance + * addition, this visitor also adds instrumentation for branch distance * measurement *

* defUse, concurrency and LCSAJs instrumentation is also added (if the * properties are set). * - * @author Gordon Fraser */ -public class CFGMethodAdapter extends MethodVisitor { +public class CFGMethodVisitor extends MethodVisitor { /** * Methods to skip during CFG analysis. @@ -61,7 +50,7 @@ public class CFGMethodAdapter extends MethodVisitor { /** *

- * Constructor for CFGMethodAdapter. + * Constructor for CFGMethodVisitor. *

* * @param className a {@link java.lang.String} object. @@ -72,7 +61,7 @@ public class CFGMethodAdapter extends MethodVisitor { * @param exceptions an array of {@link java.lang.String} objects. * @param mv a {@link org.objectweb.asm.MethodVisitor} object. */ - public CFGMethodAdapter(ClassLoader classLoader, String className, int access, + public CFGMethodVisitor(ClassLoader classLoader, String className, int access, String name, String desc, String signature, String[] exceptions, MethodVisitor mv) { @@ -105,20 +94,32 @@ public void visitEnd() { return; } SimpleLogger.info("Analyzing method " + methodName + " in class " + className); - BytecodeAnalyzer bytecodeAnalyzer = new BytecodeAnalyzer(); + CFGGenerator cfgGenerator = new CFGGenerator(classLoader, className, methodName, mn); SimpleLogger.info("Generating CFG for method " + methodName); try { - bytecodeAnalyzer.analyze(classLoader, className, methodName, mn); + 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 " - + bytecodeAnalyzer.retrieveCFGGenerator().getRawGraph().vertexSet().size() - + " nodes for " + bytecodeAnalyzer.getFrames().length + + cfgGenerator.getRawGraph().vertexSet().size() + + " nodes for " + analyzer.getFrames().length + " instructions"); // compute Raw and ActualCFG and put both into GraphPool - bytecodeAnalyzer.retrieveCFGGenerator().registerCFGs(); + cfgGenerator.registerCFGs(); SimpleLogger.info("Created CFG for method " + methodName); } catch (AnalyzerException e) { SimpleLogger.error("Analyzer exception while analyzing " + className + "." 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 053b34fa02..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 @@ -5,12 +5,14 @@ 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; /** @@ -113,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; @@ -246,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 49d3a2ca12..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 @@ -23,5 +23,6 @@ public enum Command implements Serializable { BOOT_TIME_INFO, EXTRACT_JVM_DTO, BOOTING_SUT, - SET_DYNAMOSA_CONFIG + 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 index c85695af0b..fe5c54fdb3 100644 --- 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 @@ -5,6 +5,4 @@ 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 ccd5e6e4cf..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); } @@ -295,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/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt index 946bcaaf90..ee428e2bf7 100644 --- a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt +++ b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt @@ -3,6 +3,7 @@ package org.evomaster.core.remote.service import org.evomaster.client.java.controller.api.dto.* import org.evomaster.client.java.controller.api.dto.problem.param.DeriveParamResponseDto import org.evomaster.client.java.controller.api.dto.problem.param.DerivedParamChangeReqDto +import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto import org.evomaster.core.problem.enterprise.param.DerivedParamChangeReq import org.evomaster.core.scheduletask.ScheduleTaskExecutor import org.evomaster.core.sql.DatabaseExecutor @@ -61,4 +62,6 @@ interface RemoteController : DatabaseExecutor, ScheduleTaskExecutor { fun close() fun deriveParams(deriveParams: List) : List + + fun getDynamosaControlDependenceGraphs(): List = emptyList() } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt index e6346c1666..6e3b531539 100644 --- a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt +++ b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt @@ -7,6 +7,7 @@ import org.evomaster.client.java.controller.api.dto.* import org.evomaster.client.java.controller.api.dto.database.operations.* import org.evomaster.client.java.controller.api.dto.problem.param.DeriveParamResponseDto import org.evomaster.client.java.controller.api.dto.problem.param.DerivedParamChangeReqDto +import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto import org.evomaster.client.java.controller.api.dto.problem.rpc.ScheduleTaskInvocationDto import org.evomaster.client.java.controller.api.dto.problem.rpc.ScheduleTaskInvocationsDto import org.evomaster.client.java.controller.api.dto.problem.rpc.ScheduleTaskInvocationsResult @@ -47,6 +48,7 @@ class RemoteControllerImplementation() : RemoteController{ private var extractSqlExecutionInfo = true private var cachedSutInfoDto : SutInfoDto? = null + private val pendingDynamosaCdgs: MutableList = mutableListOf() @Inject private lateinit var config: EMConfig @@ -369,7 +371,15 @@ class RemoteControllerImplementation() : RemoteController{ return null } - return getData(dto) + val result = getData(dto) + + if (result != null && result.dynamosaCdgs.isNotEmpty()) { + synchronized(pendingDynamosaCdgs) { + pendingDynamosaCdgs.addAll(result.dynamosaCdgs) + } + } + + return result } override fun deriveParams(deriveParams: List) : List{ @@ -389,6 +399,17 @@ class RemoteControllerImplementation() : RemoteController{ return dto?.data ?: listOf() } + override fun getDynamosaControlDependenceGraphs(): List { + synchronized(pendingDynamosaCdgs) { + if (pendingDynamosaCdgs.isEmpty()) { + return emptyList() + } + val copy = pendingDynamosaCdgs.toList() + pendingDynamosaCdgs.clear() + return copy + } + } + /** * execute [actionDto] through [ControllerConstants.NEW_ACTION] endpoints of EMController, diff --git a/core/src/main/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraph.kt b/core/src/main/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraph.kt new file mode 100644 index 0000000000..548406d016 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraph.kt @@ -0,0 +1,108 @@ +package org.evomaster.core.search.algorithms + +import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto +import org.evomaster.core.search.service.IdMapper +import java.util.LinkedHashMap +import java.util.LinkedHashSet +import org.slf4j.LoggerFactory + +/** + * Lightweight graph that captures parent→child dependencies between branch targets. + * Nodes are identified by the numeric ids assigned by the instrumentation agent + */ + +private val log = LoggerFactory.getLogger(BranchDependencyGraph::class.java) +class BranchDependencyGraph( + private val idMapper: IdMapper +) { + + private val children: MutableMap> = LinkedHashMap() + private val roots: MutableSet = LinkedHashSet() + private val objectives: MutableSet = LinkedHashSet() + private val parentCounts: MutableMap = LinkedHashMap() + + fun addGraphs(cdgs: List) { + for (dto in cdgs) { + // method objectives are the objectives that are specific to the method + val methodObjectives = registerObjectives(dto) + // register edges between the objectives + registerEdges(dto) + // register roots are the method objectives that are the roots of the graph + registerRoots(dto, methodObjectives) + } + } + + private fun registerObjectives(dto: ControlDependenceGraphDto): Set { + val methodObjectives = LinkedHashSet() + + for (obj in dto.objectives) { + val id = obj.id + + // register the objective + objectives.add(id) + // method objectives are the objectives that are specific to the method + methodObjectives.add(id) + + + // register the parent counts. + // if the objective is not in the parent counts, add it with a count of 0 + // this is used to determine the roots of the graph + // if the parent counts is zero, then the objective is a root + if (!parentCounts.containsKey(id)) { + parentCounts[id] = 0 + } + + // add the descriptive id to the id mapper + if (obj.descriptiveId != null) { + idMapper.addMapping(id, obj.descriptiveId) + } + } + + return methodObjectives + } + + private fun registerEdges(dto: ControlDependenceGraphDto) { + for (edge in dto.edges) { + // register the parent child relationship + registerParentChild(edge.parentObjectiveId, edge.childObjectiveId) + } + } + + private fun registerParentChild(parentId: Int, childId: Int) { + // register the parent child relationship + // If the parent is not in the children map (meaning it is a new parent), add it with an empty set + val set = children.getOrPut(parentId) { LinkedHashSet() } + // add the child to the parent's set of children + set.add(childId) + // increment the parent count for the child + val newCount = (parentCounts[childId] ?: 0) + 1 + // update the parent count for the child + parentCounts[childId] = newCount + // if the parent count is greater than 0, then the child is not a root, so remove it from the roots set + if (newCount > 0) { + roots.remove(childId) + } + } + + private fun registerRoots(dto: ControlDependenceGraphDto, methodObjectives: Set) { + + // potential roots are the root objectives that are specific to the method + val potentialRoots = dto.rootObjectiveIds + + // register the roots, just if they are roots according to the parent counts + for (root in potentialRoots) { + if (isRoot(root)) { + roots.add(root) + } + } + } + + private fun isRoot(id: Int): Boolean = (parentCounts[id] ?: 0) == 0 + + fun getRoots(): Set = LinkedHashSet(roots) + + fun getChildren(parent: Int): Set = children[parent]?.toSet() ?: emptySet() + + fun getAllObjectives(): Set = LinkedHashSet(objectives) +} + diff --git a/core/src/main/kotlin/org/evomaster/core/search/algorithms/DynamosaAlgorithm.kt b/core/src/main/kotlin/org/evomaster/core/search/algorithms/DynamosaAlgorithm.kt index 530c694ca6..7566a3670f 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/algorithms/DynamosaAlgorithm.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/algorithms/DynamosaAlgorithm.kt @@ -4,13 +4,19 @@ import org.evomaster.core.EMConfig import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Individual import org.evomaster.core.search.service.SearchAlgorithm +import org.evomaster.core.search.service.IdMapper import org.evomaster.core.logging.LoggingUtil import java.util.ArrayList +import com.google.inject.Inject +import org.evomaster.core.remote.service.RemoteController /** - * Implementation of DYMOSA + * Implementation of DYNAMOSA from: + * Automated Test Case Generation as a Many-Objective Optimisation Problem with Dynamic Selection of the Targets + * This algorithm is a variant of the MOSA algorithm that uses a dynamic, reduced focus set of targets per generation. + * The dynamic focus set of targets is computed using the branch dependency graph. */ class DynaMosaAlgorithm : SearchAlgorithm() where T : Individual { @@ -22,6 +28,16 @@ class DynaMosaAlgorithm : SearchAlgorithm() where T : Individual { private var population: MutableList = mutableListOf() + // Dynamic subset of objectives to optimize this generation + private var focusTargets: MutableSet = mutableSetOf() + private lateinit var goalsManager: MulticriteriaManager + @Inject + lateinit var idMapper: IdMapper + @Inject + lateinit var remoteController: RemoteController + + private val log = LoggingUtil.getInfoLogger() + override fun getType(): EMConfig.Algorithm { return EMConfig.Algorithm.DYNAMOSA } @@ -30,17 +46,27 @@ class DynaMosaAlgorithm : SearchAlgorithm() where T : Individual { population.clear() + goalsManager = MulticriteriaManager(archive, idMapper) + initPopulation() - sortPopulation() + val newCdgs = remoteController.getDynamosaControlDependenceGraphs() + if (newCdgs.isNotEmpty()) { + goalsManager.addControlDependenceGraphs(newCdgs) + } + + sortPopulation() } override fun searchOnce() { + val newCdgs = remoteController.getDynamosaControlDependenceGraphs() + if (newCdgs.isNotEmpty()) { + goalsManager.addControlDependenceGraphs(newCdgs) + } val n = config.populationSize - //new generation val nextPop: MutableList = mutableListOf() @@ -61,20 +87,33 @@ class DynaMosaAlgorithm : SearchAlgorithm() where T : Individual { nextPop.add(Data(ie as EvaluatedIndividual)) population.addAll(nextPop) + + val moreCdgs = remoteController.getDynamosaControlDependenceGraphs() + if (moreCdgs.isNotEmpty()) { + goalsManager.addControlDependenceGraphs(moreCdgs) + } + sortPopulation() } private fun sortPopulation() { - val notCovered = archive.notCoveredTargets() + // Use manager-computed uncovered goals from complete CFGs + val notCovered = goalsManager.getUncoveredGoals() if(notCovered.isEmpty()){ //Trivial problem: everything covered in first population return } - val fronts = preferenceSorting(notCovered, population) + // DynaMOSA: use MultiCriteriaManager to get dynamic goals (branch roots among uncovered) + goalsManager.refreshGoals() + val dynamic = goalsManager.getCurrentGoals() + focusTargets.clear() + focusTargets.addAll(dynamic) + + val fronts = preferenceSorting(focusTargets, population) var remain: Int = config.populationSize var index = 0 @@ -85,7 +124,7 @@ class DynaMosaAlgorithm : SearchAlgorithm() where T : Individual { while (front!=null && remain > 0 && remain >= front.size && front.isNotEmpty()) { // Assign crowding distance to individuals - subvectorDominance(notCovered, front) + subvectorDominance(focusTargets, front) // Add the individuals of this front for (d in front) { population.add(d) @@ -103,7 +142,7 @@ class DynaMosaAlgorithm : SearchAlgorithm() where T : Individual { // Remain is less than front(index).size, insert only the best one if (remain > 0 && front!=null && front.isNotEmpty()) { - subvectorDominance(notCovered, front) + subvectorDominance(focusTargets, front) var front2 = front.sortedWith(compareBy { - it.crowdingDistance }) .toMutableList() for (k in 0..remain - 1) { @@ -242,7 +281,7 @@ class DynaMosaAlgorithm : SearchAlgorithm() where T : Individual { else if (dominatesX) return -1 - else (dominatesY) + else // (dominatesY) return +1 } @@ -305,4 +344,6 @@ class DynaMosaAlgorithm : SearchAlgorithm() where T : Individual { return ff.calculateCoverage(sampler.sample(), modifiedSpec = null) ?.also { archive.addIfNeeded(it) } } -} \ No newline at end of file +} + + diff --git a/core/src/main/kotlin/org/evomaster/core/search/algorithms/MosaAlgorithm.kt b/core/src/main/kotlin/org/evomaster/core/search/algorithms/MosaAlgorithm.kt index 085a271740..34284c2125 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/algorithms/MosaAlgorithm.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/algorithms/MosaAlgorithm.kt @@ -8,7 +8,6 @@ import org.evomaster.core.logging.LoggingUtil import java.util.ArrayList - /** * Implementation of MOSA from * "Automated Test Case Generation as a Many-Objective Optimisation Problem with Dynamic @@ -244,7 +243,7 @@ class MosaAlgorithm : SearchAlgorithm() where T : Individual { else if (dominatesX) return -1 - else (dominatesY) + else // (dominatesY) return +1 } diff --git a/core/src/main/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManager.kt b/core/src/main/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManager.kt new file mode 100644 index 0000000000..7f80716d37 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManager.kt @@ -0,0 +1,122 @@ +package org.evomaster.core.search.algorithms + +import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming +import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto +import org.evomaster.core.search.service.Archive +import org.evomaster.core.search.service.IdMapper +import org.evomaster.core.logging.LoggingUtil +import java.util.LinkedHashSet + +/** + * MultiCriteriaManager for DynaMOSA + */ +class MulticriteriaManager( + private val archive: Archive<*>, + private val idMapper: IdMapper, +) { + + private val log = LoggingUtil.getInfoLogger() + + private val branchGraph: BranchDependencyGraph = BranchDependencyGraph(idMapper) + + /** + * Current generation focus set of targets (numeric ids from ObjectiveRecorder mapping). + */ + private val currentGoals: LinkedHashSet = LinkedHashSet() + + fun addControlDependenceGraphs(cdgs: List) { + + if (cdgs.isEmpty()) { + return + } + branchGraph.addGraphs(cdgs) + + currentGoals.addAll(branchGraph.getRoots()) + } + + /** + * Refresh the current goals using uncovered targets and the dependency graph(s). + * Seeds with roots ∩ uncovered, then adds uncovered children of covered parents. + */ + fun refreshGoals() { + val uncovered: Set = getUncoveredGoals() + val covered: Set = getCoveredGoals() + currentGoals.clear() + + val branchRoots = branchGraph.getRoots() + val seeded = branchRoots.intersect(uncovered) + val expanded: MutableSet = LinkedHashSet() + + for (p in covered) { + for (c in branchGraph.getChildren(p)) { + if (c in uncovered) { + expanded.add(c) + } + } + } + + val next = LinkedHashSet() + next.addAll(seeded) + next.addAll(expanded) + if (next.isNotEmpty()) { + currentGoals.addAll(next) + } else { + currentGoals.addAll(uncovered) + } + } + + /** + * Snapshot of current goals. + */ + fun getCurrentGoals(): Set = LinkedHashSet(currentGoals) + + /** + * Roots of the branch dependency graph (base roots only). + */ + fun getBranchRoots(): Set = branchGraph.getRoots() + + /** + * (all CFG-derived branch ids) minus (archive-covered branch ids) + */ + fun getUncoveredGoals(): Set { + val allBranchObjectives = branchGraph.getAllObjectives() + if (allBranchObjectives.isEmpty()) return emptySet() + val covered: Set = getCoveredGoals() + return allBranchObjectives.minus(covered) + } + + /** + * Use archive as source of covered targets, then keep only Branch targets + */ + fun getCoveredGoals(): Set { + // Get Covered Targets from the Archive + val covered = archive.coveredTargets() + if (covered.isEmpty()) { + return emptySet() + } + + val result = LinkedHashSet() + val allObjectives = branchGraph.getAllObjectives() + + for (id in covered) { + // Consider only the objectives that are part of the branch dependency graph + + if (allObjectives.contains(id)) { + log.info("Covered target $id is in the branch dependency graph") + val desc = idMapper.getDescriptiveId(id) + log.info("Descriptive ID: $desc") + result.add(id) + } else { + // Sanity check: if it's a branch according to its descriptive id but isn't in the branch dependency graph, warn + val desc = idMapper.getDescriptiveId(id) + if (desc != null && desc.startsWith(ObjectiveNaming.BRANCH)) { + log.warn("Covered target $id ($desc) looks like a Branch but is not in BranchDependencyGraph!") + } + } + } + return result + } + +} + + diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraphTest.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraphTest.kt new file mode 100644 index 0000000000..fed09ed2bf --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraphTest.kt @@ -0,0 +1,111 @@ +package org.evomaster.core.search.algorithms + +import io.mockk.mockk +import io.mockk.verify +import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto +import org.evomaster.core.search.service.IdMapper +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class BranchDependencyGraphTest { + + private lateinit var idMapper: IdMapper + private lateinit var graph: BranchDependencyGraph + + @BeforeEach + fun setUp() { + idMapper = mockk(relaxed = true) + graph = BranchDependencyGraph(idMapper) + } + + @Test + fun test_addGraphsRegistersObjectivesAndRoots() { + val dto = createGraph( + objectives = listOf(1, 2), + rootIds = listOf(1), + edges = listOf(1 to 2) + ) + + graph.addGraphs(listOf(dto)) + + assertEquals(setOf(1, 2), graph.getAllObjectives()) + assertEquals(setOf(1), graph.getRoots()) + verify { idMapper.addMapping(1, "desc_1") } + verify { idMapper.addMapping(2, "desc_2") } + } + + @Test + fun test_getChildrenReturnsRegisteredEdges() { + val dto = createGraph( + objectives = listOf(10, 11, 12), + rootIds = listOf(10), + edges = listOf(10 to 11, 10 to 12) + ) + + graph.addGraphs(listOf(dto)) + + assertEquals(setOf(11, 12), graph.getChildren(10)) + assertTrue(graph.getChildren(999).isEmpty()) + } + + @Test + fun test_getAllObjectivesAggregatesAcrossGraphs() { + val first = createGraph( + objectives = listOf(1, 2), + rootIds = listOf(1), + edges = listOf(1 to 2) + ) + val second = createGraph( + objectives = listOf(20, 21), + rootIds = listOf(20), + edges = listOf(20 to 21) + ) + + graph.addGraphs(listOf(first, second)) + + assertEquals(setOf(1, 2, 20, 21), graph.getAllObjectives()) + assertEquals(setOf(1, 20), graph.getRoots()) + } + + @Test + fun test_childIsNotRootIfItHasParent() { + val dto = createGraph( + objectives = listOf(1, 2), + rootIds = listOf(1, 2), + edges = listOf(1 to 2) + ) + + graph.addGraphs(listOf(dto)) + + assertEquals(setOf(1), graph.getRoots()) + } + + private fun createGraph( + objectives: List, + rootIds: List, + edges: List> + ): ControlDependenceGraphDto { + val dto = ControlDependenceGraphDto() + dto.className = "Clazz" + dto.methodName = "method" + + dto.objectives = objectives.map { id -> + ControlDependenceGraphDto.BranchObjectiveDto().apply { + this.id = id + this.descriptiveId = "desc_$id" + } + } + dto.rootObjectiveIds = rootIds + dto.edges = edges.map { (parent, child) -> + ControlDependenceGraphDto.DependencyEdgeDto().apply { + parentObjectiveId = parent + childObjectiveId = child + } + } + return dto + } +} + + diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/DynaMosaAlgorithmTest.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/DynaMosaAlgorithmTest.kt new file mode 100644 index 0000000000..7accaab56b --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/DynaMosaAlgorithmTest.kt @@ -0,0 +1,99 @@ +package org.evomaster.core.search.algorithms + +import com.google.inject.AbstractModule +import com.google.inject.Injector +import com.google.inject.Key +import com.google.inject.Module +import com.google.inject.TypeLiteral +import com.netflix.governator.guice.LifecycleInjector +import org.evomaster.core.BaseModule +import org.evomaster.core.EMConfig +import org.evomaster.core.search.algorithms.onemax.OneMaxIndividual +import org.evomaster.core.search.algorithms.onemax.OneMaxModule +import org.evomaster.core.search.algorithms.onemax.OneMaxSampler +import org.evomaster.core.search.service.ExecutionPhaseController +import org.evomaster.core.remote.service.RemoteController +import org.evomaster.client.java.controller.api.dto.* +import org.evomaster.client.java.controller.api.dto.problem.param.DeriveParamResponseDto +import org.evomaster.client.java.controller.api.dto.problem.param.DerivedParamChangeReqDto +import org.evomaster.client.java.controller.api.dto.problem.rpc.ScheduleTaskInvocationsDto +import org.evomaster.client.java.controller.api.dto.problem.rpc.ScheduleTaskInvocationsResult +import org.evomaster.client.java.controller.api.dto.database.operations.* +import org.evomaster.client.java.controller.api.dto.ActionDto +import org.evomaster.client.java.controller.api.dto.ActionResponseDto +import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class DynaMosaAlgorithmTest { + + private lateinit var injector: Injector + + @BeforeEach + fun setUp() { + injector = LifecycleInjector.builder() + .withModules(* arrayOf(OneMaxModule(), BaseModule(), DynaMosaTestModule())) + .build().createInjector() + } + + @Test + fun testDynamosaFindsOptimumOnOneMax() { + val dynamosa = injector.getInstance( + Key.get(object : TypeLiteral>() {}) + ) + + val config = injector.getInstance(EMConfig::class.java) + config.maxEvaluations = 10_000 + config.stoppingCriterion = EMConfig.StoppingCriterion.ACTION_EVALUATIONS + + val epc = injector.getInstance(ExecutionPhaseController::class.java) + epc.startSearch() + val solution = dynamosa.search() + epc.finishSearch() + + assertEquals(1, solution.individuals.size) + assertEquals( + OneMaxSampler.DEFAULT_N.toDouble(), + solution.overall.computeFitnessScore(), + 0.001 + ) + } +} + +private class DynaMosaTestModule : AbstractModule() { + override fun configure() { + bind(RemoteController::class.java).toInstance(DummyRemoteController()) + } +} + +private class DummyRemoteController : RemoteController { + override fun getSutInfo(): SutInfoDto? = null + override fun getControllerInfo(): ControllerInfoDto? = null + override fun startSUT(): Boolean = true + override fun stopSUT(): Boolean = true + override fun resetSUT(): Boolean = true + override fun checkConnection() {} + override fun startANewSearch(): Boolean = true + override fun getTestResults( + ids: Set, + ignoreKillSwitch: Boolean, + fullyCovered: Boolean, + descriptiveIds: Boolean + ): TestResultsDto? = null + + override fun executeNewRPCActionAndGetResponse(actionDto: ActionDto): ActionResponseDto? = null + override fun postSearchAction(postSearchActionDto: PostSearchActionDto): Boolean = true + override fun registerNewAction(actionDto: ActionDto): Boolean = true + override fun address(): String = "dummy" + override fun close() {} + override fun deriveParams(deriveParams: List): List = emptyList() + override fun getDynamosaControlDependenceGraphs(): List = emptyList() + + override fun executeDatabaseCommand(dto: DatabaseCommandDto): Boolean = true + override fun executeDatabaseCommandAndGetQueryResults(dto: DatabaseCommandDto): QueryResultDto? = null + override fun executeDatabaseInsertionsAndGetIdMapping(dto: DatabaseCommandDto): InsertionResultsDto? = null + override fun executeMongoDatabaseInsertions(dto: MongoDatabaseCommandDto): MongoInsertionResultsDto? = null + override fun invokeScheduleTasksAndGetResults(dtos: ScheduleTaskInvocationsDto): ScheduleTaskInvocationsResult? = null +} + diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManagerTest.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManagerTest.kt new file mode 100644 index 0000000000..3f39ce03f3 --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManagerTest.kt @@ -0,0 +1,130 @@ +package org.evomaster.core.search.algorithms + +import io.mockk.every +import io.mockk.mockk +import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming +import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto +import org.evomaster.core.search.service.Archive +import org.evomaster.core.search.service.IdMapper +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class MulticriteriaManagerTest { + + private lateinit var archive: Archive<*> + private lateinit var idMapper: IdMapper + private lateinit var manager: MulticriteriaManager + + @BeforeEach + fun setUp() { + archive = mockk(relaxed = true) + idMapper = mockk(relaxed = true) + } + + @Test + fun test_initializationWithGraphs() { + val dto = createSimpleGraphDto(rootId = 1, childId = 2) + val graphs = listOf(dto) + + manager = MulticriteriaManager(archive, idMapper) + manager.addControlDependenceGraphs(graphs) + + val roots = manager.getBranchRoots() + assertTrue(roots.contains(1)) + assertEquals(1, roots.size) + + // Initially, current goals should be the roots + val currentGoals = manager.getCurrentGoals() + assertEquals(setOf(1), currentGoals) + } + + @Test + fun test_refreshGoals_expandsChildrenWhenParentIsCovered() { + + // Graph: 1 -> 2 + val dto = createSimpleGraphDto(rootId = 1, childId = 2) + manager = MulticriteriaManager(archive, idMapper) + manager.addControlDependenceGraphs(listOf(dto)) + + // Mock archive to say 1 is covered + every { archive.coveredTargets() } returns setOf(1) + + // Mock idMapper to return descriptive IDs if needed (though getCoveredGoals logic uses it mainly for logging/sanity) + every { idMapper.getDescriptiveId(1) } returns ObjectiveNaming.BRANCH + "_1" + + manager.refreshGoals() + val goals = manager.getCurrentGoals() + + // Assert + // Since 1 is covered, it should expand to children of 1 (which is 2). + assertEquals(setOf(2), goals) + } + + @Test + fun test_getCoveredGoals_filtersOutTargetsNotInGraph() { + // Arrange + // Graph has only ID 1. + val dto = createSingleNodeGraphDto(id = 1) + manager = MulticriteriaManager(archive, idMapper) + manager.addControlDependenceGraphs(listOf(dto)) + + // Archive reports 1 and 99 (ghost ID) as covered + every { archive.coveredTargets() } returns setOf(1, 99) + every { idMapper.getDescriptiveId(1) } returns ObjectiveNaming.BRANCH + "_1" + every { idMapper.getDescriptiveId(99) } returns ObjectiveNaming.BRANCH + "_99" + + // Act + val covered = manager.getCoveredGoals() + + // Assert + // Should only contain 1, because 99 is not in the BranchDependencyGraph + assertTrue(covered.contains(1)) + assertFalse(covered.contains(99)) + assertEquals(1, covered.size) + } + + // --- Helpers --- + + private fun createSimpleGraphDto(rootId: Int, childId: Int): ControlDependenceGraphDto { + val dto = ControlDependenceGraphDto() + dto.className = "TestClass" + dto.methodName = "testMethod" + + val rootObj = ControlDependenceGraphDto.BranchObjectiveDto() + rootObj.id = rootId + rootObj.descriptiveId = ObjectiveNaming.BRANCH + "_$rootId" + + val childObj = ControlDependenceGraphDto.BranchObjectiveDto() + childObj.id = childId + childObj.descriptiveId = ObjectiveNaming.BRANCH + "_$childId" + + dto.objectives = listOf(rootObj, childObj) + dto.rootObjectiveIds = listOf(rootId) + + val edge = ControlDependenceGraphDto.DependencyEdgeDto() + edge.parentObjectiveId = rootId + edge.childObjectiveId = childId + + dto.edges = listOf(edge) + + return dto + } + + private fun createSingleNodeGraphDto(id: Int): ControlDependenceGraphDto { + val dto = ControlDependenceGraphDto() + dto.className = "TestClass" + dto.methodName = "testMethod" + + val obj = ControlDependenceGraphDto.BranchObjectiveDto() + obj.id = id + obj.descriptiveId = ObjectiveNaming.BRANCH + "_$id" + + dto.objectives = listOf(obj) + dto.rootObjectiveIds = listOf(id) + dto.edges = emptyList() + + return dto + } +} + From 550f4ba4f3490a3b7e04be95ce3f7976ddc89365 Mon Sep 17 00:00:00 2001 From: francastagna Date: Tue, 2 Dec 2025 16:09:30 -0300 Subject: [PATCH 04/21] moved annotated methods --- .../{ => dynamosa}/AnnotatedLabel.java | 25 +++--------------- .../{ => dynamosa}/AnnotatedMethodNode.java | 26 +++---------------- .../graphs/cfg/BytecodeInstructionPool.java | 2 +- .../dynamosa/visitor/CFGMethodVisitor.java | 3 +-- 4 files changed, 9 insertions(+), 47 deletions(-) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{ => dynamosa}/AnnotatedLabel.java (55%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{ => dynamosa}/AnnotatedMethodNode.java (63%) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AnnotatedLabel.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedLabel.java similarity index 55% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AnnotatedLabel.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedLabel.java index 05d593279f..fdfa1cc9cd 100755 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AnnotatedLabel.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedLabel.java @@ -1,33 +1,14 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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; +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 - * such that EvoSuite knows how to deal with - * - * @author fraser */ public class AnnotatedLabel extends Label { diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AnnotatedMethodNode.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedMethodNode.java similarity index 63% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AnnotatedMethodNode.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedMethodNode.java index 9da154dd6a..e04dde9f3d 100755 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AnnotatedMethodNode.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedMethodNode.java @@ -1,24 +1,8 @@ /* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . + * 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; +package org.evomaster.client.java.instrumentation.dynamosa; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; @@ -26,9 +10,7 @@ import org.objectweb.asm.tree.MethodNode; /** - *

AnnotatedMethodNode class.

- * - * @author fraser + * AnnotatedMethodNode wraps ASM's MethodNode to support Dynamosa-specific metadata. */ public class AnnotatedMethodNode extends MethodNode { 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 index 939784decf..2026f747ed 100755 --- 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 @@ -4,8 +4,8 @@ */ 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.evomaster.client.java.instrumentation.AnnotatedLabel; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.*; import org.evomaster.client.java.utils.SimpleLogger; 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 index 21bdf874e8..da6aed4f3c 100755 --- 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 @@ -4,9 +4,8 @@ */ 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.evomaster.client.java.instrumentation.AnnotatedMethodNode; import org.objectweb.asm.*; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.Analyzer; From 1a14ed974819ab35be13dad3527f0939e0365df7 Mon Sep 17 00:00:00 2001 From: francastagna Date: Tue, 2 Dec 2025 16:19:42 -0300 Subject: [PATCH 05/21] updated docs/options.md --- docs/options.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/docs/options.md b/docs/options.md index 9980693f46..a3f1e022c8 100644 --- a/docs/options.md +++ b/docs/options.md @@ -68,7 +68,7 @@ There are 3 types of options: |`addPreDefinedTests`| __Boolean__. Add predefined tests at the end of the search. An example is a test to fetch the schema of RESTful APIs. *Default value*: `true`.| |`addTestComments`| __Boolean__. Add summary comments on each test. *Default value*: `true`.| |`advancedBlackBoxCoverage`| __Boolean__. Apply more advanced coverage criteria for black-box testing. This can result in larger generated test suites. *Default value*: `true`.| -|`algorithm`| __Enum__. The algorithm used to generate test cases. The default depends on whether black-box or white-box testing is done. *Valid values*: `DEFAULT, SMARTS, MIO, RANDOM, WTS, MOSA, RW, StandardGA, MonotonicGA, SteadyStateGA, BreederGA, CellularGA, OnePlusLambdaLambdaGA, MuLambdaEA, MuPlusLambdaEA, LIPS, CRO`. *Default value*: `DEFAULT`.| +|`algorithm`| __Enum__. The algorithm used to generate test cases. The default depends on whether black-box or white-box testing is done. *Valid values*: `DEFAULT, SMARTS, MIO, RANDOM, WTS, MOSA, RW, DYNAMOSA, StandardGA, MonotonicGA, SteadyStateGA, BreederGA, CellularGA, OnePlusLambdaLambdaGA, MuLambdaEA, MuPlusLambdaEA, LIPS`. *Default value*: `DEFAULT`.| |`allowInvalidData`| __Boolean__. When generating data, allow in some cases to use invalid values on purpose. *Default value*: `true`.| |`appendToStatisticsFile`| __Boolean__. Whether should add to an existing statistics file, instead of replacing it. *Default value*: `false`.| |`archiveAfterMutationFile`| __String__. Specify a path to save archive after each mutation during search, only useful for debugging. *DEBUG option*. *Default value*: `archive.csv`.| @@ -83,11 +83,6 @@ There are 3 types of options: |`coveredTargetSortedBy`| __Enum__. Specify a format to organize the covered targets by the search. *Valid values*: `NAME, TEST`. *Default value*: `NAME`.| |`createConfigPathIfMissing`| __Boolean__. If there is no configuration file, create a default template at given configPath location. However this is done only on the 'default' location. If you change 'configPath', no new file will be created. *Default value*: `true`.| |`createTests`| __Boolean__. Specify if test classes should be created as output of the tool. Usually, you would put it to 'false' only when debugging EvoMaster itself. *Default value*: `true`.| -|`croDecompositionThreshold`| __Int__. CRO: Decomposition threshold d_t (min number of collisions before decomposition). *Constraints*: `min=0.0`. *Default value*: `500`.| -|`croInitialKineticEnergy`| __Double__. CRO: Initial kinetic energy assigned to each molecule. *Constraints*: `min=0.0`. *Default value*: `1000.0`.| -|`croKineticEnergyLossRate`| __Double__. CRO: Kinetic energy loss rate k_r (lower bound of retained fraction after on-wall). *Constraints*: `probability 0.0-1.0`. *Default value*: `0.2`.| -|`croMolecularCollisionRate`| __Double__. CRO: Molecular collision rate c_r (probability of binary reactions). *Constraints*: `probability 0.0-1.0`. *Default value*: `0.2`.| -|`croSynthesisThreshold`| __Double__. CRO: Synthesis KE threshold s_t (molecule can synthesize if KE ≤ s_t). *Constraints*: `min=0.0`. *Default value*: `10.0`.| |`customNaming`| __Boolean__. Enable custom naming and sorting criteria. *Default value*: `true`.| |`d`| __Double__. When weight-based mutation rate is enabled, specify a percentage of calculating mutation rate based on a number of candidate genes to mutate. For instance, d = 1.0 means that the mutation rate fully depends on a number of candidate genes to mutate, and d = 0.0 means that the mutation rate fully depends on weights of candidates genes to mutate. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.8`.| |`dependencyFile`| __String__. Specify a file that saves derived dependencies. *DEBUG option*. *Default value*: `dependencies.csv`.| @@ -233,6 +228,7 @@ There are 3 types of options: |`useResponseDataPool`| __Boolean__. Enable the collection of response data, to feed new individuals based on field names matching. *Default value*: `true`.| |`useTimeInFeedbackSampling`| __Boolean__. Whether to use timestamp info on the execution time of the tests for sampling (e.g., to reward the quickest ones). *Default value*: `true`.| |`weightBasedMutationRate`| __Boolean__. Whether to enable a weight-based mutation rate. *Default value*: `true`.| +|`writeCfg`| __Boolean__. Enable writing CFG/CDG graphs to disk on the agent side. *Default value*: `true`.| |`writeExtraHeuristicsFile`| __Boolean__. Whether we should collect data on the extra heuristics. Only needed for experiments. *Default value*: `false`.| |`writeStatistics`| __Boolean__. Whether or not writing statistics of the search process. This is only needed when running experiments with different parameter settings. *Default value*: `false`.| |`writeWFCReport`| __Boolean__. Output a JSON file representing statistics of the fuzzing session, written in the WFC Report format. This also includes a index.html web application to visualize such data. *Default value*: `true`.| @@ -323,4 +319,3 @@ There are 3 types of options: |`vulnerableInputClassificationStrategy`| __Enum__. Strategy to classify inputs for potential vulnerability classes related to an REST endpoint. *Valid values*: `MANUAL, LLM`. *Default value*: `MANUAL`.| |`wbProbabilityUseDataPool`| __Double__. Specify the probability of using the data pool when sampling test cases. This is for white-box (wb) mode. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.2`.| |`writeSnapshotTestsIntervalInSeconds`| __Int__. The size (in seconds) of the interval that the snapshots will be printed, if enabled. *Default value*: `3600`.| -|`xss`| __Boolean__. To apply XSS detection as part of security testing. *Default value*: `false`.| From 1e7634cb466cf8bf748b9d1aa5b7db3a2bea18dd Mon Sep 17 00:00:00 2001 From: francastagna Date: Tue, 2 Dec 2025 21:52:08 -0300 Subject: [PATCH 06/21] updated client-java/pom.xml --- client-java/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-java/pom.xml b/client-java/pom.xml index b7639bfe12..892f6e62b7 100644 --- a/client-java/pom.xml +++ b/client-java/pom.xml @@ -14,10 +14,10 @@ ${project.groupId}:${project.artifactId} + instrumentation-shared controller controller-api instrumentation - instrumentation-shared client-util ci-utils dependencies From 9477a69fa58a22b3bf70fd2bb1fb2ea6cd65b5cb Mon Sep 17 00:00:00 2001 From: francastagna Date: Tue, 2 Dec 2025 21:53:43 -0300 Subject: [PATCH 07/21] fixes --- .../shared/dto/ControlDependenceGraphDto.java | 200 ++++++++++++++++++ client-java/pom.xml | 2 +- 2 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 client-java/instrumentation-shared/src/main/java/org/evomaster/client/java/instrumentation/shared/dto/ControlDependenceGraphDto.java diff --git a/client-java/instrumentation-shared/src/main/java/org/evomaster/client/java/instrumentation/shared/dto/ControlDependenceGraphDto.java b/client-java/instrumentation-shared/src/main/java/org/evomaster/client/java/instrumentation/shared/dto/ControlDependenceGraphDto.java new file mode 100644 index 0000000000..2e14e59966 --- /dev/null +++ b/client-java/instrumentation-shared/src/main/java/org/evomaster/client/java/instrumentation/shared/dto/ControlDependenceGraphDto.java @@ -0,0 +1,200 @@ +package org.evomaster.client.java.instrumentation.shared.dto; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Serializable representation of the control-dependence information of a method. + * Contains only the data needed by EvoMaster core: branch objective identifiers, + * the subset that are roots, and the parent→child relationships between them. + */ +public class ControlDependenceGraphDto implements Serializable { + + private static final long serialVersionUID = -1703430508792441369L; + + private String className; + private String methodName; + private List objectives = new ArrayList<>(); + private List rootObjectiveIds = new ArrayList<>(); + private List edges = new ArrayList<>(); + + public ControlDependenceGraphDto() { + } + + public ControlDependenceGraphDto(String className, + String methodName, + List objectives, + List rootObjectiveIds, + List edges) { + this.className = className; + this.methodName = methodName; + if (objectives != null) { + this.objectives = new ArrayList<>(objectives); + } + if (rootObjectiveIds != null) { + this.rootObjectiveIds = new ArrayList<>(rootObjectiveIds); + } + if (edges != null) { + this.edges = new ArrayList<>(edges); + } + } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public List getObjectives() { + return Collections.unmodifiableList(objectives); + } + + public void setObjectives(List objectives) { + this.objectives = objectives == null ? new ArrayList<>() : new ArrayList<>(objectives); + } + + public List getRootObjectiveIds() { + return Collections.unmodifiableList(rootObjectiveIds); + } + + public void setRootObjectiveIds(List rootObjectiveIds) { + this.rootObjectiveIds = rootObjectiveIds == null ? new ArrayList<>() : new ArrayList<>(rootObjectiveIds); + } + + public List getEdges() { + return Collections.unmodifiableList(edges); + } + + public void setEdges(List edges) { + this.edges = edges == null ? new ArrayList<>() : new ArrayList<>(edges); + } + + public void addObjective(BranchObjectiveDto objective) { + if (objective != null) { + this.objectives.add(objective); + } + } + + public void addRootObjectiveId(Integer id) { + if (id != null) { + this.rootObjectiveIds.add(id); + } + } + + public void addEdge(DependencyEdgeDto edge) { + if (edge != null) { + this.edges.add(edge); + } + } + + /** + * Descriptor and numeric id for a branch objective (true or false outcome). + */ + public static class BranchObjectiveDto implements Serializable { + + private static final long serialVersionUID = -8122197698885173402L; + + private int id; + private String descriptiveId; + + public BranchObjectiveDto() { + } + + public BranchObjectiveDto(int id, String descriptiveId) { + this.id = id; + this.descriptiveId = descriptiveId; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getDescriptiveId() { + return descriptiveId; + } + + public void setDescriptiveId(String descriptiveId) { + this.descriptiveId = descriptiveId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BranchObjectiveDto that = (BranchObjectiveDto) o; + return id == that.id && Objects.equals(descriptiveId, that.descriptiveId); + } + + @Override + public int hashCode() { + return Objects.hash(id, descriptiveId); + } + } + + /** + * Directed edge describing that {@code parentObjectiveId} must be covered before {@code childObjectiveId}. + */ + public static class DependencyEdgeDto implements Serializable { + + private static final long serialVersionUID = 3167520314102399629L; + + private int parentObjectiveId; + private int childObjectiveId; + + public DependencyEdgeDto() { + } + + public DependencyEdgeDto(int parentObjectiveId, int childObjectiveId) { + this.parentObjectiveId = parentObjectiveId; + this.childObjectiveId = childObjectiveId; + } + + public int getParentObjectiveId() { + return parentObjectiveId; + } + + public void setParentObjectiveId(int parentObjectiveId) { + this.parentObjectiveId = parentObjectiveId; + } + + public int getChildObjectiveId() { + return childObjectiveId; + } + + public void setChildObjectiveId(int childObjectiveId) { + this.childObjectiveId = childObjectiveId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DependencyEdgeDto that = (DependencyEdgeDto) o; + return parentObjectiveId == that.parentObjectiveId && + childObjectiveId == that.childObjectiveId; + } + + @Override + public int hashCode() { + return Objects.hash(parentObjectiveId, childObjectiveId); + } + } +} + diff --git a/client-java/pom.xml b/client-java/pom.xml index 892f6e62b7..b7639bfe12 100644 --- a/client-java/pom.xml +++ b/client-java/pom.xml @@ -14,10 +14,10 @@ ${project.groupId}:${project.artifactId} - instrumentation-shared controller controller-api instrumentation + instrumentation-shared client-util ci-utils dependencies From 0782b66b53a86c3a110bdf7091a1d7ed7098104f Mon Sep 17 00:00:00 2001 From: francastagna Date: Tue, 2 Dec 2025 21:59:44 -0300 Subject: [PATCH 08/21] refactoring: remove unused class --- .../RemoveFinalClassAdapter.java | 77 ------------------- 1 file changed, 77 deletions(-) delete mode 100644 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/RemoveFinalClassAdapter.java diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/RemoveFinalClassAdapter.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/RemoveFinalClassAdapter.java deleted file mode 100644 index a87416c89d..0000000000 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/RemoveFinalClassAdapter.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite - * contributors - * - * This file is part of EvoSuite. - * - * EvoSuite is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * EvoSuite is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with EvoSuite. If not, see . - */ -package org.evomaster.client.java.instrumentation; - -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; - -import java.util.LinkedHashSet; -import java.util.Set; - -public class RemoveFinalClassAdapter extends ClassVisitor { - - public static final Set finalClasses = new LinkedHashSet<>(); - - public RemoveFinalClassAdapter(ClassVisitor cv) { - super(Opcodes.ASM9, cv); - } - - /** - * Remove "final" accessor from class definition - */ - @Override - public void visit(int version, int access, String name, String signature, - String superName, String[] interfaces) { - if ((access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL) { - finalClasses.add(name.replace('/', '.')); - } - if ((access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT && - (access & Opcodes.ACC_PUBLIC) == 0 && - (access & Opcodes.ACC_PRIVATE) == 0 && - (access & Opcodes.ACC_PROTECTED) == 0) { - // If a class is abstract and default-accessible, then we make it public to allow - // Mockito to create mocks - super.visit(version, (access & ~Opcodes.ACC_FINAL) | Opcodes.ACC_PUBLIC, name, signature, superName, interfaces); - } else { - super.visit(version, access & ~Opcodes.ACC_FINAL, name, signature, superName, interfaces); - } - } - - /** - * Remove "final" accessor from inner class definition - */ - @Override - public void visitInnerClass(String name, String outerName, String innerName, int access) { - if ((access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL) { - finalClasses.add(name.replace('/', '.')); - } - super.visitInnerClass(name, outerName, innerName, access & ~Opcodes.ACC_FINAL); - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - return super.visitMethod(access & ~Opcodes.ACC_FINAL, name, desc, signature, exceptions); - } - - public static void reset() { - finalClasses.clear(); - } -} From d29d51589f0baa8797bc095d1ed32794bda61ebf Mon Sep 17 00:00:00 2001 From: francastagna Date: Tue, 2 Dec 2025 22:01:20 -0300 Subject: [PATCH 09/21] refactoring: cleaning stuff --- .../org/evomaster/client/java/controller/api/dto/SutInfoDto.java | 1 + 1 file changed, 1 insertion(+) diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutInfoDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutInfoDto.java index 774bf6edc1..ea50070eea 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutInfoDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutInfoDto.java @@ -6,6 +6,7 @@ import org.evomaster.client.java.controller.api.dto.problem.RestProblemDto; import org.evomaster.client.java.controller.api.dto.problem.GraphQLProblemDto; import org.evomaster.client.java.controller.api.dto.problem.WebProblemDto; + import java.util.List; public class SutInfoDto { From 755bd48a677e0f0c6404b720f9512ae4a84db097 Mon Sep 17 00:00:00 2001 From: francastagna Date: Tue, 2 Dec 2025 22:02:47 -0300 Subject: [PATCH 10/21] refactoring: cleaning stuff --- .../client/java/controller/api/ControllerConstants.java | 1 - 1 file changed, 1 deletion(-) diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/ControllerConstants.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/ControllerConstants.java index c58e8cf8ff..95a1ee6017 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/ControllerConstants.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/ControllerConstants.java @@ -33,5 +33,4 @@ public class ControllerConstants { public static final String POST_SEARCH_ACTION = "/postSearchAction"; public static final String DERIVE_PARAMS = "/deriveParams"; - } From 83149d37d4fd401b4bfb839d6c646897defa139a Mon Sep 17 00:00:00 2001 From: francastagna Date: Tue, 2 Dec 2025 22:13:10 -0300 Subject: [PATCH 11/21] refactoring: cleaning stuff --- .../org/evomaster/core/search/algorithms/MosaAlgorithm.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/algorithms/MosaAlgorithm.kt b/core/src/main/kotlin/org/evomaster/core/search/algorithms/MosaAlgorithm.kt index 34284c2125..085a271740 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/algorithms/MosaAlgorithm.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/algorithms/MosaAlgorithm.kt @@ -8,6 +8,7 @@ import org.evomaster.core.logging.LoggingUtil import java.util.ArrayList + /** * Implementation of MOSA from * "Automated Test Case Generation as a Many-Objective Optimisation Problem with Dynamic @@ -243,7 +244,7 @@ class MosaAlgorithm : SearchAlgorithm() where T : Individual { else if (dominatesX) return -1 - else // (dominatesY) + else (dominatesY) return +1 } From 300ba05fef3164765f8f37c069b0c5caa8c4bf20 Mon Sep 17 00:00:00 2001 From: francastagna Date: Fri, 5 Dec 2025 10:25:16 -0300 Subject: [PATCH 12/21] add tests --- client-java/instrumentation/pom.xml | 1 - .../dynamosa/graphs/GraphPool.java | 9 + .../graphs/cdg/ControlDependenceGraph.java | 9 + .../dynamosa/graphs/cdg/DominatorTree.java | 1 - .../graphs/cfg/branch/BranchPool.java | 11 + .../dynamosa/graphs/GraphPoolTest.java | 155 ++++++++++++ .../cdg/ControlDependenceGraphTest.java | 229 ++++++++++++++++++ .../graphs/cdg/DominatorNodeTest.java | 85 +++++++ .../cfg/ActualControlFlowGraphTest.java | 168 +++++++++++++ .../dynamosa/graphs/cfg/BasicBlockTest.java | 105 ++++++++ .../cfg/BytecodeInstructionPoolTest.java | 89 +++++++ .../graphs/cfg/BytecodeInstructionTest.java | 132 ++++++++++ .../dynamosa/graphs/cfg/CFGGeneratorTest.java | 114 +++++++++ .../graphs/cfg/ControlDependencyTest.java | 68 ++++++ .../graphs/cfg/ControlFlowEdgeTest.java | 76 ++++++ .../dynamosa/graphs/cfg/EntryBlockTest.java | 31 +++ .../dynamosa/graphs/cfg/ExitBlockTest.java | 31 +++ .../graphs/cfg/RawControlFlowGraphTest.java | 159 ++++++++++++ .../graphs/cfg/branch/BranchPoolTest.java | 117 +++++++++ .../graphs/cfg/branch/BranchTest.java | 105 ++++++++ 20 files changed, 1693 insertions(+), 2 deletions(-) create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/GraphPoolTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/ControlDependenceGraphTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorNodeTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ActualControlFlowGraphTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BasicBlockTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionPoolTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGGeneratorTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlDependencyTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowEdgeTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/EntryBlockTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ExitBlockTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/RawControlFlowGraphTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchPoolTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchTest.java diff --git a/client-java/instrumentation/pom.xml b/client-java/instrumentation/pom.xml index cf5284c16e..b4b9dc84d0 100644 --- a/client-java/instrumentation/pom.xml +++ b/client-java/instrumentation/pom.xml @@ -181,7 +181,6 @@ com.google.guava guava - test org.mongodb 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 index 64ccdd0ce4..a0bc407a18 100755 --- 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 @@ -5,6 +5,7 @@ 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; @@ -66,6 +67,14 @@ public static GraphPool getInstance(ClassLoader 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 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 index 242e8800c4..560fcbdb2a 100755 --- 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 @@ -207,4 +207,13 @@ public String getClassName() { 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/DominatorTree.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorTree.java index ec16a547bc..7e51026f58 100755 --- 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 @@ -104,7 +104,6 @@ private void createDominatorTree() { // check tree is connected if (!isConnected()) throw new IllegalStateException("dominator tree expected to be connected"); - // TODO more sanity checks - no one likes to be insane ;) } private void computeDominatorFrontiers(DominatorNode currentNode) { 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 index 1cbb5ef5af..dbd5c2190f 100755 --- 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 @@ -4,6 +4,7 @@ */ 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; @@ -43,6 +44,16 @@ public static BranchPool getInstance(ClassLoader classLoader) { 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 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 nodes = Collections.singletonList(instruction); + + assertThrows(IllegalArgumentException.class, + () -> new BasicBlock(TEST_LOADER, null, METHOD_NAME, nodes)); + assertThrows(IllegalArgumentException.class, + () -> new BasicBlock(TEST_LOADER, CLASS_NAME, null, nodes)); + assertThrows(IllegalArgumentException.class, + () -> new BasicBlock(TEST_LOADER, CLASS_NAME, METHOD_NAME, null)); + } + + @Test + void constructorRejectsInvalidInstructionLists() { + BytecodeInstruction valid = instruction(CLASS_NAME, METHOD_NAME, 1, 101); + BytecodeInstruction otherClass = instruction("other.Class", METHOD_NAME, 2, 102); + BytecodeInstruction otherMethod = instruction(CLASS_NAME, "other()V", 3, 103); + BytecodeInstruction duplicate = instruction(CLASS_NAME, METHOD_NAME, 4, 104); + + assertThrows(IllegalArgumentException.class, + () -> new BasicBlock(TEST_LOADER, CLASS_NAME, METHOD_NAME, Arrays.asList(valid, otherClass))); + assertThrows(IllegalArgumentException.class, + () -> new BasicBlock(TEST_LOADER, CLASS_NAME, METHOD_NAME, Arrays.asList(valid, otherMethod))); + assertThrows(IllegalArgumentException.class, + () -> new BasicBlock(TEST_LOADER, CLASS_NAME, METHOD_NAME, Arrays.asList(duplicate, duplicate))); + } + + @Test + void constructorRejectsInstructionsAlreadyBelongingToAnotherBlock() { + BytecodeInstruction shared = instruction(CLASS_NAME, METHOD_NAME, 5, 105); + new BasicBlock(TEST_LOADER, CLASS_NAME, METHOD_NAME, Collections.singletonList(shared)); + + assertThrows(IllegalArgumentException.class, + () -> new BasicBlock(TEST_LOADER, CLASS_NAME, METHOD_NAME, Collections.singletonList(shared))); + } + + @Test + void exposesInstructionOrderLinesAndFlags() { + BytecodeInstruction first = instruction(CLASS_NAME, METHOD_NAME, 6, 106); + BytecodeInstruction second = instruction(CLASS_NAME, METHOD_NAME, 7, 207); + BasicBlock block = new BasicBlock(TEST_LOADER, CLASS_NAME, METHOD_NAME, Arrays.asList(first, second)); + + assertSame(first, block.getFirstInstruction()); + assertSame(second, block.getLastInstruction()); + assertEquals(106, block.getFirstLine()); + assertEquals(207, block.getLastLine()); + assertTrue(block.containsInstruction(first)); + assertTrue(block.containsInstruction(second)); + + List iterated = new ArrayList<>(); + block.forEach(iterated::add); + assertEquals(Arrays.asList(first, second), iterated); + + assertFalse(block.isEntryBlock()); + assertFalse(block.isExitBlock()); + } + + @Test + void nameAndToStringReflectBlockIdentity() { + BytecodeInstruction ins = instruction(CLASS_NAME, METHOD_NAME, 8, 208); + BasicBlock block = new BasicBlock(TEST_LOADER, CLASS_NAME, METHOD_NAME, Collections.singletonList(ins)); + + assertTrue(block.getName().contains("BasicBlock")); + assertTrue(block.toString().contains(ins.getInstructionType())); + assertTrue(block.toString().contains("l208")); + + BasicBlock other = new BasicBlock(TEST_LOADER, CLASS_NAME, METHOD_NAME, Collections.singletonList(instruction(CLASS_NAME, METHOD_NAME, 9, 209))); + assertNotEquals(block.getName(), other.getName()); + assertNotEquals(block, other); + } + + private static BytecodeInstruction instruction(String className, String methodName, int id, int line) { + BytecodeInstruction instruction = new BytecodeInstruction( + TEST_LOADER, + className, + methodName, + id, + id, + new InsnNode(Opcodes.NOP) + ); + instruction.setLineNumber(line); + return instruction; + } +} + diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionPoolTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionPoolTest.java new file mode 100644 index 0000000000..dfd2f43a89 --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionPoolTest.java @@ -0,0 +1,89 @@ +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.*; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class BytecodeInstructionPoolTest { + + private static final ClassLoader TEST_LOADER = BytecodeInstructionPoolTest.class.getClassLoader(); + private static final String CLASS_NAME_PREFIX = "com.example.BytecodeInstructionPoolFixture"; + private static final String METHOD_NAME_PREFIX = "sample"; + + @AfterEach + void resetBranchPool() { + BranchPool.resetForTesting(TEST_LOADER); + } + + @Test + void registerMethodNodePopulatesMappingsAndBranches() { + String className = uniqueClassName("register"); + String methodName = uniqueMethodName("register"); + BytecodeInstructionPool pool = BytecodeInstructionPool.getInstance(TEST_LOADER); + MethodNode methodNode = new MethodNode(Opcodes.ASM9, Opcodes.ACC_PUBLIC, methodName, "()V", null, null); + + LabelNode label = new LabelNode(); + methodNode.instructions.add(new LineNumberNode(100, label)); + methodNode.instructions.add(label); + methodNode.instructions.add(new InsnNode(Opcodes.NOP)); + methodNode.instructions.add(new JumpInsnNode(Opcodes.IFEQ, new LabelNode())); + methodNode.instructions.add(new InsnNode(Opcodes.RETURN)); + + List instructions = pool.registerMethodNode(methodNode, className, methodName); + + assertEquals(5, instructions.size()); + assertTrue(instructions.get(2).hasLineNumberSet()); + assertEquals(100, instructions.get(2).getLineNumber()); + + BytecodeInstruction branch = instructions.get(3); + assertTrue(branch.isBranch()); + assertTrue(BranchPool.getInstance(TEST_LOADER).isKnownAsBranch(branch)); + + List byLookup = pool.getInstructionsIn(className, methodName); + assertEquals(instructions, byLookup); + } + + @Test + void getInstructionByIdReturnsRegisteredInstruction() { + String className = uniqueClassName("lookup"); + String methodName = uniqueMethodName("lookup"); + BytecodeInstructionPool pool = BytecodeInstructionPool.getInstance(TEST_LOADER); + MethodNode methodNode = new MethodNode(Opcodes.ASM9, Opcodes.ACC_PUBLIC, methodName, "()V", null, null); + methodNode.instructions.add(new InsnNode(Opcodes.NOP)); + methodNode.instructions.add(new InsnNode(Opcodes.RETURN)); + pool.registerMethodNode(methodNode, className, methodName); + + BytecodeInstruction instruction = pool.getInstruction(className, methodName, 1); + assertNotNull(instruction); + assertEquals(Opcodes.RETURN, instruction.getASMNode().getOpcode()); + } + + @Test + void forgetInstructionRemovesFromPool() { + String className = uniqueClassName("forget"); + String methodName = uniqueMethodName("forget"); + BytecodeInstructionPool pool = BytecodeInstructionPool.getInstance(TEST_LOADER); + MethodNode methodNode = new MethodNode(Opcodes.ASM9, Opcodes.ACC_PUBLIC, methodName, "()V", null, null); + methodNode.instructions.add(new InsnNode(Opcodes.NOP)); + pool.registerMethodNode(methodNode, className, methodName); + + BytecodeInstruction instruction = pool.getInstruction(className, methodName, 0); + assertTrue(pool.forgetInstruction(instruction)); + assertTrue(pool.getInstructionsIn(className, methodName).isEmpty()); + } + + private static String uniqueClassName(String suffix) { + return CLASS_NAME_PREFIX + "." + suffix + "." + System.nanoTime(); + } + + private static String uniqueMethodName(String suffix) { + return METHOD_NAME_PREFIX + "_" + suffix + "_" + System.nanoTime(); + } +} + diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionTest.java new file mode 100644 index 0000000000..ade368d33b --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionTest.java @@ -0,0 +1,132 @@ +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.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 org.objectweb.asm.tree.LineNumberNode; + +import static org.junit.jupiter.api.Assertions.*; + +class BytecodeInstructionTest { + + private static final ClassLoader TEST_LOADER = BytecodeInstructionTest.class.getClassLoader(); + private static final String CLASS_NAME = "com.example.BytecodeInstructionFixture"; + private static final String METHOD_NAME = "sample()V"; + + @AfterEach + void resetBranchPool() { + BranchPool.resetForTesting(TEST_LOADER); + } + + @Test + void constructorRejectsInvalidArguments() { + AbstractInsnNodeStub node = new AbstractInsnNodeStub(Opcodes.NOP); + assertThrows(IllegalArgumentException.class, + () -> new BytecodeInstruction(TEST_LOADER, null, METHOD_NAME, 0, 0, node)); + assertThrows(IllegalArgumentException.class, + () -> new BytecodeInstruction(TEST_LOADER, CLASS_NAME, METHOD_NAME, -1, 0, node)); + assertThrows(IllegalArgumentException.class, + () -> new BytecodeInstruction(TEST_LOADER, CLASS_NAME, METHOD_NAME, 0, 0, null)); + } + + @Test + void setBasicBlockValidatesClassAndSingleAssignment() { + BytecodeInstruction instruction = instruction(1, new InsnNode(Opcodes.NOP)); + + DummyBlock matching = new DummyBlock(CLASS_NAME, METHOD_NAME); + instruction.setBasicBlock(matching); + assertTrue(instruction.hasBasicBlockSet()); + assertSame(matching, instruction.getBasicBlock()); + + DummyBlock wrongClass = new DummyBlock("other.Class", METHOD_NAME); + assertThrows(IllegalArgumentException.class, () -> instruction.setBasicBlock(wrongClass)); + assertThrows(IllegalArgumentException.class, () -> instruction.setBasicBlock(matching)); + } + + @Test + void lineNumberManagementHandlesManualAndAsmValues() { + BytecodeInstruction manual = instruction(2, new InsnNode(Opcodes.NOP)); + manual.setLineNumber(150); + assertEquals(150, manual.getLineNumber()); + + BytecodeInstruction fromAsm = new BytecodeInstruction( + TEST_LOADER, + CLASS_NAME, + METHOD_NAME, + 3, + 3, + new LineNumberNode(321, new LabelNode()) + ); + assertEquals(321, fromAsm.getLineNumber()); + } + + @Test + void branchDetectionAndExplainUseBranchPoolInformation() { + BytecodeInstruction branchInsn = instruction(4, new JumpInsnNode(Opcodes.IFEQ, new LabelNode())); + BranchPool.getInstance(TEST_LOADER).registerAsBranch(branchInsn); + + assertTrue(branchInsn.isBranch()); + Branch branch = branchInsn.toBranch(); + assertNotNull(branch); + + String explanation = branchInsn.explain(); + assertTrue(explanation.contains("Branch")); + assertTrue(explanation.contains("IFEQ")); + } + + @Test + void returnAndThrowDetectionWorks() { + BytecodeInstruction returnInsn = instruction(5, new InsnNode(Opcodes.RETURN)); + assertTrue(returnInsn.isReturn()); + assertTrue(returnInsn.canReturnFromMethod()); + assertFalse(returnInsn.isThrow()); + + BytecodeInstruction throwInsn = instruction(6, new InsnNode(Opcodes.ATHROW)); + assertFalse(throwInsn.isReturn()); + assertTrue(throwInsn.isThrow()); + assertTrue(throwInsn.canReturnFromMethod()); + } + + private static BytecodeInstruction instruction(int id, org.objectweb.asm.tree.AbstractInsnNode node) { + return new BytecodeInstruction( + TEST_LOADER, + CLASS_NAME, + METHOD_NAME, + id, + id, + node + ); + } + + private static final class DummyBlock extends BasicBlock { + private DummyBlock(String className, String methodName) { + super(className, methodName); + } + } + + private static final class AbstractInsnNodeStub extends org.objectweb.asm.tree.AbstractInsnNode { + private AbstractInsnNodeStub(int opcode) { + super(opcode); + } + + @Override + public int getType() { + return 0; + } + + @Override + public void accept(org.objectweb.asm.MethodVisitor methodVisitor) { + } + + @Override + public org.objectweb.asm.tree.AbstractInsnNode clone(java.util.Map labels) { + return new AbstractInsnNodeStub(opcode); + } + } +} + diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGGeneratorTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGGeneratorTest.java new file mode 100644 index 0000000000..330bbfa3e2 --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGGeneratorTest.java @@ -0,0 +1,114 @@ +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.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.LabelNode; +import org.objectweb.asm.tree.LineNumberNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.analysis.Frame; + +import static org.junit.jupiter.api.Assertions.*; + +class CFGGeneratorTest { + + private static final ClassLoader TEST_LOADER = CFGGeneratorTest.class.getClassLoader(); + private static final String CLASS_NAME_PREFIX = "com.example.CFGGeneratorFixture"; + private static final String METHOD_NAME_PREFIX = "sample"; + + @AfterEach + void resetPools() { + GraphPool.resetForTesting(TEST_LOADER); + BranchPool.resetForTesting(TEST_LOADER); + } + + @Test + void constructorRejectsNullArguments() { + MethodNode method = simpleMethodNode(); + assertThrows(IllegalArgumentException.class, () -> new CFGGenerator(TEST_LOADER, null, "m()V", method)); + assertThrows(IllegalArgumentException.class, () -> new CFGGenerator(TEST_LOADER, "c", null, method)); + assertThrows(IllegalArgumentException.class, () -> new CFGGenerator(TEST_LOADER, "c", "m()V", null)); + } + + @Test + void registerCFGsStoresGraphsInPool() { + String className = uniqueClassName("store"); + String methodName = uniqueMethodName("store"); + MethodNode method = simpleMethodNode(); + + CFGGenerator generator = new CFGGenerator(TEST_LOADER, className, methodName, method); + + // Frames in ASM are analysis snapshots of the JVM state at each instruction. + // We don't care about them, but registerControlFlowEdge() requires them. + // So, we create a dummy frame for each instruction. + + int size = method.instructions.size(); + Frame[] frames = new Frame[size]; + for (int i = 0; i < size; i++) { + frames[i] = new Frame<>(0, 0); + } + for (int i = 0; i < size - 1; i++) { + generator.registerControlFlowEdge(i, i + 1, frames, false); + } + + generator.registerCFGs(); + + RawControlFlowGraph raw = GraphPool.getInstance(TEST_LOADER).getRawCFG(className, methodName); + ActualControlFlowGraph actual = GraphPool.getInstance(TEST_LOADER).getActualCFG(className, methodName); + + assertSame(generator.getRawGraph(), raw); + assertNotNull(actual); + assertFalse(raw.vertexSet().isEmpty()); + assertFalse(actual.vertexSet().isEmpty()); + } + + @Test + void registerControlFlowEdgeRejectsNullFrames() { + String className = uniqueClassName("nullFrames"); + String methodName = uniqueMethodName("nullFrames"); + MethodNode method = simpleMethodNode(); + + CFGGenerator generator = new CFGGenerator(TEST_LOADER, className, methodName, method); + + assertThrows(IllegalArgumentException.class, () -> + generator.registerControlFlowEdge(0, 1, null, false)); + } + + @Test + void registerControlFlowEdgeSkipsUnreachableDestination() { + String className = uniqueClassName("unreachable"); + String methodName = uniqueMethodName("unreachable"); + MethodNode method = simpleMethodNode(); + + CFGGenerator generator = new CFGGenerator(TEST_LOADER, className, methodName, method); + + Frame[] frames = new Frame[2]; + generator.registerControlFlowEdge(0, 1, frames, false); + + assertTrue(generator.getRawGraph().edgeSet().isEmpty()); + } + + private static MethodNode simpleMethodNode() { + MethodNode methodNode = new MethodNode(Opcodes.ASM9, Opcodes.ACC_PUBLIC, "dummy", "()V", null, null); + + LabelNode label = new LabelNode(); + methodNode.instructions.add(new LineNumberNode(100, label)); + methodNode.instructions.add(label); + methodNode.instructions.add(new InsnNode(Opcodes.NOP)); + methodNode.instructions.add(new InsnNode(Opcodes.RETURN)); + + return methodNode; + } + + private static String uniqueClassName(String suffix) { + return CLASS_NAME_PREFIX + "." + suffix + "." + System.nanoTime(); + } + + private static String uniqueMethodName(String suffix) { + return METHOD_NAME_PREFIX + "_" + suffix + "_" + System.nanoTime() + "()V"; + } +} + diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlDependencyTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlDependencyTest.java new file mode 100644 index 0000000000..be56e490c3 --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlDependencyTest.java @@ -0,0 +1,68 @@ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; + +import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch; +import org.junit.jupiter.api.Test; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.JumpInsnNode; +import org.objectweb.asm.tree.LabelNode; + +import static org.junit.jupiter.api.Assertions.*; + +class ControlDependencyTest { + + private static final ClassLoader TEST_LOADER = ControlDependencyTest.class.getClassLoader(); + private static final String CLASS_NAME = "com.example.ControlDependencyFixture"; + private static final String METHOD_NAME = "sample()V"; + + @Test + void constructorRequiresBranch() { + assertThrows(IllegalArgumentException.class, () -> new ControlDependency(null, true)); + } + + @Test + void gettersReflectBranchAndExpressionValue() { + Branch branch = branch(1, 120); + ControlDependency dependency = new ControlDependency(branch, true); + + assertSame(branch, dependency.getBranch()); + assertTrue(dependency.getBranchExpressionValue()); + assertTrue(dependency.toString().contains("TRUE")); + + ControlDependency falseDependency = new ControlDependency(branch, false); + assertFalse(falseDependency.getBranchExpressionValue()); + assertTrue(falseDependency.toString().contains("FALSE")); + } + + @Test + void equalsAndHashCodeDependOnBranchAndValue() { + Branch branchA = branch(2, 130); + Branch branchB = branch(3, 140); + + ControlDependency depTrue = new ControlDependency(branchA, true); + ControlDependency depTrueCopy = new ControlDependency(branchA, true); + ControlDependency depFalse = new ControlDependency(branchA, false); + ControlDependency depOtherBranch = new ControlDependency(branchB, true); + + assertEquals(depTrue, depTrueCopy); + assertEquals(depTrue.hashCode(), depTrueCopy.hashCode()); + + assertNotEquals(depTrue, depFalse); + assertNotEquals(depTrue, depOtherBranch); + assertNotEquals(depTrue, null); + assertNotEquals(depTrue, "dependency"); + } + + private static Branch branch(int instructionId, int lineNumber) { + BytecodeInstruction instruction = new BytecodeInstruction( + TEST_LOADER, + CLASS_NAME, + METHOD_NAME, + instructionId, + instructionId, + new JumpInsnNode(Opcodes.IFEQ, new LabelNode()) + ); + instruction.setLineNumber(lineNumber); + return new Branch(instruction, instructionId + 1); + } +} + diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowEdgeTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowEdgeTest.java new file mode 100644 index 0000000000..558a9d435a --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowEdgeTest.java @@ -0,0 +1,76 @@ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; + +import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch; +import org.junit.jupiter.api.Test; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.JumpInsnNode; +import org.objectweb.asm.tree.LabelNode; + +import static org.junit.jupiter.api.Assertions.*; + +class ControlFlowEdgeTest { + + private static final ClassLoader TEST_LOADER = ControlFlowEdgeTest.class.getClassLoader(); + private static final String CLASS_NAME = "com.example.ControlFlowEdgeFixture"; + private static final String METHOD_NAME = "sample()V"; + + @Test + void defaultConstructorProducesEdgeWithoutDependency() { + ControlFlowEdge edge = new ControlFlowEdge(); + + assertFalse(edge.hasControlDependency()); + assertNull(edge.getControlDependency()); + assertNull(edge.getBranchInstruction()); + assertTrue(edge.getBranchExpressionValue()); + assertFalse(edge.isExceptionEdge()); + } + + @Test + void constructorWithDependencyStoresBranchAndExceptionFlag() { + Branch branch = branch(1, 120); + ControlDependency dependency = new ControlDependency(branch, false); + + ControlFlowEdge edge = new ControlFlowEdge(dependency, true); + + assertTrue(edge.hasControlDependency()); + assertSame(dependency, edge.getControlDependency()); + assertSame(branch, edge.getBranchInstruction()); + assertFalse(edge.getBranchExpressionValue()); + assertTrue(edge.isExceptionEdge()); + assertTrue(edge.toString().contains("FALSE")); + } + + @Test + void copyConstructorDuplicatesFlagsAndDependencyReference() { + ControlFlowEdge original = new ControlFlowEdge(new ControlDependency(branch(2, 130), true), false); + + ControlFlowEdge copy = new ControlFlowEdge(original); + + assertNotSame(original, copy); + assertEquals(original.hasControlDependency(), copy.hasControlDependency()); + assertSame(original.getControlDependency(), copy.getControlDependency()); + assertEquals(original.isExceptionEdge(), copy.isExceptionEdge()); + } + + @Test + void booleanConstructorOnlySetsExceptionFlag() { + ControlFlowEdge edge = new ControlFlowEdge(true); + + assertTrue(edge.isExceptionEdge()); + assertFalse(edge.hasControlDependency()); + } + + private static Branch branch(int instructionId, int lineNumber) { + BytecodeInstruction instruction = new BytecodeInstruction( + TEST_LOADER, + CLASS_NAME, + METHOD_NAME, + instructionId, + instructionId, + new JumpInsnNode(Opcodes.IFEQ, new LabelNode()) + ); + instruction.setLineNumber(lineNumber); + return new Branch(instruction, instructionId + 10); + } +} + diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/EntryBlockTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/EntryBlockTest.java new file mode 100644 index 0000000000..cdb229be72 --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/EntryBlockTest.java @@ -0,0 +1,31 @@ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class EntryBlockTest { + + private static final String CLASS_NAME = "com.example.EntryBlockFixture"; + private static final String METHOD_NAME = "sample()V"; + + @Test + void entryBlockSetsFlagsAndMetadata() { + EntryBlock block = new EntryBlock(CLASS_NAME, METHOD_NAME); + + assertTrue(block.isEntryBlock()); + assertFalse(block.isExitBlock()); + assertEquals(CLASS_NAME, block.getClassName()); + assertEquals(METHOD_NAME, block.getMethodName()); + } + + @Test + void entryBlockProvidesDescriptiveNameAndToString() { + EntryBlock block = new EntryBlock(CLASS_NAME, METHOD_NAME); + + String expected = "EntryBlock for method " + METHOD_NAME; + assertEquals(expected, block.getName()); + assertEquals(expected, block.toString()); + } +} + diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ExitBlockTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ExitBlockTest.java new file mode 100644 index 0000000000..8da5ade2af --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ExitBlockTest.java @@ -0,0 +1,31 @@ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ExitBlockTest { + + private static final String CLASS_NAME = "com.example.ExitBlockFixture"; + private static final String METHOD_NAME = "sample()V"; + + @Test + void exitBlockSetsFlagsAndMetadata() { + ExitBlock block = new ExitBlock(CLASS_NAME, METHOD_NAME); + + assertTrue(block.isExitBlock()); + assertFalse(block.isEntryBlock()); + assertEquals(CLASS_NAME, block.getClassName()); + assertEquals(METHOD_NAME, block.getMethodName()); + } + + @Test + void exitBlockProvidesDescriptiveNameAndToString() { + ExitBlock block = new ExitBlock(CLASS_NAME, METHOD_NAME); + + String expected = "ExitBlock for method " + METHOD_NAME; + assertEquals(expected, block.getName()); + assertEquals(expected, block.toString()); + } +} + diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/RawControlFlowGraphTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/RawControlFlowGraphTest.java new file mode 100644 index 0000000000..efbbdfa5f3 --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/RawControlFlowGraphTest.java @@ -0,0 +1,159 @@ +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.Arrays; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +class RawControlFlowGraphTest { + + private static final ClassLoader TEST_LOADER = RawControlFlowGraphTest.class.getClassLoader(); + private static final String CLASS_NAME = "com.example.RawCFGFixture"; + private static final String METHOD_NAME = "sample()V"; + + @AfterEach + void resetPools() { + BranchPool.resetForTesting(TEST_LOADER); + } + + @Test + void addEdgeCreatesControlDependenciesForBranchEdges() { + TestRawCFG cfg = new TestRawCFG(); + + BytecodeInstruction branch = branchInstruction(10); + BytecodeInstruction jumpTarget = instruction(30, Opcodes.NOP); + BytecodeInstruction fallThrough = instruction(11, Opcodes.NOP); + + cfg.addVertex(branch); + cfg.addVertex(jumpTarget); + cfg.addVertex(fallThrough); + + ControlFlowEdge jumpEdge = cfg.connect(branch, jumpTarget); + ControlFlowEdge fallEdge = cfg.connect(branch, fallThrough); + + assertTrue(jumpEdge.hasControlDependency()); + assertTrue(jumpEdge.getControlDependency().getBranchExpressionValue()); + assertFalse(fallEdge.getControlDependency().getBranchExpressionValue()); + assertSame(branch.toBranch(), jumpEdge.getBranchInstruction()); + } + + @Test + void determineEntryAndExitPointsFallbackToMinAndMaxWhenNecessary() { + TestRawCFG cfg = new TestRawCFG(); + BytecodeInstruction first = instruction(0, Opcodes.NOP); + BytecodeInstruction second = instruction(1, Opcodes.NOP); + + cfg.addVertex(first); + cfg.addVertex(second); + + cfg.connect(first, second); + cfg.connect(second, first); // cycle -> no node with zero in-degree/out-degree + + assertSame(first, cfg.determineEntryPoint()); + + Set exits = cfg.determineExitPoints(); + assertEquals(1, exits.size()); + assertTrue(exits.contains(second)); + } + + @Test + void determineBranchesAndJoinsReflectOutAndInDegrees() { + TestRawCFG cfg = new TestRawCFG(); + BytecodeInstruction entry = instruction(0, Opcodes.NOP); + BytecodeInstruction branch = branchInstruction(5); + BytecodeInstruction truePath = instruction(6, Opcodes.NOP); + BytecodeInstruction falsePath = instruction(7, Opcodes.NOP); + BytecodeInstruction join = instruction(8, Opcodes.NOP); + + Arrays.asList(entry, branch, truePath, falsePath, join) + .forEach(cfg::addVertex); + + cfg.connect(entry, branch); + cfg.connect(branch, truePath); + cfg.connect(branch, falsePath); + cfg.connect(truePath, join); + cfg.connect(falsePath, join); + + assertEquals( + java.util.Collections.singleton(branch), + cfg.determineBranches() + ); + + assertEquals( + java.util.Collections.singleton(join), + cfg.determineJoins() + ); + } + + @Test + void determineBasicBlockMergesLinearSequence() { + TestRawCFG cfg = new TestRawCFG(); + BytecodeInstruction first = instruction(0, Opcodes.NOP); + BytecodeInstruction middle = instruction(1, Opcodes.NOP); + BytecodeInstruction last = instruction(2, Opcodes.RETURN); + + cfg.addVertex(first); + cfg.addVertex(middle); + cfg.addVertex(last); + + cfg.connect(first, middle); + cfg.connect(middle, last); + + BasicBlock block = cfg.determineBasicBlockFor(middle); + + assertEquals(first, block.getFirstInstruction()); + assertEquals(last, block.getLastInstruction()); + java.util.List collected = new java.util.ArrayList<>(); + block.forEach(collected::add); + assertEquals( + Arrays.asList(first, middle, last), + collected + ); + } + + private static BytecodeInstruction instruction(int instructionId, int opcode) { + BytecodeInstruction instruction = new BytecodeInstruction( + TEST_LOADER, + CLASS_NAME, + METHOD_NAME, + instructionId, + instructionId, + new InsnNode(opcode) + ); + instruction.setLineNumber(100 + instructionId); + return instruction; + } + + private static BytecodeInstruction branchInstruction(int instructionId) { + BytecodeInstruction instruction = new BytecodeInstruction( + TEST_LOADER, + CLASS_NAME, + METHOD_NAME, + instructionId, + instructionId, + new JumpInsnNode(Opcodes.IFEQ, new LabelNode()) + ); + instruction.setLineNumber(200 + instructionId); + BranchPool.getInstance(TEST_LOADER).registerAsBranch(instruction); + return instruction; + } + + private static final class TestRawCFG extends RawControlFlowGraph { + private TestRawCFG() { + super(TEST_LOADER, CLASS_NAME, METHOD_NAME, Opcodes.ACC_PUBLIC); + } + + private ControlFlowEdge connect(BytecodeInstruction src, BytecodeInstruction dst) { + return super.addEdge(src, dst, false); + } + } +} + diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchPoolTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchPoolTest.java new file mode 100644 index 0000000000..1891790ec7 --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchPoolTest.java @@ -0,0 +1,117 @@ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch; + +import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction; +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 static org.junit.jupiter.api.Assertions.*; + +class BranchPoolTest { + + private static final ClassLoader TEST_LOADER = BranchPoolTest.class.getClassLoader(); + private static final String CLASS_NAME = "com.example.BranchPoolFixture"; + private static final String METHOD_NAME = "sample()V"; + + @AfterEach + void resetPool() { + BranchPool.resetForTesting(TEST_LOADER); + } + + @Test + void registerAsBranchRejectsNonBranchInstructions() { + BranchPool pool = BranchPool.getInstance(TEST_LOADER); + BytecodeInstruction notBranch = nonBranchInstruction(1); + + assertThrows(IllegalArgumentException.class, () -> pool.registerAsBranch(notBranch)); + } + + @Test + void registerAsBranchAssignsDeterministicIdsAndObjectiveNamesCorrectly() { + BranchPool pool = BranchPool.getInstance(TEST_LOADER); + BytecodeInstruction first = branchInstruction(2, 150); + BytecodeInstruction second = branchInstruction(3, 150); // same line to exercise ordinal handling + + pool.registerAsBranch(first); + pool.registerAsBranch(second); + + Branch firstBranch = pool.getBranchForInstruction(first); + Branch secondBranch = pool.getBranchForInstruction(second); + + assertEquals(1, firstBranch.getActualBranchId()); + assertEquals(2, secondBranch.getActualBranchId()); + + assertEquals("Branch_at_com.example.BranchPoolFixture_at_line_00150_position_0_trueBranch_153", + firstBranch.getThenObjectiveId()); + assertEquals("Branch_at_com.example.BranchPoolFixture_at_line_00150_position_0_falseBranch_153", + firstBranch.getElseObjectiveId()); + assertEquals("Branch_at_com.example.BranchPoolFixture_at_line_00150_position_1_trueBranch_153", + secondBranch.getThenObjectiveId()); + assertEquals("Branch_at_com.example.BranchPoolFixture_at_line_00150_position_1_falseBranch_153", + secondBranch.getElseObjectiveId()); + } + + @Test + void isKnownAsBranchReflectsRegistrationState() { + BranchPool pool = BranchPool.getInstance(TEST_LOADER); + BytecodeInstruction instruction = branchInstruction(4, 200); + + assertFalse(pool.isKnownAsBranch(instruction)); + + pool.registerAsBranch(instruction); + + assertTrue(pool.isKnownAsBranch(instruction)); + } + + @Test + void getBranchForInstructionRejectsUnknownBranches() { + BranchPool pool = BranchPool.getInstance(TEST_LOADER); + BytecodeInstruction instruction = branchInstruction(5, 220); + + assertThrows(IllegalArgumentException.class, () -> pool.getBranchForInstruction(instruction)); + } + + @Test + void registeringSameInstructionTwiceReturnsExistingBranch() { + BranchPool pool = BranchPool.getInstance(TEST_LOADER); + BytecodeInstruction instruction = branchInstruction(6, 230); + + pool.registerAsBranch(instruction); + Branch first = pool.getBranchForInstruction(instruction); + + pool.registerAsBranch(instruction); + Branch second = pool.getBranchForInstruction(instruction); + + assertSame(first, second); + } + + private static BytecodeInstruction branchInstruction(int instructionId, int lineNumber) { + BytecodeInstruction instruction = new BytecodeInstruction( + TEST_LOADER, + CLASS_NAME, + METHOD_NAME, + instructionId, + instructionId, + new JumpInsnNode(Opcodes.IFEQ, new LabelNode()) + ); + instruction.setLineNumber(lineNumber); + return instruction; + } + + private static BytecodeInstruction nonBranchInstruction(int instructionId) { + BytecodeInstruction instruction = new BytecodeInstruction( + TEST_LOADER, + CLASS_NAME, + METHOD_NAME, + instructionId, + instructionId, + new InsnNode(Opcodes.NOP) + ); + instruction.setLineNumber(50 + instructionId); + return instruction; + } +} + diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchTest.java new file mode 100644 index 0000000000..18dfebbf24 --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchTest.java @@ -0,0 +1,105 @@ +package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch; + +import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction; +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 static org.junit.jupiter.api.Assertions.*; + +class BranchTest { + + private static final ClassLoader TEST_LOADER = BranchTest.class.getClassLoader(); + private static final String CLASS_NAME = "com.example.BranchFixture"; + private static final String METHOD_NAME = "sample()V"; + + @Test + void constructorRejectsNonBranchInstructions() { + BytecodeInstruction notBranch = nonBranchInstruction(1); + assertThrows(IllegalArgumentException.class, () -> new Branch(notBranch, 1)); + } + + @Test + void constructorRejectsNonPositiveIds() { + BytecodeInstruction instruction = branchInstruction(2); + assertThrows(IllegalStateException.class, () -> new Branch(instruction, 0)); + } + + @Test + void gettersExposeInstructionMetadata() { + BytecodeInstruction instruction = branchInstruction(3); + Branch branch = new Branch(instruction, 7); + + assertEquals(7, branch.getActualBranchId()); + assertSame(instruction, branch.getInstruction()); + assertEquals(CLASS_NAME, branch.getClassName()); + assertEquals(METHOD_NAME, branch.getMethodName()); + } + + @Test + void objectiveIdsAreStoredAndReflectedInToString() { + BytecodeInstruction instruction = branchInstruction(4); + Branch branch = new Branch(instruction, 9); + + assertNull(branch.getThenObjectiveId()); + assertNull(branch.getElseObjectiveId()); + + branch.setObjectiveIds("thenId", "elseId"); + + assertEquals("thenId", branch.getThenObjectiveId()); + assertEquals("elseId", branch.getElseObjectiveId()); + + String representation = branch.toString(); + assertTrue(representation.contains("I4")); + assertTrue(representation.contains("Branch 9")); + assertTrue(representation.contains("IFEQ")); + assertTrue(representation.contains("L104")); + assertTrue(representation.contains("[thenId, elseId]")); + } + + @Test + void equalsAndHashCodeDependOnInstructionAndId() { + Branch reference = new Branch(branchInstruction(5), 11); + Branch sameValues = new Branch(branchInstruction(5), 11); + Branch differentId = new Branch(branchInstruction(5), 12); + Branch differentInstruction = new Branch(branchInstruction(6), 11); + + assertEquals(reference, sameValues); + assertEquals(reference.hashCode(), sameValues.hashCode()); + + assertNotEquals(reference, differentId); + assertNotEquals(reference, differentInstruction); + assertNotEquals(reference, null); + assertNotEquals(reference, "branch"); + } + + private static BytecodeInstruction branchInstruction(int instructionId) { + BytecodeInstruction instruction = new BytecodeInstruction( + TEST_LOADER, + CLASS_NAME, + METHOD_NAME, + instructionId, + instructionId, + new JumpInsnNode(Opcodes.IFEQ, new LabelNode()) + ); + instruction.setLineNumber(100 + instructionId); + return instruction; + } + + private static BytecodeInstruction nonBranchInstruction(int instructionId) { + BytecodeInstruction instruction = new BytecodeInstruction( + TEST_LOADER, + CLASS_NAME, + METHOD_NAME, + instructionId, + instructionId, + new InsnNode(Opcodes.NOP) + ); + instruction.setLineNumber(10 + instructionId); + return instruction; + } +} + + From 9f184afbf8b2d98ee576859ccfdb6434fd3312a8 Mon Sep 17 00:00:00 2001 From: francastagna Date: Fri, 5 Dec 2025 11:33:31 -0300 Subject: [PATCH 13/21] enablegraphs and writecfg is false by default --- .../client/java/instrumentation/dynamosa/DynamosaConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index 91c4f2e71d..1010449e9c 100644 --- 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 @@ -10,8 +10,8 @@ */ public class DynamosaConfig { - private static volatile boolean enableGraphs = true; - private static volatile boolean writeCfg = true; + private static volatile boolean enableGraphs = false; + private static volatile boolean writeCfg = false; public static boolean isGraphsEnabled() { From 7f63038c41d49658c5659cafa6946dc5c6df5767 Mon Sep 17 00:00:00 2001 From: francastagna Date: Fri, 5 Dec 2025 11:51:29 -0300 Subject: [PATCH 14/21] fixing stuff --- .../dynamosa/graphs/cfg/ControlDependency.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index a6d567f048..7a44ead7be 100755 --- 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 @@ -16,7 +16,7 @@ public class ControlDependency { /** *

Constructor for ControlDependency.

* - * @param branch a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.Branch} object. + * @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) { @@ -31,7 +31,7 @@ public ControlDependency(Branch branch, boolean branchExpressionValue) { /** *

Getter for the field branch.

* - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.Branch} object. + * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch} object. */ public Branch getBranch() { return branch; From 38b68f83bfb267be8e858ca8988374ed25b25da2 Mon Sep 17 00:00:00 2001 From: francastagna Date: Fri, 5 Dec 2025 12:54:49 -0300 Subject: [PATCH 15/21] update docs/options.md --- docs/options.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/options.md b/docs/options.md index a3f1e022c8..e5c545abde 100644 --- a/docs/options.md +++ b/docs/options.md @@ -68,7 +68,7 @@ There are 3 types of options: |`addPreDefinedTests`| __Boolean__. Add predefined tests at the end of the search. An example is a test to fetch the schema of RESTful APIs. *Default value*: `true`.| |`addTestComments`| __Boolean__. Add summary comments on each test. *Default value*: `true`.| |`advancedBlackBoxCoverage`| __Boolean__. Apply more advanced coverage criteria for black-box testing. This can result in larger generated test suites. *Default value*: `true`.| -|`algorithm`| __Enum__. The algorithm used to generate test cases. The default depends on whether black-box or white-box testing is done. *Valid values*: `DEFAULT, SMARTS, MIO, RANDOM, WTS, MOSA, RW, DYNAMOSA, StandardGA, MonotonicGA, SteadyStateGA, BreederGA, CellularGA, OnePlusLambdaLambdaGA, MuLambdaEA, MuPlusLambdaEA, LIPS`. *Default value*: `DEFAULT`.| +|`algorithm`| __Enum__. The algorithm used to generate test cases. The default depends on whether black-box or white-box testing is done. *Valid values*: `DEFAULT, SMARTS, MIO, RANDOM, WTS, MOSA, RW, DYNAMOSA, StandardGA, MonotonicGA, SteadyStateGA, BreederGA, CellularGA, OnePlusLambdaLambdaGA, MuLambdaEA, MuPlusLambdaEA, LIPS, CRO`. *Default value*: `DEFAULT`.| |`allowInvalidData`| __Boolean__. When generating data, allow in some cases to use invalid values on purpose. *Default value*: `true`.| |`appendToStatisticsFile`| __Boolean__. Whether should add to an existing statistics file, instead of replacing it. *Default value*: `false`.| |`archiveAfterMutationFile`| __String__. Specify a path to save archive after each mutation during search, only useful for debugging. *DEBUG option*. *Default value*: `archive.csv`.| @@ -83,6 +83,11 @@ There are 3 types of options: |`coveredTargetSortedBy`| __Enum__. Specify a format to organize the covered targets by the search. *Valid values*: `NAME, TEST`. *Default value*: `NAME`.| |`createConfigPathIfMissing`| __Boolean__. If there is no configuration file, create a default template at given configPath location. However this is done only on the 'default' location. If you change 'configPath', no new file will be created. *Default value*: `true`.| |`createTests`| __Boolean__. Specify if test classes should be created as output of the tool. Usually, you would put it to 'false' only when debugging EvoMaster itself. *Default value*: `true`.| +|`croDecompositionThreshold`| __Int__. CRO: Decomposition threshold d_t (min number of collisions before decomposition). *Constraints*: `min=0.0`. *Default value*: `500`.| +|`croInitialKineticEnergy`| __Double__. CRO: Initial kinetic energy assigned to each molecule. *Constraints*: `min=0.0`. *Default value*: `1000.0`.| +|`croKineticEnergyLossRate`| __Double__. CRO: Kinetic energy loss rate k_r (lower bound of retained fraction after on-wall). *Constraints*: `probability 0.0-1.0`. *Default value*: `0.2`.| +|`croMolecularCollisionRate`| __Double__. CRO: Molecular collision rate c_r (probability of binary reactions). *Constraints*: `probability 0.0-1.0`. *Default value*: `0.2`.| +|`croSynthesisThreshold`| __Double__. CRO: Synthesis KE threshold s_t (molecule can synthesize if KE ≤ s_t). *Constraints*: `min=0.0`. *Default value*: `10.0`.| |`customNaming`| __Boolean__. Enable custom naming and sorting criteria. *Default value*: `true`.| |`d`| __Double__. When weight-based mutation rate is enabled, specify a percentage of calculating mutation rate based on a number of candidate genes to mutate. For instance, d = 1.0 means that the mutation rate fully depends on a number of candidate genes to mutate, and d = 0.0 means that the mutation rate fully depends on weights of candidates genes to mutate. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.8`.| |`dependencyFile`| __String__. Specify a file that saves derived dependencies. *DEBUG option*. *Default value*: `dependencies.csv`.| @@ -319,3 +324,4 @@ There are 3 types of options: |`vulnerableInputClassificationStrategy`| __Enum__. Strategy to classify inputs for potential vulnerability classes related to an REST endpoint. *Valid values*: `MANUAL, LLM`. *Default value*: `MANUAL`.| |`wbProbabilityUseDataPool`| __Double__. Specify the probability of using the data pool when sampling test cases. This is for white-box (wb) mode. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.2`.| |`writeSnapshotTestsIntervalInSeconds`| __Int__. The size (in seconds) of the interval that the snapshots will be printed, if enabled. *Default value*: `3600`.| +|`xss`| __Boolean__. To apply XSS detection as part of security testing. *Default value*: `false`.| From a44bc0491336594b45317fe8311155be6d52d39d Mon Sep 17 00:00:00 2001 From: francastagna Date: Mon, 15 Dec 2025 12:25:54 -0300 Subject: [PATCH 16/21] Address Andrea's initial feedback on PR #1399 --- .gitignore | 1 + client-java/controller-api/pom.xml | 4 - .../api/dto/BranchObjectiveDto.java | 13 + .../api/dto/ControlDependenceGraphDto.java | 20 ++ .../controller/api/dto/DependencyEdgeDto.java | 13 + .../java/controller/api/dto/SutRunDto.java | 4 +- .../controller/api/dto/TestResultsDto.java | 6 +- .../controller/EmbeddedSutController.java | 16 +- .../controller/ExternalSutController.java | 20 +- .../controller/internal/EMController.java | 16 +- .../controller/internal/SutController.java | 19 +- .../shared/dto/ControlDependenceGraphDto.java | 200 --------------- client-java/instrumentation/pom.xml | 4 + .../InstrumentationController.java | 14 +- .../java/instrumentation/Instrumentator.java | 6 +- .../graphs/cdg/ControlDependenceGraph.java | 219 ----------------- .../external/AgentController.java | 30 +-- .../instrumentation/external/Command.java | 4 +- ...a => ControlDependenceGraphConfigDto.java} | 5 +- ...ot.java => ControlDependenceSnapshot.java} | 7 +- .../external/ServerController.java | 32 +-- .../{dynamosa => graphs}/AnnotatedLabel.java | 5 +- .../AnnotatedMethodNode.java | 7 +- .../ControlDependenceGraphConfig.java} | 11 +- .../{dynamosa => }/graphs/EvoMasterGraph.java | 4 +- .../{dynamosa => }/graphs/GraphPool.java | 64 ++--- .../graphs/cdg/ControlDependenceGraph.java | 232 ++++++++++++++++++ .../graphs/cdg/DominatorNode.java | 26 +- .../graphs/cdg/DominatorTree.java | 66 ++--- .../graphs/cfg/ActualControlFlowGraph.java | 97 ++++---- .../{dynamosa => }/graphs/cfg/BasicBlock.java | 18 +- .../graphs/cfg/BytecodeInstruction.java | 28 +-- .../graphs/cfg/BytecodeInstructionPool.java | 46 +--- .../graphs/cfg/CFGGenerator.java | 10 +- .../graphs/cfg/ControlDependency.java | 10 +- .../graphs/cfg/ControlFlowEdge.java | 14 +- .../graphs/cfg/ControlFlowGraph.java | 13 +- .../{dynamosa => }/graphs/cfg/EntryBlock.java | 4 +- .../{dynamosa => }/graphs/cfg/ExitBlock.java | 4 +- .../graphs/cfg/RawControlFlowGraph.java | 66 +++-- .../graphs/cfg/branch/Branch.java | 10 +- .../graphs/cfg/branch/BranchPool.java | 14 +- .../visitor/CFGClassVisitor.java | 4 +- .../visitor/CFGMethodVisitor.java | 17 +- .../{dynamosa => }/graphs/GraphPoolTest.java | 8 +- .../cdg/ControlDependenceGraphTest.java | 22 +- .../graphs/cdg/DominatorNodeTest.java | 24 +- .../cfg/ActualControlFlowGraphTest.java | 4 +- .../graphs/cfg/BasicBlockTest.java | 2 +- .../cfg/BytecodeInstructionPoolTest.java | 4 +- .../graphs/cfg/BytecodeInstructionTest.java | 6 +- .../graphs/cfg/CFGGeneratorTest.java | 6 +- .../graphs/cfg/ControlDependencyTest.java | 4 +- .../graphs/cfg/ControlFlowEdgeTest.java | 4 +- .../graphs/cfg/EntryBlockTest.java | 2 +- .../graphs/cfg/ExitBlockTest.java | 2 +- .../graphs/cfg/RawControlFlowGraphTest.java | 4 +- .../graphs/cfg/branch/BranchPoolTest.java | 4 +- .../graphs/cfg/branch/BranchTest.java | 4 +- .../core/remote/service/RemoteController.kt | 4 +- .../service/RemoteControllerImplementation.kt | 24 +- .../algorithms/BranchDependencyGraph.kt | 2 +- .../search/algorithms/DynamosaAlgorithm.kt | 6 +- .../search/algorithms/MulticriteriaManager.kt | 2 +- .../algorithms/BranchDependencyGraphTest.kt | 8 +- .../algorithms/DynaMosaAlgorithmTest.kt | 4 +- .../algorithms/MulticriteriaManagerTest.kt | 12 +- docs/reused_code.md | 28 ++- 68 files changed, 719 insertions(+), 894 deletions(-) create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/BranchObjectiveDto.java create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/ControlDependenceGraphDto.java create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/DependencyEdgeDto.java delete mode 100644 client-java/instrumentation-shared/src/main/java/org/evomaster/client/java/instrumentation/shared/dto/ControlDependenceGraphDto.java delete mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/ControlDependenceGraph.java rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/{DynamosaConfigDto.java => ControlDependenceGraphConfigDto.java} (67%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/{DynamosaControlDependenceSnapshot.java => ControlDependenceSnapshot.java} (76%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => graphs}/AnnotatedLabel.java (90%) mode change 100755 => 100644 rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => graphs}/AnnotatedMethodNode.java (88%) mode change 100755 => 100644 rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa/DynamosaConfig.java => graphs/ControlDependenceGraphConfig.java} (80%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/EvoMasterGraph.java (99%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/GraphPool.java (87%) create mode 100755 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cdg/ControlDependenceGraph.java rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cdg/DominatorNode.java (76%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cdg/DominatorTree.java (80%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/ActualControlFlowGraph.java (86%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/BasicBlock.java (94%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/BytecodeInstruction.java (96%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/BytecodeInstructionPool.java (85%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/CFGGenerator.java (96%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/ControlDependency.java (85%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/ControlFlowEdge.java (85%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/ControlFlowGraph.java (92%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/EntryBlock.java (86%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/ExitBlock.java (86%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/RawControlFlowGraph.java (83%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/branch/Branch.java (93%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/branch/BranchPool.java (93%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => graphs}/visitor/CFGClassVisitor.java (92%) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/{dynamosa => graphs}/visitor/CFGMethodVisitor.java (85%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/GraphPoolTest.java (94%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cdg/ControlDependenceGraphTest.java (91%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cdg/DominatorNodeTest.java (80%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/ActualControlFlowGraphTest.java (97%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/BasicBlockTest.java (98%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/BytecodeInstructionPoolTest.java (95%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/BytecodeInstructionTest.java (95%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/CFGGeneratorTest.java (94%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/ControlDependencyTest.java (94%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/ControlFlowEdgeTest.java (94%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/EntryBlockTest.java (92%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/ExitBlockTest.java (92%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/RawControlFlowGraphTest.java (97%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/branch/BranchPoolTest.java (96%) rename client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/{dynamosa => }/graphs/cfg/branch/BranchTest.java (95%) diff --git a/.gitignore b/.gitignore index 7161fe28e4..80116611fe 100644 --- a/.gitignore +++ b/.gitignore @@ -159,3 +159,4 @@ client-java/sql-dto/target client-java/distance-heuristics/target client-java/test-old-libraries/target core-tests/e2e-tests/spring/spring-rest-mysql/target +client-java/instrumentation/evosuite-graphs/ diff --git a/client-java/controller-api/pom.xml b/client-java/controller-api/pom.xml index 3120ab2676..c7fab551a0 100644 --- a/client-java/controller-api/pom.xml +++ b/client-java/controller-api/pom.xml @@ -22,10 +22,6 @@ org.evomaster evomaster-client-java-sql-dto
- - org.evomaster - evomaster-client-java-instrumentation-shared - org.evomaster evomaster-test-utils-java diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/BranchObjectiveDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/BranchObjectiveDto.java new file mode 100644 index 0000000000..d689f7d4b2 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/BranchObjectiveDto.java @@ -0,0 +1,13 @@ +package org.evomaster.client.java.controller.api.dto; + +import java.io.Serializable; + +/** + * Descriptor and numeric id for a branch objective (true or false outcome). + */ +public class BranchObjectiveDto implements Serializable { + + public int id; + public String descriptiveId; +} + diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/ControlDependenceGraphDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/ControlDependenceGraphDto.java new file mode 100644 index 0000000000..3e139af426 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/ControlDependenceGraphDto.java @@ -0,0 +1,20 @@ +package org.evomaster.client.java.controller.api.dto; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * Serializable representation of the control-dependence information of a method. + * Contains only the data needed by EvoMaster core: branch objective identifiers, + * the subset that are roots, and the parent-child relationships between them. + */ +public class ControlDependenceGraphDto implements Serializable { + + public String className; + public String methodName; + public List objectives = new ArrayList<>(); + public List rootObjectiveIds = new ArrayList<>(); + public List edges = new ArrayList<>(); +} + diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/DependencyEdgeDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/DependencyEdgeDto.java new file mode 100644 index 0000000000..1e30b52ff4 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/DependencyEdgeDto.java @@ -0,0 +1,13 @@ +package org.evomaster.client.java.controller.api.dto; + +import java.io.Serializable; + +/** + * Directed edge describing that parentObjectiveId must be covered before childObjectiveId. + */ +public class DependencyEdgeDto implements Serializable { + + public int parentObjectiveId; + public int childObjectiveId; +} + diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java index e2039bc8f3..f585fcb760 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java @@ -46,9 +46,9 @@ public class SutRunDto { public String methodReplacementCategories; /** - * Whether to enable Dynamosa graphs generation in the Java agent + * Whether to enable Control Dependence Graph generation in the Java agent */ - public Boolean enableDynamosaGraphs; + public Boolean enableControlDependenceGraphs; /** * Whether to write generated graphs (DOT/PNGs) to disk on the agent side diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/TestResultsDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/TestResultsDto.java index 8f8a6a1efb..4c6be52736 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/TestResultsDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/TestResultsDto.java @@ -1,7 +1,5 @@ package org.evomaster.client.java.controller.api.dto; -import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto; - import java.util.ArrayList; import java.util.List; @@ -18,7 +16,7 @@ public class TestResultsDto { public List extraHeuristics = new ArrayList<>(); /** - * Incremental DynaMOSA control-dependence graphs discovered since the last handshake. + * Incremental control-dependence graphs discovered since the last handshake. */ - public List dynamosaCdgs = new ArrayList<>(); + public List controlDependenceGraphs = new ArrayList<>(); } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java index 22d3606268..02c79dd56a 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java @@ -6,8 +6,8 @@ import org.evomaster.client.java.controller.api.dto.problem.rpc.ScheduleTaskInvocationDto; import org.evomaster.client.java.controller.internal.SutController; import org.evomaster.client.java.instrumentation.*; -import org.evomaster.client.java.instrumentation.external.DynamosaControlDependenceSnapshot; -import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto; +import org.evomaster.client.java.instrumentation.external.ControlDependenceSnapshot; +import org.evomaster.client.java.controller.api.dto.ControlDependenceGraphDto; import org.evomaster.client.java.instrumentation.object.ClassToSchema; import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; import org.evomaster.client.java.instrumentation.staticstate.ObjectiveRecorder; @@ -33,7 +33,7 @@ */ public abstract class EmbeddedSutController extends SutController { - private int dynamosaCdgIndex = 0; + private int controlDependenceGraphIndex = 0; @Override public final void setupForGeneratedTest(){ @@ -144,15 +144,15 @@ public final void bootingSut(boolean bootingSut) { } @Override - public List getDynamosaControlDependenceGraphs() { - DynamosaControlDependenceSnapshot snapshot = InstrumentationController.getControlDependenceSnapshot(dynamosaCdgIndex); - dynamosaCdgIndex = snapshot.getNextIndex(); + public List getControlDependenceGraphs() { + ControlDependenceSnapshot snapshot = InstrumentationController.getControlDependenceSnapshot(controlDependenceGraphIndex); + controlDependenceGraphIndex = snapshot.getNextIndex(); return snapshot.getGraphs(); } @Override - public void setDynamosaGraphsEnabled(boolean enableGraphs) { - InstrumentationController.setDynamosaGraphsEnabled(enableGraphs); + public void setControlDependenceGraphsEnabled(boolean enableGraphs) { + InstrumentationController.setControlDependenceGraphsEnabled(enableGraphs); } @Override diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java index e077b78222..deb4b8af94 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java @@ -12,8 +12,8 @@ import org.evomaster.client.java.controller.internal.SutController; import org.evomaster.client.java.instrumentation.external.JarAgentLocator; import org.evomaster.client.java.instrumentation.external.ServerController; -import org.evomaster.client.java.instrumentation.external.DynamosaConfigDto; -import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto; +import org.evomaster.client.java.instrumentation.external.ControlDependenceGraphConfigDto; +import org.evomaster.client.java.controller.api.dto.ControlDependenceGraphDto; import java.io.BufferedReader; import java.io.IOException; @@ -450,11 +450,11 @@ public BootTimeInfoDto getBootTimeInfoDto() { } @Override - public List getDynamosaControlDependenceGraphs() { + public List getControlDependenceGraphs() { if (!isInstrumentationActivated()){ return Collections.emptyList(); } - List graphs = serverController.getDynamosaControlDependenceGraphs(); + List graphs = serverController.getControlDependenceGraphs(); return graphs != null ? graphs : Collections.emptyList(); } @@ -534,14 +534,14 @@ public final void setExecutingInitMongo(boolean executingInitMongo) { } /** - * Send Dynamosa configuration to the Java Agent. + * Send CDG configuration to the Java Agent. */ - public final void setDynamosaGraphsEnabled(boolean enableGraphs) { + public final void setControlDependenceGraphsEnabled(boolean enableGraphs) { checkInstrumentation(); - DynamosaConfigDto dto = new DynamosaConfigDto(); + ControlDependenceGraphConfigDto dto = new ControlDependenceGraphConfigDto(); dto.enableGraphs = enableGraphs; dto.writeCfg = null; - serverController.setDynamosaConfig(dto); + serverController.setControlDependenceGraphConfig(dto); } /** @@ -549,10 +549,10 @@ public final void setDynamosaGraphsEnabled(boolean enableGraphs) { */ public final void setWriteCfgEnabled(boolean writeCfg) { checkInstrumentation(); - DynamosaConfigDto dto = new DynamosaConfigDto(); + ControlDependenceGraphConfigDto dto = new ControlDependenceGraphConfigDto(); dto.enableGraphs = null; dto.writeCfg = writeCfg; - serverController.setDynamosaConfig(dto); + serverController.setControlDependenceGraphConfig(dto); } @Override diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java index 6a474f5f75..7a3beaf93d 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java @@ -19,7 +19,7 @@ import org.evomaster.client.java.sql.SqlScriptRunner; import org.evomaster.client.java.controller.problem.rpc.schema.LocalAuthSetupSchema; import org.evomaster.client.java.instrumentation.*; -import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto; +import org.evomaster.client.java.controller.api.dto.ControlDependenceGraphDto; import org.evomaster.client.java.instrumentation.shared.StringSpecializationInfo; import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; import org.evomaster.client.java.utils.SimpleLogger; @@ -377,9 +377,9 @@ public Response runSut(SutRunDto dto, @Context HttpServletRequest httpServletReq if (!noKillSwitch(() -> sutController.isSutRunning())) { noKillSwitch(() -> sutController.bootingSut(true)); baseUrlOfSUT = noKillSwitch(() -> sutController.startSut()); - // Configure Dynamosa graphs on the agent, if requested by core - Boolean enableGraphs = dto.enableDynamosaGraphs; - if (enableGraphs != null) noKillSwitch(() -> sutController.setDynamosaGraphsEnabled(enableGraphs)); + // Configure CDG generation on the agent, if requested by core + Boolean enableGraphs = dto.enableControlDependenceGraphs; + if (enableGraphs != null) noKillSwitch(() -> sutController.setControlDependenceGraphsEnabled(enableGraphs)); Boolean writeCfg = dto.writeCfg; if (writeCfg != null) noKillSwitch(() -> sutController.setWriteCfgEnabled(writeCfg)); noKillSwitch(() -> sutController.bootingSut(false)); @@ -594,10 +594,10 @@ public Response getTestResults( return Response.status(500).entity(WrappedResponseDto.withError(msg)).build(); } - List dynamosaCdgs = - noKillSwitch(() -> sutController.getDynamosaControlDependenceGraphs()); - if (dynamosaCdgs != null && !dynamosaCdgs.isEmpty()) { - dto.dynamosaCdgs.addAll(dynamosaCdgs); + List cdgs = + noKillSwitch(() -> sutController.getControlDependenceGraphs()); + if (cdgs != null && !cdgs.isEmpty()) { + dto.controlDependenceGraphs.addAll(cdgs); } // } // else { diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index bfa81cb1e7..9f4750e4ea 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -49,7 +49,7 @@ import org.evomaster.client.java.instrumentation.AdditionalInfo; import org.evomaster.client.java.instrumentation.BootTimeObjectiveInfo; import org.evomaster.client.java.instrumentation.TargetInfo; -import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto; +import org.evomaster.client.java.controller.api.dto.ControlDependenceGraphDto; import org.evomaster.client.java.instrumentation.staticstate.UnitsInfoRecorder; import org.evomaster.client.java.utils.SimpleLogger; import org.glassfish.jersey.jackson.JacksonFeature; @@ -1566,21 +1566,14 @@ public abstract List getTargetInfos(Collection ids, public abstract void setExecutingAction(boolean executingAction); /** - * Enable/disable Dynamosa graphs generation in the Java agent. - * Default no-op; external controllers can override to send to agent. + * Enable/disable Control Dependence Graph generation in the Java agent. */ - public void setDynamosaGraphsEnabled(boolean enableGraphs){ - // no-op by default - } + public abstract void setControlDependenceGraphsEnabled(boolean enableGraphs); /** * Enable/disable writing graphs to disk in the Java agent. - * Default no-op; external controllers can override to send to agent. */ - public void setWriteCfgEnabled(boolean writeCfg){ - // no-op by default - } - + public abstract void setWriteCfgEnabled(boolean writeCfg); /** * specify whether the SUT is booting (ie starting up), or not. @@ -1593,9 +1586,7 @@ public void setWriteCfgEnabled(boolean writeCfg){ public abstract BootTimeInfoDto getBootTimeInfoDto(); - public List getDynamosaControlDependenceGraphs(){ - return Collections.emptyList(); - } + public abstract List getControlDependenceGraphs(); protected BootTimeInfoDto getBootTimeInfoDto(BootTimeObjectiveInfo info){ if (info == null) diff --git a/client-java/instrumentation-shared/src/main/java/org/evomaster/client/java/instrumentation/shared/dto/ControlDependenceGraphDto.java b/client-java/instrumentation-shared/src/main/java/org/evomaster/client/java/instrumentation/shared/dto/ControlDependenceGraphDto.java deleted file mode 100644 index 2e14e59966..0000000000 --- a/client-java/instrumentation-shared/src/main/java/org/evomaster/client/java/instrumentation/shared/dto/ControlDependenceGraphDto.java +++ /dev/null @@ -1,200 +0,0 @@ -package org.evomaster.client.java.instrumentation.shared.dto; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -/** - * Serializable representation of the control-dependence information of a method. - * Contains only the data needed by EvoMaster core: branch objective identifiers, - * the subset that are roots, and the parent→child relationships between them. - */ -public class ControlDependenceGraphDto implements Serializable { - - private static final long serialVersionUID = -1703430508792441369L; - - private String className; - private String methodName; - private List objectives = new ArrayList<>(); - private List rootObjectiveIds = new ArrayList<>(); - private List edges = new ArrayList<>(); - - public ControlDependenceGraphDto() { - } - - public ControlDependenceGraphDto(String className, - String methodName, - List objectives, - List rootObjectiveIds, - List edges) { - this.className = className; - this.methodName = methodName; - if (objectives != null) { - this.objectives = new ArrayList<>(objectives); - } - if (rootObjectiveIds != null) { - this.rootObjectiveIds = new ArrayList<>(rootObjectiveIds); - } - if (edges != null) { - this.edges = new ArrayList<>(edges); - } - } - - public String getClassName() { - return className; - } - - public void setClassName(String className) { - this.className = className; - } - - public String getMethodName() { - return methodName; - } - - public void setMethodName(String methodName) { - this.methodName = methodName; - } - - public List getObjectives() { - return Collections.unmodifiableList(objectives); - } - - public void setObjectives(List objectives) { - this.objectives = objectives == null ? new ArrayList<>() : new ArrayList<>(objectives); - } - - public List getRootObjectiveIds() { - return Collections.unmodifiableList(rootObjectiveIds); - } - - public void setRootObjectiveIds(List rootObjectiveIds) { - this.rootObjectiveIds = rootObjectiveIds == null ? new ArrayList<>() : new ArrayList<>(rootObjectiveIds); - } - - public List getEdges() { - return Collections.unmodifiableList(edges); - } - - public void setEdges(List edges) { - this.edges = edges == null ? new ArrayList<>() : new ArrayList<>(edges); - } - - public void addObjective(BranchObjectiveDto objective) { - if (objective != null) { - this.objectives.add(objective); - } - } - - public void addRootObjectiveId(Integer id) { - if (id != null) { - this.rootObjectiveIds.add(id); - } - } - - public void addEdge(DependencyEdgeDto edge) { - if (edge != null) { - this.edges.add(edge); - } - } - - /** - * Descriptor and numeric id for a branch objective (true or false outcome). - */ - public static class BranchObjectiveDto implements Serializable { - - private static final long serialVersionUID = -8122197698885173402L; - - private int id; - private String descriptiveId; - - public BranchObjectiveDto() { - } - - public BranchObjectiveDto(int id, String descriptiveId) { - this.id = id; - this.descriptiveId = descriptiveId; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getDescriptiveId() { - return descriptiveId; - } - - public void setDescriptiveId(String descriptiveId) { - this.descriptiveId = descriptiveId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BranchObjectiveDto that = (BranchObjectiveDto) o; - return id == that.id && Objects.equals(descriptiveId, that.descriptiveId); - } - - @Override - public int hashCode() { - return Objects.hash(id, descriptiveId); - } - } - - /** - * Directed edge describing that {@code parentObjectiveId} must be covered before {@code childObjectiveId}. - */ - public static class DependencyEdgeDto implements Serializable { - - private static final long serialVersionUID = 3167520314102399629L; - - private int parentObjectiveId; - private int childObjectiveId; - - public DependencyEdgeDto() { - } - - public DependencyEdgeDto(int parentObjectiveId, int childObjectiveId) { - this.parentObjectiveId = parentObjectiveId; - this.childObjectiveId = childObjectiveId; - } - - public int getParentObjectiveId() { - return parentObjectiveId; - } - - public void setParentObjectiveId(int parentObjectiveId) { - this.parentObjectiveId = parentObjectiveId; - } - - public int getChildObjectiveId() { - return childObjectiveId; - } - - public void setChildObjectiveId(int childObjectiveId) { - this.childObjectiveId = childObjectiveId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - DependencyEdgeDto that = (DependencyEdgeDto) o; - return parentObjectiveId == that.parentObjectiveId && - childObjectiveId == that.childObjectiveId; - } - - @Override - public int hashCode() { - return Objects.hash(parentObjectiveId, childObjectiveId); - } - } -} - diff --git a/client-java/instrumentation/pom.xml b/client-java/instrumentation/pom.xml index b4b9dc84d0..6cc31fda73 100644 --- a/client-java/instrumentation/pom.xml +++ b/client-java/instrumentation/pom.xml @@ -26,6 +26,10 @@ org.evomaster evomaster-client-java-instrumentation-shared + + org.evomaster + evomaster-client-java-controller-api + org.ow2.asm asm diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java index ccaafe3da5..06864c2e71 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java @@ -1,11 +1,11 @@ package org.evomaster.client.java.instrumentation; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.GraphPool; +import org.evomaster.client.java.instrumentation.graphs.GraphPool; import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; import org.evomaster.client.java.instrumentation.staticstate.ObjectiveRecorder; import org.evomaster.client.java.instrumentation.staticstate.UnitsInfoRecorder; -import org.evomaster.client.java.instrumentation.dynamosa.DynamosaConfig; -import org.evomaster.client.java.instrumentation.external.DynamosaControlDependenceSnapshot; +import org.evomaster.client.java.instrumentation.graphs.ControlDependenceGraphConfig; +import org.evomaster.client.java.instrumentation.external.ControlDependenceSnapshot; import java.util.ArrayList; import java.util.Collection; @@ -149,16 +149,16 @@ public static void extractSpecifiedDto(List dtoNames){ UnitsInfoRecorder.registerSpecifiedDtoSchema(ExtractJvmClass.extractAsSchema(dtoNames)); } - public static DynamosaControlDependenceSnapshot getControlDependenceSnapshot(int fromIndex){ + public static ControlDependenceSnapshot getControlDependenceSnapshot(int fromIndex){ return GraphPool.exportSnapshotFromIndex(fromIndex); } - public static void setDynamosaGraphsEnabled(boolean enableGraphs) { - DynamosaConfig.setEnableGraphs(enableGraphs); + public static void setControlDependenceGraphsEnabled(boolean enableGraphs) { + ControlDependenceGraphConfig.setEnableGraphs(enableGraphs); } public static void setWriteCfgEnabled(boolean writeCfg) { - DynamosaConfig.setWriteCfgEnabled(writeCfg); + ControlDependenceGraphConfig.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 889d75bf02..a80229991f 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,8 +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.evomaster.client.java.instrumentation.graphs.ControlDependenceGraphConfig; +import org.evomaster.client.java.instrumentation.graphs.visitor.CFGClassVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; @@ -69,7 +69,7 @@ public byte[] transformBytes(ClassLoader classLoader, ClassName className, Class boolean canCollectCoverage = canInstrumentForCoverage(className); if(canCollectCoverage){ - if (DynamosaConfig.isGraphsEnabled()){ + if (ControlDependenceGraphConfig.isGraphsEnabled()){ cv = new CFGClassVisitor(classLoader, cv); } cv = new CoverageClassVisitor(cv, className); 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 deleted file mode 100755 index 560fcbdb2a..0000000000 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/ControlDependenceGraph.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * 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/external/AgentController.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/AgentController.java index e1a3e3a6e7..979271d948 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,8 +4,8 @@ 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 org.evomaster.client.java.instrumentation.graphs.ControlDependenceGraphConfig; +import org.evomaster.client.java.instrumentation.external.ControlDependenceSnapshot; import java.io.IOException; import java.io.ObjectInputStream; @@ -68,8 +68,8 @@ public static void start(int port){ InstrumentationController.resetForNewSearch(); sendCommand(Command.ACK); break; - case SET_DYNAMOSA_CONFIG: - handleDynamosaConfig(); + case SET_CDG_CONFIG: + handleControlDependenceGraphConfig(); sendCommand(Command.ACK); break; case NEW_TEST: @@ -115,8 +115,8 @@ public static void start(int port){ case EXTRACT_JVM_DTO: handleExtractingSpecifiedDto(); break; - case DYNAMOSA_CDG_SNAPSHOT: - handleDynamosaControlDependenceSnapshot(); + case CDG_SNAPSHOT: + handleControlDependenceSnapshot(); break; default: SimpleLogger.error("Unrecognized command: "+command); @@ -131,18 +131,18 @@ public static void start(int port){ thread.start(); } - private static void handleDynamosaConfig() { + private static void handleControlDependenceGraphConfig() { try { Object msg = in.readObject(); - DynamosaConfigDto dto = (DynamosaConfigDto) msg; + ControlDependenceGraphConfigDto dto = (ControlDependenceGraphConfigDto) msg; if (dto.enableGraphs != null) { - DynamosaConfig.setEnableGraphs(dto.enableGraphs); + ControlDependenceGraphConfig.setEnableGraphs(dto.enableGraphs); } if (dto.writeCfg != null) { - DynamosaConfig.setWriteCfgEnabled(dto.writeCfg); + ControlDependenceGraphConfig.setWriteCfgEnabled(dto.writeCfg); } } catch (Exception e) { - SimpleLogger.error("Failure in handling DYNAMOSA config: "+e.getMessage()); + SimpleLogger.error("Failure in handling CDG config: "+e.getMessage()); } } @@ -251,19 +251,19 @@ private static void handleExtractingSpecifiedDto(){ } } - private static void handleDynamosaControlDependenceSnapshot(){ + private static void handleControlDependenceSnapshot(){ try { Object msg = in.readObject(); int fromIndex = 0; if (msg instanceof Integer) { fromIndex = (Integer) msg; } - DynamosaControlDependenceSnapshot snapshot = InstrumentationController.getControlDependenceSnapshot(fromIndex); + ControlDependenceSnapshot snapshot = InstrumentationController.getControlDependenceSnapshot(fromIndex); sendObject(snapshot); } catch (Exception e){ - SimpleLogger.error("Failure in handling Dynamosa CDG snapshot: "+e.getMessage()); + SimpleLogger.error("Failure in handling CDG snapshot: "+e.getMessage()); try { - sendObject(new DynamosaControlDependenceSnapshot(Collections.emptyList(), 0)); + sendObject(new ControlDependenceSnapshot(Collections.emptyList(), 0)); } catch (IOException ignored) { } } 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 8448c97096..1937ff7616 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 @@ -23,6 +23,6 @@ public enum Command implements Serializable { BOOT_TIME_INFO, EXTRACT_JVM_DTO, BOOTING_SUT, - SET_DYNAMOSA_CONFIG, - DYNAMOSA_CDG_SNAPSHOT + SET_CDG_CONFIG, + 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/ControlDependenceGraphConfigDto.java similarity index 67% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/DynamosaConfigDto.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ControlDependenceGraphConfigDto.java index fe5c54fdb3..04e0a3f343 100644 --- 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/ControlDependenceGraphConfigDto.java @@ -2,7 +2,8 @@ import java.io.Serializable; -public class DynamosaConfigDto implements Serializable { +public class ControlDependenceGraphConfigDto 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/ControlDependenceSnapshot.java similarity index 76% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/DynamosaControlDependenceSnapshot.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ControlDependenceSnapshot.java index 42b6f63572..fc23cb7684 100644 --- 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/ControlDependenceSnapshot.java @@ -1,6 +1,6 @@ package org.evomaster.client.java.instrumentation.external; -import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto; +import org.evomaster.client.java.controller.api.dto.ControlDependenceGraphDto; import java.io.Serializable; import java.util.ArrayList; @@ -12,14 +12,14 @@ * exchange newly discovered control-dependence graphs together with the * next index to request. */ -public class DynamosaControlDependenceSnapshot implements Serializable { +public class ControlDependenceSnapshot implements Serializable { private static final long serialVersionUID = 6779255129756570792L; private final List graphs; private final int nextIndex; - public DynamosaControlDependenceSnapshot(List graphs, int nextIndex) { + public ControlDependenceSnapshot(List graphs, int nextIndex) { this.graphs = graphs == null ? new ArrayList<>() : new ArrayList<>(graphs); this.nextIndex = Math.max(nextIndex, 0); } @@ -33,4 +33,3 @@ public int getNextIndex() { } } - 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 50765e0be0..31f7f2d00c 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,7 +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.controller.api.dto.ControlDependenceGraphDto; import org.evomaster.client.java.instrumentation.staticstate.UnitsInfoRecorder; import org.evomaster.client.java.utils.SimpleLogger; @@ -36,7 +36,7 @@ public class ServerController { private Socket socket; protected ObjectOutputStream out; protected ObjectInputStream in; - private int dynamosaCdgIndex = 0; + private int controlDependenceGraphIndex = 0; public synchronized int startServer() { @@ -49,7 +49,7 @@ public synchronized int startServer() { throw new IllegalStateException(e); } - dynamosaCdgIndex = 0; + controlDependenceGraphIndex = 0; return server.getLocalPort(); } @@ -62,7 +62,7 @@ public synchronized void closeServer() { socket = null; in = null; out = null; - dynamosaCdgIndex = 0; + controlDependenceGraphIndex = 0; } catch (IOException e) { throw new IllegalStateException(e); } @@ -221,8 +221,8 @@ public boolean setBootingSut(boolean isBooting){ return sendWithDataAndExpectACK(Command.BOOTING_SUT, isBooting); } - public boolean setDynamosaConfig(DynamosaConfigDto dto){ - return sendWithDataAndExpectACK(Command.SET_DYNAMOSA_CONFIG, dto); + public boolean setControlDependenceGraphConfig(ControlDependenceGraphConfigDto dto){ + return sendWithDataAndExpectACK(Command.SET_CDG_CONFIG, dto); } // public synchronized List getAllCoveredTargetsInfo() { @@ -300,31 +300,31 @@ public synchronized List getAdditionalInfoList() { return (List) response; } - public synchronized List getDynamosaControlDependenceGraphs() { + public synchronized List getControlDependenceGraphs() { - boolean sent = sendCommand(Command.DYNAMOSA_CDG_SNAPSHOT); + boolean sent = sendCommand(Command.CDG_SNAPSHOT); if (!sent) { - SimpleLogger.error("Failed to send Dynamosa CDG request"); + SimpleLogger.error("Failed to send CDG request"); return Collections.emptyList(); } - if (!sendObject(dynamosaCdgIndex)) { - SimpleLogger.error("Failed to send Dynamosa CDG index"); + if (!sendObject(controlDependenceGraphIndex)) { + SimpleLogger.error("Failed to send CDG index"); return Collections.emptyList(); } Object response = waitAndGetResponse(); if (response == null) { - SimpleLogger.error("Failed to read Dynamosa CDG response"); + SimpleLogger.error("Failed to read CDG response"); return Collections.emptyList(); } - if (!(response instanceof DynamosaControlDependenceSnapshot)) { - throw new IllegalStateException(errorMsgExpectingResponse(response, DynamosaControlDependenceSnapshot.class.getSimpleName())); + if (!(response instanceof ControlDependenceSnapshot)) { + throw new IllegalStateException(errorMsgExpectingResponse(response, ControlDependenceSnapshot.class.getSimpleName())); } - DynamosaControlDependenceSnapshot snapshot = (DynamosaControlDependenceSnapshot) response; - dynamosaCdgIndex = snapshot.getNextIndex(); + ControlDependenceSnapshot snapshot = (ControlDependenceSnapshot) response; + controlDependenceGraphIndex = snapshot.getNextIndex(); return snapshot.getGraphs(); } 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/graphs/AnnotatedLabel.java old mode 100755 new mode 100644 similarity index 90% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedLabel.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/AnnotatedLabel.java index fdfa1cc9cd..0ebf55abe7 --- 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/graphs/AnnotatedLabel.java @@ -1,8 +1,8 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa; +package org.evomaster.client.java.instrumentation.graphs; import org.objectweb.asm.Label; import org.objectweb.asm.tree.LabelNode; @@ -55,3 +55,4 @@ 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/graphs/AnnotatedMethodNode.java old mode 100755 new mode 100644 similarity index 88% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/AnnotatedMethodNode.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/AnnotatedMethodNode.java index e04dde9f3d..0b967185b8 --- 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/graphs/AnnotatedMethodNode.java @@ -1,8 +1,8 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa; +package org.evomaster.client.java.instrumentation.graphs; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; @@ -10,7 +10,7 @@ import org.objectweb.asm.tree.MethodNode; /** - * AnnotatedMethodNode wraps ASM's MethodNode to support Dynamosa-specific metadata. + * AnnotatedMethodNode wraps ASM's MethodNode to support graph-specific metadata. */ public class AnnotatedMethodNode extends MethodNode { @@ -50,3 +50,4 @@ protected LabelNode getLabelNode(final Label l) { } } } + 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/graphs/ControlDependenceGraphConfig.java similarity index 80% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/DynamosaConfig.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/ControlDependenceGraphConfig.java index 1010449e9c..14b5e500ad 100644 --- 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/graphs/ControlDependenceGraphConfig.java @@ -1,14 +1,10 @@ -/* - * 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; +package org.evomaster.client.java.instrumentation.graphs; /** - * Runtime configuration for DYNAMOSA-related instrumentation features. + * Runtime configuration for control-dependence graph instrumentation features. * Populated via the agent control channel. */ -public class DynamosaConfig { +public class ControlDependenceGraphConfig { private static volatile boolean enableGraphs = false; private static volatile boolean writeCfg = false; @@ -41,4 +37,3 @@ public static void setWriteCfgEnabled(boolean 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/graphs/EvoMasterGraph.java similarity index 99% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/EvoMasterGraph.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/EvoMasterGraph.java index 92be370ade..ec5336c1a6 100755 --- 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/graphs/EvoMasterGraph.java @@ -1,8 +1,8 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs; +package org.evomaster.client.java.instrumentation.graphs; import org.jgrapht.DirectedGraph; import org.jgrapht.alg.DijkstraShortestPath; 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/graphs/GraphPool.java similarity index 87% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/GraphPool.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/GraphPool.java index a0bc407a18..e9be91e3a1 100755 --- 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/graphs/GraphPool.java @@ -1,23 +1,22 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs; +package org.evomaster.client.java.instrumentation.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.graphs.cdg.ControlDependenceGraph; +import org.evomaster.client.java.instrumentation.graphs.cfg.ActualControlFlowGraph; +import org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction; +import org.evomaster.client.java.instrumentation.graphs.cfg.ControlDependency; +import org.evomaster.client.java.instrumentation.graphs.cfg.RawControlFlowGraph; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.Branch; +import org.evomaster.client.java.instrumentation.external.ControlDependenceSnapshot; 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.controller.api.dto.ControlDependenceGraphDto; +import org.evomaster.client.java.controller.api.dto.BranchObjectiveDto; +import org.evomaster.client.java.controller.api.dto.DependencyEdgeDto; import org.evomaster.client.java.instrumentation.staticstate.ObjectiveRecorder; import org.evomaster.client.java.utils.SimpleLogger; @@ -56,7 +55,7 @@ private GraphPool(ClassLoader classLoader) { } private static boolean isWriteCfgEnabled() { - return DynamosaConfig.isWriteCfgEnabled(); + return ControlDependenceGraphConfig.isWriteCfgEnabled(); } public static GraphPool getInstance(ClassLoader classLoader) { @@ -135,7 +134,7 @@ public RawControlFlowGraph getRawCFG(String className, String methodName) { * * @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. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.ActualControlFlowGraph} object. */ public ActualControlFlowGraph getActualCFG(String className, String methodName) { @@ -152,7 +151,7 @@ public ActualControlFlowGraph getActualCFG(String className, String methodName) * * @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. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cdg.ControlDependenceGraph} object. */ public ControlDependenceGraph getCDG(String className, String methodName) { @@ -162,11 +161,11 @@ public ControlDependenceGraph getCDG(String className, String methodName) { return controlDependencies.get(className).get(methodName); } - public static DynamosaControlDependenceSnapshot exportSnapshotFromIndex(int fromIndex) { + public static ControlDependenceSnapshot 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()); + return new ControlDependenceSnapshot(slice, exportedCdgs.size()); } } @@ -177,7 +176,7 @@ public static DynamosaControlDependenceSnapshot exportSnapshotFromIndex(int from * registerRawCFG *

* - * @param cfg a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph} object. + * @param cfg a {@link org.evomaster.client.java.instrumentation.graphs.cfg.RawControlFlowGraph} object. */ public void registerRawCFG(RawControlFlowGraph cfg) { String className = cfg.getClassName(); @@ -191,8 +190,6 @@ public void registerRawCFG(RawControlFlowGraph cfg) { 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()) @@ -204,7 +201,7 @@ public void registerRawCFG(RawControlFlowGraph cfg) { * registerActualCFG *

* - * @param cfg a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph} + * @param cfg a {@link org.evomaster.client.java.instrumentation.graphs.cfg.ActualControlFlowGraph} * object. */ public void registerActualCFG(ActualControlFlowGraph cfg) { @@ -220,7 +217,6 @@ public void registerActualCFG(ActualControlFlowGraph cfg) { // 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()) @@ -405,16 +401,19 @@ private ControlDependenceGraphDto buildControlDependenceDto(String className, List edges = new ArrayList<>(); adjacency.forEach((parent, childrenIds) -> { for (Integer child : childrenIds) { - edges.add(new DependencyEdgeDto(parent, child)); + DependencyEdgeDto edge = new DependencyEdgeDto(); + edge.parentObjectiveId = parent; + edge.childObjectiveId = child; + edges.add(edge); } }); ControlDependenceGraphDto dto = new ControlDependenceGraphDto(); - dto.setClassName(className); - dto.setMethodName(methodName); - dto.setObjectives(new ArrayList<>(objectiveMap.values())); - dto.setRootObjectiveIds(new ArrayList<>(rootObjectives)); - dto.setEdges(edges); + dto.className = className; + dto.methodName = methodName; + dto.objectives = new ArrayList<>(objectiveMap.values()); + dto.rootObjectiveIds = new ArrayList<>(rootObjectives); + dto.edges = edges; return dto; } @@ -445,7 +444,12 @@ private Integer resolveParentObjectiveId(Branch branch, private Integer registerObjective(String descriptiveId, Map objectiveMap) { int id = ObjectiveRecorder.getMappedId(descriptiveId); - objectiveMap.computeIfAbsent(id, key -> new BranchObjectiveDto(id, descriptiveId)); + objectiveMap.computeIfAbsent(id, key -> { + BranchObjectiveDto obj = new BranchObjectiveDto(); + obj.id = id; + obj.descriptiveId = descriptiveId; + return obj; + }); return id; } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cdg/ControlDependenceGraph.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cdg/ControlDependenceGraph.java new file mode 100755 index 0000000000..6c4f378a72 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cdg/ControlDependenceGraph.java @@ -0,0 +1,232 @@ +/* + * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) + * and modified for use in EvoMaster. + */ +package org.evomaster.client.java.instrumentation.graphs.cdg; + +import org.evomaster.client.java.instrumentation.graphs.EvoMasterGraph; +import org.evomaster.client.java.instrumentation.graphs.cfg.*; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Represents the Control Dependence Graph (CDG) of a method. + *

+ * A CDG captures the control-flow dependencies between basic blocks: an edge from + * block A to block B indicates that the execution of B depends on a branch decision + * made in A. + *

+ * Built from an {@link ActualControlFlowGraph} using dominator frontiers. + */ +public class ControlDependenceGraph extends EvoMasterGraph { + + private final ActualControlFlowGraph cfg; + + private final String className; + private final String methodName; + + // Memoized values for performance + private final String name; + private final String dotSubFolderPath; + + public ControlDependenceGraph(ActualControlFlowGraph cfg) { + super(ControlFlowEdge.class); + + this.cfg = cfg; + this.className = cfg.getClassName(); + this.methodName = cfg.getMethodName(); + + // Memoize computed strings + this.name = methodName + "_CDG"; + this.dotSubFolderPath = toFileString(className) + "/CDG/"; + + 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.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()) { + boolean removed = graph.removeVertex(b); + if (!removed) { + 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()) { + continue; + } + + 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. + + // 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"); + } + } + + boolean added = addEdge(cd, b, new ControlFlowEdge(orig)); + if (!added) { + throw new IllegalStateException( + "internal error while adding CD edge"); + } + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return name; + } + + /** + * {@inheritDoc} + */ + @Override + protected String dotSubFolder() { + return dotSubFolderPath; + } + + /** + *

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/graphs/cdg/DominatorNode.java similarity index 76% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorNode.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cdg/DominatorNode.java index 8d4ba72c45..89f3574a5c 100755 --- 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/graphs/cdg/DominatorNode.java @@ -1,8 +1,8 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg; +package org.evomaster.client.java.instrumentation.graphs.cdg; import java.util.HashSet; import java.util.Set; @@ -20,10 +20,15 @@ * Look at cfg.DominatorTree for more detailed information * */ -class DominatorNode { +public class DominatorNode { final V node; - int n = 0; + + /** + * The depth-first search order number assigned during DominatorTree computation. + * Root node has dfsOrder == 1. + */ + int dfsOrder = 0; // parent of node within spanning tree of DFS inside cfg.DominatorTree DominatorNode parent; @@ -64,9 +69,9 @@ void compress() { if (ancestor.ancestor != null) { ancestor.compress(); - if (ancestor.label.semiDominator.n < label.semiDominator.n) + if (ancestor.label.semiDominator.dfsOrder < label.semiDominator.dfsOrder) { label = ancestor.label; - + } ancestor = ancestor.ancestor; } } @@ -80,13 +85,12 @@ DominatorNode getFromBucket() { } /** - *

isRootNode

+ * Returns true if this node is the root of the dominator tree (first node visited in DFS). * - * @return a boolean. + * @return true if this is the root node */ public boolean isRootNode() { - // TODO not that nice :/ - return n == 1; + return dfsOrder == 1; } /** @@ -94,6 +98,6 @@ public boolean isRootNode() { */ @Override public String toString() { - return "DTNode " + n + " - " + node; + return "DTNode " + dfsOrder + " - " + 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/graphs/cdg/DominatorTree.java similarity index 80% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorTree.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cdg/DominatorTree.java index 7e51026f58..f12f993e94 100755 --- 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/graphs/cdg/DominatorTree.java @@ -1,13 +1,12 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg; +package org.evomaster.client.java.instrumentation.graphs.cdg; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.EvoMasterGraph; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlFlowGraph; +import org.evomaster.client.java.instrumentation.graphs.EvoMasterGraph; +import org.evomaster.client.java.instrumentation.graphs.cfg.ControlFlowGraph; import org.jgrapht.graph.DefaultEdge; -import org.evomaster.client.java.utils.SimpleLogger; import java.util.HashSet; import java.util.LinkedHashMap; @@ -51,19 +50,16 @@ public class DominatorTree extends EvoMasterGraph, DefaultEd * 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. + * @param cfg a {@link org.evomaster.client.java.instrumentation.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); + V root = cfg.determineEntryPoint(); DominatorNode rootNode = getDominatorNodeFor(root); depthFirstAnalyze(rootNode); @@ -74,8 +70,6 @@ public DominatorTree(ControlFlowGraph cfg) { createDominatorTree(); computeDominatorFrontiers(rootNode); - - // toDot(); } private void createDominatorTree() { @@ -83,21 +77,17 @@ 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()) + if (v.isRootNode()) { continue; - if (addEdge(v.immediateDominator, v) == null) + } + 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"); @@ -110,31 +100,31 @@ private void computeDominatorFrontiers(DominatorNode currentNode) { // TODO check assumption: exitPoints in original CFG are exitPoints in resulting DominatorTree - for (DominatorNode child : getChildren(currentNode)) + for (DominatorNode child : getChildren(currentNode)) { computeDominatorFrontiers(child); - - SimpleLogger.debug("computing dominatingFrontier for: " + currentNode.toString()); + } Set dominatingFrontier = dominatingFrontiers.get(currentNode); - if (dominatingFrontier == null) + 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); + if (y.immediateDominator.dfsOrder != currentNode.dfsOrder) { 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); + for (DominatorNode z : getChildren(currentNode)) { + for (V y : dominatingFrontiers.get(z.node)) { + if (getDominatorNodeFor(y).immediateDominator.dfsOrder != currentNode.dfsOrder) { dominatingFrontier.add(y); } + } + } dominatingFrontiers.put(currentNode.node, dominatingFrontier); } @@ -165,7 +155,7 @@ public V getImmediateDominator(V v) { if (domNode.immediateDominator == null) { // sanity check: this is only allowed to happen if v is root of CFG - if (domNode.n != 1) + if (domNode.dfsOrder != 1) throw new IllegalStateException( "expect known node without iDom to be root of CFG"); @@ -213,17 +203,17 @@ private void depthFirstAnalyze(DominatorNode currentNode) { private void initialize(DominatorNode currentNode) { nodeCount++; - currentNode.n = nodeCount; + currentNode.dfsOrder = nodeCount; currentNode.semiDominator = currentNode; - SimpleLogger.debug("created " + currentNode + " for " - + currentNode.node.toString()); - dominatorIDMap.put(nodeCount, currentNode); } private void computeSemiDominators() { + // Process nodes in reverse DFS order, starting from the last visited node. + // We skip node 1 (i >= 2) because node 1 is the root/entry point, + // which has no dominator and dominates all other nodes. for (int i = nodeCount; i >= 2; i--) { DominatorNode w = getDominatorNodeById(i); @@ -232,7 +222,7 @@ private void computeSemiDominators() { DominatorNode v = getDominatorNodeFor(current); DominatorNode u = v.eval(); - if (u.semiDominator.n < w.semiDominator.n) + if (u.semiDominator.dfsOrder < w.semiDominator.dfsOrder) w.semiDominator = u.semiDominator; } @@ -247,7 +237,7 @@ private void computeSemiDominators() { throw new IllegalStateException("internal error"); DominatorNode u = v.eval(); - v.immediateDominator = (u.semiDominator.n < v.semiDominator.n ? u + v.immediateDominator = (u.semiDominator.dfsOrder < v.semiDominator.dfsOrder ? u : w.parent); } } @@ -260,8 +250,6 @@ private void computeImmediateDominators(DominatorNode rootNode) { if (w.immediateDominator != w.semiDominator) w.immediateDominator = w.immediateDominator.immediateDominator; - - // logger.debug("iDom for node "+i+" was: "+w.immediateDominator.n); } rootNode.immediateDominator = null; 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/graphs/cfg/ActualControlFlowGraph.java similarity index 86% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ActualControlFlowGraph.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/ActualControlFlowGraph.java index 07ee96ad97..c897587561 100755 --- 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/graphs/cfg/ActualControlFlowGraph.java @@ -1,10 +1,8 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; - -import org.evomaster.client.java.utils.SimpleLogger; +package org.evomaster.client.java.instrumentation.graphs.cfg; import java.util.HashSet; import java.util.Set; @@ -25,7 +23,7 @@ public class ActualControlFlowGraph extends ControlFlowGraph { * Constructor for ActualControlFlowGraph. *

* - * @param rawGraph a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph} object. + * @param rawGraph a {@link org.evomaster.client.java.instrumentation.graphs.cfg.RawControlFlowGraph} object. */ public ActualControlFlowGraph(RawControlFlowGraph rawGraph) { super(rawGraph.getClassName(), rawGraph.getMethodName(), @@ -42,7 +40,7 @@ public ActualControlFlowGraph(RawControlFlowGraph rawGraph) { * Constructor for ActualControlFlowGraph. *

* - * @param toRevert a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph} + * @param toRevert a {@link org.evomaster.client.java.instrumentation.graphs.cfg.ActualControlFlowGraph} * object. */ protected ActualControlFlowGraph(ActualControlFlowGraph toRevert) { @@ -55,7 +53,7 @@ protected ActualControlFlowGraph(ActualControlFlowGraph toRevert) { * computeReverseCFG *

* - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.ActualControlFlowGraph} object. */ public ActualControlFlowGraph computeReverseCFG() { // TODO: this must be possible to "pre implement" in EvoMasterGraph for @@ -211,8 +209,6 @@ private void computeNodes() { BasicBlock nodeBlock = rawGraph.determineBasicBlockFor(node); addBlock(nodeBlock); } - - SimpleLogger.debug(vertexCount() + " BasicBlocks"); } private void computeEdges() { @@ -222,8 +218,6 @@ private void computeEdges() { computeIncomingEdgesFor(block); computeOutgoingEdgesFor(block); } - - SimpleLogger.debug(edgeCount() + " ControlFlowEdges"); } private void computeIncomingEdgesFor(BasicBlock block) { @@ -260,27 +254,26 @@ private void computeOutgoingEdgesFor(BasicBlock block) { * addBlock *

* - * @param nodeBlock a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. + * @param nodeBlock a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BasicBlock} object. */ protected void addBlock(BasicBlock nodeBlock) { - if (nodeBlock == null) + if (nodeBlock == null) { throw new IllegalArgumentException("null given"); + } - SimpleLogger.debug("Adding block: " + nodeBlock.getName()); - - if (containsVertex(nodeBlock)) + if (containsVertex(nodeBlock)) { throw new IllegalArgumentException("block already added before"); + } - if (!addVertex(nodeBlock)) + if (!addVertex(nodeBlock)) { throw new IllegalStateException( "internal error while addind basic block to CFG"); + } - - if (!containsVertex(nodeBlock)) + if (!containsVertex(nodeBlock)) { throw new IllegalStateException( "expect graph to contain the given block on returning of addBlock()"); - - SimpleLogger.debug(".. succeeded. nodeCount: " + vertexCount()); + } } /** @@ -288,9 +281,9 @@ protected void addBlock(BasicBlock nodeBlock) { * 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. + * @param src a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. + * @param target a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BasicBlock} object. + * @param origEdge a {@link org.evomaster.client.java.instrumentation.graphs.cfg.ControlFlowEdge} object. */ protected void addRawEdge(BytecodeInstruction src, BasicBlock target, ControlFlowEdge origEdge) { @@ -307,9 +300,9 @@ protected void addRawEdge(BytecodeInstruction src, BasicBlock target, * 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. + * @param src a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BasicBlock} object. + * @param target a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. + * @param origEdge a {@link org.evomaster.client.java.instrumentation.graphs.cfg.ControlFlowEdge} object. */ protected void addRawEdge(BasicBlock src, BytecodeInstruction target, ControlFlowEdge origEdge) { @@ -326,45 +319,45 @@ protected void addRawEdge(BasicBlock src, BytecodeInstruction target, * 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. + * @param src a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BasicBlock} object. + * @param target a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BasicBlock} object. + * @param origEdge a {@link org.evomaster.client.java.instrumentation.graphs.cfg.ControlFlowEdge} object. */ protected void addRawEdge(BasicBlock src, BasicBlock target, ControlFlowEdge origEdge) { - if (src == null || target == null) + 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) + if (current == null) { throw new IllegalStateException( "expect getEdge() not to return null on parameters on which containsEdge() retruned true"); + } if (current.getBranchExpressionValue() - && !origEdge.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) + 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())) + || !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)) + 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 @@ -373,8 +366,8 @@ protected void addRawEdge(BasicBlock src, BasicBlock target, ControlFlowEdge ori * 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. + * @param instruction a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BasicBlock} object. */ public BasicBlock getBlockOf(BytecodeInstruction instruction) { if (instruction == null) @@ -383,13 +376,13 @@ public BasicBlock getBlockOf(BytecodeInstruction instruction) { if (instruction.hasBasicBlockSet()) return instruction.getBasicBlock(); - for (BasicBlock block : vertexSet()) + for (BasicBlock block : vertexSet()) { if (block.containsInstruction(instruction)) { instruction.setBasicBlock(block); return block; } + } - SimpleLogger.debug("unknown instruction " + instruction); return null; } @@ -397,7 +390,7 @@ public BasicBlock getBlockOf(BytecodeInstruction instruction) { * 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. + * @param instruction a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. * @return a boolean. */ public boolean knowsInstruction(BytecodeInstruction instruction) { @@ -419,8 +412,8 @@ public boolean knowsInstruction(BytecodeInstruction instruction) { * 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. + * @param v1 a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. + * @param v2 a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. * @return a int. */ /** @@ -428,7 +421,7 @@ public boolean knowsInstruction(BytecodeInstruction instruction) { * isEntryPoint *

* - * @param block a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. + * @param block a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BasicBlock} object. * @return a boolean. */ private boolean isEntryPoint(BasicBlock block) { @@ -443,7 +436,7 @@ private boolean isEntryPoint(BasicBlock block) { * isExitPoint *

* - * @param block a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BasicBlock} object. + * @param block a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BasicBlock} object. * @return a boolean. */ private boolean isExitPoint(BasicBlock block) { @@ -463,7 +456,7 @@ private boolean isExitPoint(BasicBlock block) { * belongsToMethod *

* - * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @param instruction a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. * @return a boolean. */ private boolean belongsToMethod(BytecodeInstruction instruction) { 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/graphs/cfg/BasicBlock.java similarity index 94% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BasicBlock.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/BasicBlock.java index b0251a5125..ae86d95360 100755 --- 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/graphs/cfg/BasicBlock.java @@ -1,11 +1,11 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.GraphPool; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg.ControlDependenceGraph; +import org.evomaster.client.java.instrumentation.graphs.GraphPool; +import org.evomaster.client.java.instrumentation.graphs.cdg.ControlDependenceGraph; import java.io.Serializable; import java.util.*; @@ -34,7 +34,7 @@ * 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 + * @see org.evomaster.client.java.instrumentation.graphs.cfg.ActualControlFlowGraph */ public class BasicBlock implements Serializable, Iterable { @@ -97,7 +97,7 @@ protected BasicBlock(String className, String methodName) { *

* Convenience method. Redirects the call to GraphPool.getCDG() * - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg.ControlDependenceGraph} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cdg.ControlDependenceGraph} object. */ public ControlDependenceGraph getCDG() { @@ -189,7 +189,7 @@ private void setId() { * containsInstruction *

* - * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @param instruction a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. * @return a boolean. */ public boolean containsInstruction(BytecodeInstruction instruction) { @@ -204,7 +204,7 @@ public boolean containsInstruction(BytecodeInstruction instruction) { * getFirstInstruction *

* - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. */ public BytecodeInstruction getFirstInstruction() { if (instructions.isEmpty()) @@ -217,7 +217,7 @@ public BytecodeInstruction getFirstInstruction() { * getLastInstruction *

* - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. */ public BytecodeInstruction getLastInstruction() { if (instructions.isEmpty()) 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/graphs/cfg/BytecodeInstruction.java similarity index 96% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstruction.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/BytecodeInstruction.java index 43cb8ff21b..48d7f6e142 100755 --- 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/graphs/cfg/BytecodeInstruction.java @@ -1,14 +1,14 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.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.graphs.cfg.branch.Branch; +import org.evomaster.client.java.instrumentation.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.evomaster.client.java.instrumentation.graphs.GraphPool; +import org.evomaster.client.java.instrumentation.graphs.cdg.ControlDependenceGraph; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.*; import org.objectweb.asm.util.Printer; @@ -75,7 +75,7 @@ public BytecodeInstruction(ClassLoader classLoader, String className, /** * Can represent any byteCode instruction * - * @param wrap a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @param wrap a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. */ public BytecodeInstruction(BytecodeInstruction wrap) { @@ -94,7 +94,7 @@ public BytecodeInstruction(BytecodeInstruction wrap) { * @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. + * @param basicBlock a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BasicBlock} object. */ public BytecodeInstruction(ClassLoader classLoader, String className, String methodName, int instructionId, int bytecodeOffset, AbstractInsnNode asmNode, @@ -188,7 +188,7 @@ public String getClassName() { * 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. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BasicBlock} object. */ public BasicBlock getBasicBlock() { if (!hasBasicBlockSet()) @@ -206,7 +206,7 @@ private void retrieveBasicBlock() { * 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. + * @param block a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BasicBlock} object. */ public void setBasicBlock(BasicBlock block) { if (block == null) @@ -315,7 +315,7 @@ private void retrieveLineNumber() { *

* Convenience method. Redirects the call to GraphPool.getActualCFG() * - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.ActualControlFlowGraph} object. */ public ActualControlFlowGraph getActualCFG() { @@ -333,7 +333,7 @@ public ActualControlFlowGraph getActualCFG() { *

* Convenience method. Redirects the call to GraphPool.getRawCFG() * - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.RawControlFlowGraph} object. */ public RawControlFlowGraph getRawCFG() { @@ -351,7 +351,7 @@ public RawControlFlowGraph getRawCFG() { *

* Convenience method. Redirects the call to GraphPool.getCDG() * - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg.ControlDependenceGraph} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cdg.ControlDependenceGraph} object. */ public ControlDependenceGraph getCDG() { @@ -529,7 +529,7 @@ public String toString() { *

* Otherwise this method will return null; * - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.branch.Branch} object. */ public Branch toBranch() { 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/graphs/cfg/BytecodeInstructionPool.java similarity index 85% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionPool.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/BytecodeInstructionPool.java index 2026f747ed..78fd654332 100755 --- 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/graphs/cfg/BytecodeInstructionPool.java @@ -1,14 +1,13 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; -import org.evomaster.client.java.instrumentation.dynamosa.AnnotatedLabel; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.BranchPool; +import org.evomaster.client.java.instrumentation.graphs.AnnotatedLabel; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.BranchPool; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.*; -import org.evomaster.client.java.utils.SimpleLogger; import java.util.*; @@ -193,7 +192,7 @@ private int getBytecodeIncrement(AbstractInsnNode instructionNode) { * registerInstruction *

* - * @param instruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @param instruction a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. */ public void registerInstruction(BytecodeInstruction instruction) { String className = instruction.getClassName(); @@ -207,7 +206,6 @@ public void registerInstruction(BytecodeInstruction instruction) { 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); @@ -217,7 +215,6 @@ public void registerInstruction(BytecodeInstruction instruction) { AnnotatedLabel aLabel = (AnnotatedLabel) ln.getLabel(); if (aLabel.isStartTag()) { if (aLabel.shouldIgnore()) { - SimpleLogger.debug("Ignoring artificial branch: " + instruction); return; } } @@ -241,7 +238,7 @@ public void registerInstruction(BytecodeInstruction instruction) { * @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. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. */ public BytecodeInstruction getInstruction(String className, String methodName, int instructionId, AbstractInsnNode asmNode) { @@ -261,30 +258,21 @@ public BytecodeInstruction getInstruction(String className, String methodName, * @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. + * @return a {@link org.evomaster.client.java.instrumentation.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) + 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; @@ -298,31 +286,23 @@ public BytecodeInstruction getInstruction(String className, String methodName, * @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. + * @return a {@link org.evomaster.client.java.instrumentation.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) + 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; } @@ -363,7 +343,7 @@ public List getInstructionsIn(String className) { * forgetInstruction *

* - * @param ins a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @param ins a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. * @return a boolean. */ public boolean forgetInstruction(BytecodeInstruction 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/graphs/cfg/CFGGenerator.java similarity index 96% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGGenerator.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/CFGGenerator.java index b541ba60e7..805ab13b76 100755 --- 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/graphs/cfg/CFGGenerator.java @@ -1,10 +1,10 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.GraphPool; +import org.evomaster.client.java.instrumentation.graphs.GraphPool; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.Frame; @@ -181,7 +181,7 @@ public void registerControlFlowEdge(int src, int dst, Frame[] frames, *

* See ActualControlFlowGraph and GraphPool for further details. * - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ActualControlFlowGraph} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.ActualControlFlowGraph} object. */ public ActualControlFlowGraph computeActualCFG() { return new ActualControlFlowGraph(rawGraph); @@ -194,7 +194,7 @@ public ActualControlFlowGraph computeActualCFG() { * Getter for the field rawGraph. *

* - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.RawControlFlowGraph} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.RawControlFlowGraph} object. */ public RawControlFlowGraph getRawGraph() { return rawGraph; 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/graphs/cfg/ControlDependency.java similarity index 85% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlDependency.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/ControlDependency.java index 7a44ead7be..4a87e93941 100755 --- 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/graphs/cfg/ControlDependency.java @@ -1,10 +1,10 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.Branch; import java.util.Objects; @@ -16,7 +16,7 @@ public class ControlDependency { /** *

Constructor for ControlDependency.

* - * @param branch a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch} object. + * @param branch a {@link org.evomaster.client.java.instrumentation.graphs.cfg.branch.Branch} object. * @param branchExpressionValue a boolean. */ public ControlDependency(Branch branch, boolean branchExpressionValue) { @@ -31,7 +31,7 @@ public ControlDependency(Branch branch, boolean branchExpressionValue) { /** *

Getter for the field branch.

* - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.branch.Branch} object. */ public Branch getBranch() { return branch; 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/graphs/cfg/ControlFlowEdge.java similarity index 85% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowEdge.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/ControlFlowEdge.java index beaa8b3412..b7ff8b2469 100755 --- 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/graphs/cfg/ControlFlowEdge.java @@ -1,10 +1,10 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.Branch; import org.jgrapht.graph.DefaultEdge; public class ControlFlowEdge extends DefaultEdge { @@ -32,7 +32,7 @@ public ControlFlowEdge(boolean isExceptionEdge) { /** *

Constructor for ControlFlowEdge.

* - * @param cd a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlDependency} object. + * @param cd a {@link org.evomaster.client.java.instrumentation.graphs.cfg.ControlDependency} object. * @param isExceptionEdge a boolean. */ public ControlFlowEdge(ControlDependency cd, boolean isExceptionEdge) { @@ -44,7 +44,7 @@ public ControlFlowEdge(ControlDependency cd, boolean isExceptionEdge) { /** * Sort of a copy constructor * - * @param clone a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlFlowEdge} object. + * @param clone a {@link org.evomaster.client.java.instrumentation.graphs.cfg.ControlFlowEdge} object. */ public ControlFlowEdge(ControlFlowEdge clone) { if (clone != null) { @@ -56,7 +56,7 @@ public ControlFlowEdge(ControlFlowEdge clone) { /** *

getControlDependency

* - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlDependency} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.ControlDependency} object. */ public ControlDependency getControlDependency() { return cd; @@ -74,7 +74,7 @@ public boolean hasControlDependency() { /** *

getBranchInstruction

* - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.branch.Branch} object. */ public Branch getBranchInstruction() { if (cd == null) 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/graphs/cfg/ControlFlowGraph.java similarity index 92% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowGraph.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/ControlFlowGraph.java index 07ac2deb19..054155e3fa 100755 --- 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/graphs/cfg/ControlFlowGraph.java @@ -1,12 +1,11 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.EvoMasterGraph; +import org.evomaster.client.java.instrumentation.graphs.EvoMasterGraph; import org.jgrapht.graph.DefaultDirectedGraph; -import org.objectweb.asm.Opcodes; import java.util.HashSet; import java.util.LinkedList; @@ -63,7 +62,7 @@ protected ControlFlowGraph(String className, String methodName, int access, /** *

leadsToNode

* - * @param e a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlFlowEdge} object. + * @param e a {@link org.evomaster.client.java.instrumentation.graphs.cfg.ControlFlowEdge} object. * @param b a V object. * @return a boolean. */ @@ -105,14 +104,14 @@ public boolean leadsToNode(ControlFlowEdge e, V b) { * 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. + * @return a {@link org.evomaster.client.java.instrumentation.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. + * @param instruction a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. * @return a boolean. */ public abstract boolean containsInstruction(BytecodeInstruction instruction); 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/graphs/cfg/EntryBlock.java similarity index 86% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/EntryBlock.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/EntryBlock.java index f7ba45042a..f571b15cba 100755 --- 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/graphs/cfg/EntryBlock.java @@ -1,8 +1,8 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; public class EntryBlock extends BasicBlock { 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/graphs/cfg/ExitBlock.java similarity index 86% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ExitBlock.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/ExitBlock.java index 8e9f34b1d3..6efeffe45b 100755 --- 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/graphs/cfg/ExitBlock.java @@ -1,8 +1,8 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; public class ExitBlock extends BasicBlock { 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/graphs/cfg/RawControlFlowGraph.java similarity index 83% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/RawControlFlowGraph.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/RawControlFlowGraph.java index 3798ce58dc..165e8e06ea 100755 --- 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/graphs/cfg/RawControlFlowGraph.java @@ -1,10 +1,10 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.BranchPool; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.BranchPool; import org.evomaster.client.java.utils.SimpleLogger; @@ -74,16 +74,14 @@ public BytecodeInstruction getInstruction(int instructionId) { * 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 src a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. + * @param target a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. * @param isExceptionEdge a boolean. - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.ControlFlowEdge} object. + * @return a {@link org.evomaster.client.java.instrumentation.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); } @@ -112,13 +110,12 @@ 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()); + // Edge already exists - retrieve the existing one e = super.getEdge(src, target); - if (e == null) + if (e == null) { throw new IllegalStateException( "internal graph error - completely unexpected"); + } } return e; @@ -141,16 +138,13 @@ private boolean isNonJumpingEdge(BytecodeInstruction src, // TODO move to * 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. + * @param instruction a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BasicBlock} object. */ public BasicBlock determineBasicBlockFor(BytecodeInstruction instruction) { - if (instruction == null) + 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); @@ -162,57 +156,53 @@ public BasicBlock determineBasicBlockFor(BytecodeInstruction instruction) { queue.add(instruction); while (!queue.isEmpty()) { BytecodeInstruction current = queue.poll(); - SimpleLogger.debug("handling " + current.toString()); // add child to queue - if (outDegreeOf(current) == 1) + if (outDegreeOf(current) == 1) { for (BytecodeInstruction child : getChildren(current)) { // this must be only one edge if inDegree was 1 - if (blockNodes.contains(child)) + if (blockNodes.contains(child)) { continue; + } - if (handledChildren.contains(child)) + 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) + if (inDegreeOf(current) == 1) { for (BytecodeInstruction parent : getParents(current)) { // this must be only one edge if outDegree was 1 - if (blockNodes.contains(parent)) + if (blockNodes.contains(parent)) { continue; + } - if (handledParents.contains(parent)) + 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; + return new BasicBlock(classLoader, className, methodName, blockNodes); } /** @@ -285,7 +275,7 @@ public Set determineJoins() { * getInstructionWithSmallestId *

* - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. */ public BytecodeInstruction getInstructionWithSmallestId() { return vertexSet().stream() @@ -298,7 +288,7 @@ public BytecodeInstruction getInstructionWithSmallestId() { * getInstructionWithBiggestId *

* - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. */ public BytecodeInstruction getInstructionWithBiggestId() { return vertexSet().stream() 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/graphs/cfg/branch/Branch.java similarity index 93% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/Branch.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/branch/Branch.java index 9ec1297d34..a5598a6858 100755 --- 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/graphs/cfg/branch/Branch.java @@ -1,10 +1,10 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch; +package org.evomaster.client.java.instrumentation.graphs.cfg.branch; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction; +import org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction; import java.io.Serializable; @@ -40,7 +40,7 @@ public class Branch implements Serializable { /** * Constructor for usual jump instruction Branches. * - * @param branchInstruction a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @param branchInstruction a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. * @param actualBranchId a int. */ public Branch(BytecodeInstruction branchInstruction, int actualBranchId) { @@ -71,7 +71,7 @@ public int getActualBranchId() { * Getter for the field instruction. *

* - * @return a {@link org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. */ public BytecodeInstruction getInstruction() { return instruction; 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/graphs/cfg/branch/BranchPool.java similarity index 93% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchPool.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/branch/BranchPool.java index dbd5c2190f..2fbe794d44 100755 --- 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/graphs/cfg/branch/BranchPool.java @@ -1,11 +1,11 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch; +package org.evomaster.client.java.instrumentation.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.graphs.cfg.BytecodeInstruction; import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming; import org.evomaster.client.java.utils.SimpleLogger; @@ -63,7 +63,7 @@ public static void resetForTesting(ClassLoader classLoader) { * 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. + * @param instruction a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. */ public void registerAsBranch(BytecodeInstruction instruction) { if (!(instruction.isBranch())) @@ -147,7 +147,7 @@ private int nextOrdinalForLine(String className, String methodName, int lineNumb * 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. + * @param instruction a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. * @return a boolean. */ public boolean isKnownAsBranch(BytecodeInstruction instruction) { @@ -159,8 +159,8 @@ public boolean isKnownAsBranch(BytecodeInstruction 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. + * @param instruction a {@link org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction} object. + * @return a {@link org.evomaster.client.java.instrumentation.graphs.cfg.branch.Branch} object. */ public Branch getBranchForInstruction(BytecodeInstruction instruction) { if (instruction == null) 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/graphs/visitor/CFGClassVisitor.java similarity index 92% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGClassVisitor.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/visitor/CFGClassVisitor.java index 676da385bf..2a334712ac 100644 --- 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/graphs/visitor/CFGClassVisitor.java @@ -1,8 +1,8 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.visitor; +package org.evomaster.client.java.instrumentation.graphs.visitor; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; 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/graphs/visitor/CFGMethodVisitor.java similarity index 85% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/dynamosa/visitor/CFGMethodVisitor.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/visitor/CFGMethodVisitor.java index da6aed4f3c..dd79815453 100755 --- 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/graphs/visitor/CFGMethodVisitor.java @@ -1,11 +1,11 @@ /* * Adapted from the EvoSuite project (https://github.com/EvoSuite/evosuite) - * and modified for use in EvoMaster's Dynamosa module. + * and modified for use in EvoMaster. */ -package org.evomaster.client.java.instrumentation.dynamosa.visitor; +package org.evomaster.client.java.instrumentation.graphs.visitor; -import org.evomaster.client.java.instrumentation.dynamosa.AnnotatedMethodNode; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.CFGGenerator; +import org.evomaster.client.java.instrumentation.graphs.AnnotatedMethodNode; +import org.evomaster.client.java.instrumentation.graphs.cfg.CFGGenerator; import org.objectweb.asm.*; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.Analyzer; @@ -83,7 +83,6 @@ public CFGMethodVisitor(ClassLoader classLoader, String className, int access, */ @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) @@ -109,14 +108,6 @@ protected boolean newControlFlowExceptionEdge(int src, int dst) { } }; 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); 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/graphs/GraphPoolTest.java similarity index 94% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/GraphPoolTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/GraphPoolTest.java index 5bf7a62949..89fc698f3b 100644 --- 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/graphs/GraphPoolTest.java @@ -1,8 +1,8 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs; +package org.evomaster.client.java.instrumentation.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.evomaster.client.java.instrumentation.graphs.cfg.ActualControlFlowGraph; +import org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction; +import org.evomaster.client.java.instrumentation.graphs.cfg.RawControlFlowGraph; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; 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/graphs/cdg/ControlDependenceGraphTest.java similarity index 91% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/ControlDependenceGraphTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cdg/ControlDependenceGraphTest.java index ca564bdc67..6933e8a6b6 100644 --- 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/graphs/cdg/ControlDependenceGraphTest.java @@ -1,13 +1,13 @@ -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; +package org.evomaster.client.java.instrumentation.graphs.cdg; + +import org.evomaster.client.java.instrumentation.graphs.GraphPool; +import org.evomaster.client.java.instrumentation.graphs.cfg.ActualControlFlowGraph; +import org.evomaster.client.java.instrumentation.graphs.cfg.BasicBlock; +import org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction; +import org.evomaster.client.java.instrumentation.graphs.cfg.ControlDependency; +import org.evomaster.client.java.instrumentation.graphs.cfg.RawControlFlowGraph; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.Branch; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.BranchPool; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; @@ -25,7 +25,7 @@ class ControlDependenceGraphTest { private static final ClassLoader TEST_LOADER = ControlDependenceGraphTest.class.getClassLoader(); - private static final String CLASS_NAME = "com.example.DynamosaTestClass"; + private static final String CLASS_NAME = "com.example.GraphTestClass"; private static final String METHOD_NAME = "sample()V"; @AfterEach 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/graphs/cdg/DominatorNodeTest.java similarity index 80% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cdg/DominatorNodeTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cdg/DominatorNodeTest.java index cb32a65584..bb67626959 100644 --- 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/graphs/cdg/DominatorNodeTest.java @@ -1,4 +1,4 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cdg; +package org.evomaster.client.java.instrumentation.graphs.cdg; import org.junit.jupiter.api.Test; @@ -33,17 +33,17 @@ void evalCompressesAncestorWhenPresentAndReturnsUpdatedLabel() { 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) + // In the Lengauer-Tarjan algorithm used to create the DominatorTree, every visited node has a dfsOrder value + // dfsOrder is the order of discovery of the node in the DFS of the DominatorTree + // Smaller dfsOrder means its higher in the tree (closer to the root) + // Label always points to the node's best dominator candidate so far (lowest dfsOrder value) - // From every path on the start to the node, the node's semi-dominator is the node with the lowest N value + // From every path on the start to the node, the node's semi-dominator is the node with the lowest dfsOrder value node.semiDominator = new DominatorNode<>("nodeSemi"); - node.semiDominator.n = 10; + node.semiDominator.dfsOrder = 10; ancestor.semiDominator = new DominatorNode<>("ancestorSemi"); - ancestor.semiDominator.n = 1; + ancestor.semiDominator.dfsOrder = 1; // Link the nodes together to form a tree node.link(ancestor); @@ -62,16 +62,16 @@ void evalCompressesAncestorWhenPresentAndReturnsUpdatedLabel() { } @Test - void isRootNodeReturnsTrueWhenNIsOne() { + void isRootNodeReturnsTrueWhenDfsOrderIsOne() { DominatorNode node = new DominatorNode<>("entry"); - node.n = 1; + node.dfsOrder = 1; assertTrue(node.isRootNode()); } @Test - void isRootNodeReturnsFalseWhenNGreaterThanOne() { + void isRootNodeReturnsFalseWhenDfsOrderGreaterThanOne() { DominatorNode node = new DominatorNode<>("entry"); - node.n = 2; + node.dfsOrder = 2; assertFalse(node.isRootNode()); } 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/graphs/cfg/ActualControlFlowGraphTest.java similarity index 97% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ActualControlFlowGraphTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/ActualControlFlowGraphTest.java index 0664bf04be..91664268c8 100644 --- 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/graphs/cfg/ActualControlFlowGraphTest.java @@ -1,6 +1,6 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.BranchPool; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.BranchPool; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; 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/graphs/cfg/BasicBlockTest.java similarity index 98% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BasicBlockTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/BasicBlockTest.java index e79cd12f50..8414f5e575 100644 --- 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/graphs/cfg/BasicBlockTest.java @@ -1,4 +1,4 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionPoolTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/BytecodeInstructionPoolTest.java similarity index 95% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionPoolTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/BytecodeInstructionPoolTest.java index dfd2f43a89..0d3bff945d 100644 --- a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionPoolTest.java +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/BytecodeInstructionPoolTest.java @@ -1,6 +1,6 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.BranchPool; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.BranchPool; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/BytecodeInstructionTest.java similarity index 95% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/BytecodeInstructionTest.java index ade368d33b..d4964ec7ea 100644 --- a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/BytecodeInstructionTest.java +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/BytecodeInstructionTest.java @@ -1,7 +1,7 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.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.graphs.cfg.branch.Branch; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.BranchPool; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGGeneratorTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/CFGGeneratorTest.java similarity index 94% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGGeneratorTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/CFGGeneratorTest.java index 330bbfa3e2..ba3e814011 100644 --- a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/CFGGeneratorTest.java +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/CFGGeneratorTest.java @@ -1,7 +1,7 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.GraphPool; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.BranchPool; +import org.evomaster.client.java.instrumentation.graphs.GraphPool; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.BranchPool; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlDependencyTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/ControlDependencyTest.java similarity index 94% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlDependencyTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/ControlDependencyTest.java index be56e490c3..fd2839d89a 100644 --- a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlDependencyTest.java +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/ControlDependencyTest.java @@ -1,6 +1,6 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.Branch; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.JumpInsnNode; diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowEdgeTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/ControlFlowEdgeTest.java similarity index 94% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowEdgeTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/ControlFlowEdgeTest.java index 558a9d435a..6f96a20e60 100644 --- a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ControlFlowEdgeTest.java +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/ControlFlowEdgeTest.java @@ -1,6 +1,6 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.Branch; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.Branch; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.JumpInsnNode; diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/EntryBlockTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/EntryBlockTest.java similarity index 92% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/EntryBlockTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/EntryBlockTest.java index cdb229be72..1a5d907f15 100644 --- a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/EntryBlockTest.java +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/EntryBlockTest.java @@ -1,4 +1,4 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; import org.junit.jupiter.api.Test; diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ExitBlockTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/ExitBlockTest.java similarity index 92% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ExitBlockTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/ExitBlockTest.java index 8da5ade2af..99bc88a75e 100644 --- a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/ExitBlockTest.java +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/ExitBlockTest.java @@ -1,4 +1,4 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; import org.junit.jupiter.api.Test; diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/RawControlFlowGraphTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/RawControlFlowGraphTest.java similarity index 97% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/RawControlFlowGraphTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/RawControlFlowGraphTest.java index efbbdfa5f3..ac1dee621a 100644 --- a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/RawControlFlowGraphTest.java +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/RawControlFlowGraphTest.java @@ -1,6 +1,6 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg; +package org.evomaster.client.java.instrumentation.graphs.cfg; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch.BranchPool; +import org.evomaster.client.java.instrumentation.graphs.cfg.branch.BranchPool; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchPoolTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/branch/BranchPoolTest.java similarity index 96% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchPoolTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/branch/BranchPoolTest.java index 1891790ec7..53ccad1bb3 100644 --- a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchPoolTest.java +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/branch/BranchPoolTest.java @@ -1,6 +1,6 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch; +package org.evomaster.client.java.instrumentation.graphs.cfg.branch; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction; +import org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/branch/BranchTest.java similarity index 95% rename from client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchTest.java rename to client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/branch/BranchTest.java index 18dfebbf24..e2dd3335e7 100644 --- a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/dynamosa/graphs/cfg/branch/BranchTest.java +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/graphs/cfg/branch/BranchTest.java @@ -1,6 +1,6 @@ -package org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.branch; +package org.evomaster.client.java.instrumentation.graphs.cfg.branch; -import org.evomaster.client.java.instrumentation.dynamosa.graphs.cfg.BytecodeInstruction; +import org.evomaster.client.java.instrumentation.graphs.cfg.BytecodeInstruction; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.InsnNode; diff --git a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt index ee428e2bf7..ef1a9eb922 100644 --- a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt +++ b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt @@ -3,7 +3,7 @@ package org.evomaster.core.remote.service import org.evomaster.client.java.controller.api.dto.* import org.evomaster.client.java.controller.api.dto.problem.param.DeriveParamResponseDto import org.evomaster.client.java.controller.api.dto.problem.param.DerivedParamChangeReqDto -import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto +import org.evomaster.client.java.controller.api.dto.ControlDependenceGraphDto import org.evomaster.core.problem.enterprise.param.DerivedParamChangeReq import org.evomaster.core.scheduletask.ScheduleTaskExecutor import org.evomaster.core.sql.DatabaseExecutor @@ -63,5 +63,5 @@ interface RemoteController : DatabaseExecutor, ScheduleTaskExecutor { fun deriveParams(deriveParams: List) : List - fun getDynamosaControlDependenceGraphs(): List = emptyList() + fun getControlDependenceGraphs(): List = emptyList() } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt index 6e3b531539..2a4e954db6 100644 --- a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt +++ b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt @@ -7,7 +7,7 @@ import org.evomaster.client.java.controller.api.dto.* import org.evomaster.client.java.controller.api.dto.database.operations.* import org.evomaster.client.java.controller.api.dto.problem.param.DeriveParamResponseDto import org.evomaster.client.java.controller.api.dto.problem.param.DerivedParamChangeReqDto -import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto +import org.evomaster.client.java.controller.api.dto.ControlDependenceGraphDto import org.evomaster.client.java.controller.api.dto.problem.rpc.ScheduleTaskInvocationDto import org.evomaster.client.java.controller.api.dto.problem.rpc.ScheduleTaskInvocationsDto import org.evomaster.client.java.controller.api.dto.problem.rpc.ScheduleTaskInvocationsResult @@ -48,7 +48,7 @@ class RemoteControllerImplementation() : RemoteController{ private var extractSqlExecutionInfo = true private var cachedSutInfoDto : SutInfoDto? = null - private val pendingDynamosaCdgs: MutableList = mutableListOf() + private val pendingControlDependenceGraphs: MutableList = mutableListOf() @Inject private lateinit var config: EMConfig @@ -261,8 +261,8 @@ class RemoteControllerImplementation() : RemoteController{ ) requestDto.advancedHeuristics = config.heuristicsForSQLAdvanced - // Pass Dynamosa settings from core to controller/driver - requestDto.enableDynamosaGraphs = config.algorithm.toString() == "DYNAMOSA" + // Pass CDG settings from core to controller/driver + requestDto.enableControlDependenceGraphs = config.algorithm.toString() == "DYNAMOSA" requestDto.writeCfg = config.writeCfg val response = try { @@ -373,9 +373,9 @@ class RemoteControllerImplementation() : RemoteController{ val result = getData(dto) - if (result != null && result.dynamosaCdgs.isNotEmpty()) { - synchronized(pendingDynamosaCdgs) { - pendingDynamosaCdgs.addAll(result.dynamosaCdgs) + if (result != null && result.controlDependenceGraphs.isNotEmpty()) { + synchronized(pendingControlDependenceGraphs) { + pendingControlDependenceGraphs.addAll(result.controlDependenceGraphs) } } @@ -399,13 +399,13 @@ class RemoteControllerImplementation() : RemoteController{ return dto?.data ?: listOf() } - override fun getDynamosaControlDependenceGraphs(): List { - synchronized(pendingDynamosaCdgs) { - if (pendingDynamosaCdgs.isEmpty()) { + override fun getControlDependenceGraphs(): List { + synchronized(pendingControlDependenceGraphs) { + if (pendingControlDependenceGraphs.isEmpty()) { return emptyList() } - val copy = pendingDynamosaCdgs.toList() - pendingDynamosaCdgs.clear() + val copy = pendingControlDependenceGraphs.toList() + pendingControlDependenceGraphs.clear() return copy } } diff --git a/core/src/main/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraph.kt b/core/src/main/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraph.kt index 548406d016..64b4b11a84 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraph.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraph.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.algorithms -import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto +import org.evomaster.client.java.controller.api.dto.ControlDependenceGraphDto import org.evomaster.core.search.service.IdMapper import java.util.LinkedHashMap import java.util.LinkedHashSet diff --git a/core/src/main/kotlin/org/evomaster/core/search/algorithms/DynamosaAlgorithm.kt b/core/src/main/kotlin/org/evomaster/core/search/algorithms/DynamosaAlgorithm.kt index 7566a3670f..3277f8ab9c 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/algorithms/DynamosaAlgorithm.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/algorithms/DynamosaAlgorithm.kt @@ -50,7 +50,7 @@ class DynaMosaAlgorithm : SearchAlgorithm() where T : Individual { initPopulation() - val newCdgs = remoteController.getDynamosaControlDependenceGraphs() + val newCdgs = remoteController.getControlDependenceGraphs() if (newCdgs.isNotEmpty()) { goalsManager.addControlDependenceGraphs(newCdgs) } @@ -60,7 +60,7 @@ class DynaMosaAlgorithm : SearchAlgorithm() where T : Individual { override fun searchOnce() { - val newCdgs = remoteController.getDynamosaControlDependenceGraphs() + val newCdgs = remoteController.getControlDependenceGraphs() if (newCdgs.isNotEmpty()) { goalsManager.addControlDependenceGraphs(newCdgs) } @@ -88,7 +88,7 @@ class DynaMosaAlgorithm : SearchAlgorithm() where T : Individual { population.addAll(nextPop) - val moreCdgs = remoteController.getDynamosaControlDependenceGraphs() + val moreCdgs = remoteController.getControlDependenceGraphs() if (moreCdgs.isNotEmpty()) { goalsManager.addControlDependenceGraphs(moreCdgs) } diff --git a/core/src/main/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManager.kt b/core/src/main/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManager.kt index 7f80716d37..5dfced89b3 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManager.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManager.kt @@ -1,7 +1,7 @@ package org.evomaster.core.search.algorithms import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming -import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto +import org.evomaster.client.java.controller.api.dto.ControlDependenceGraphDto import org.evomaster.core.search.service.Archive import org.evomaster.core.search.service.IdMapper import org.evomaster.core.logging.LoggingUtil diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraphTest.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraphTest.kt index fed09ed2bf..bf59cfea3f 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraphTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/BranchDependencyGraphTest.kt @@ -2,7 +2,9 @@ package org.evomaster.core.search.algorithms import io.mockk.mockk import io.mockk.verify -import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto +import org.evomaster.client.java.controller.api.dto.BranchObjectiveDto +import org.evomaster.client.java.controller.api.dto.ControlDependenceGraphDto +import org.evomaster.client.java.controller.api.dto.DependencyEdgeDto import org.evomaster.core.search.service.IdMapper import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue @@ -92,14 +94,14 @@ class BranchDependencyGraphTest { dto.methodName = "method" dto.objectives = objectives.map { id -> - ControlDependenceGraphDto.BranchObjectiveDto().apply { + BranchObjectiveDto().apply { this.id = id this.descriptiveId = "desc_$id" } } dto.rootObjectiveIds = rootIds dto.edges = edges.map { (parent, child) -> - ControlDependenceGraphDto.DependencyEdgeDto().apply { + DependencyEdgeDto().apply { parentObjectiveId = parent childObjectiveId = child } diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/DynaMosaAlgorithmTest.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/DynaMosaAlgorithmTest.kt index 7accaab56b..588d25685f 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/algorithms/DynaMosaAlgorithmTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/DynaMosaAlgorithmTest.kt @@ -21,7 +21,7 @@ import org.evomaster.client.java.controller.api.dto.problem.rpc.ScheduleTaskInvo import org.evomaster.client.java.controller.api.dto.database.operations.* import org.evomaster.client.java.controller.api.dto.ActionDto import org.evomaster.client.java.controller.api.dto.ActionResponseDto -import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto +import org.evomaster.client.java.controller.api.dto.ControlDependenceGraphDto import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -88,7 +88,7 @@ private class DummyRemoteController : RemoteController { override fun address(): String = "dummy" override fun close() {} override fun deriveParams(deriveParams: List): List = emptyList() - override fun getDynamosaControlDependenceGraphs(): List = emptyList() + override fun getControlDependenceGraphs(): List = emptyList() override fun executeDatabaseCommand(dto: DatabaseCommandDto): Boolean = true override fun executeDatabaseCommandAndGetQueryResults(dto: DatabaseCommandDto): QueryResultDto? = null diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManagerTest.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManagerTest.kt index 3f39ce03f3..a25664b583 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManagerTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/MulticriteriaManagerTest.kt @@ -3,7 +3,9 @@ package org.evomaster.core.search.algorithms import io.mockk.every import io.mockk.mockk import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming -import org.evomaster.client.java.instrumentation.shared.dto.ControlDependenceGraphDto +import org.evomaster.client.java.controller.api.dto.BranchObjectiveDto +import org.evomaster.client.java.controller.api.dto.ControlDependenceGraphDto +import org.evomaster.client.java.controller.api.dto.DependencyEdgeDto import org.evomaster.core.search.service.Archive import org.evomaster.core.search.service.IdMapper import org.junit.jupiter.api.Assertions.* @@ -91,18 +93,18 @@ class MulticriteriaManagerTest { dto.className = "TestClass" dto.methodName = "testMethod" - val rootObj = ControlDependenceGraphDto.BranchObjectiveDto() + val rootObj = BranchObjectiveDto() rootObj.id = rootId rootObj.descriptiveId = ObjectiveNaming.BRANCH + "_$rootId" - val childObj = ControlDependenceGraphDto.BranchObjectiveDto() + val childObj = BranchObjectiveDto() childObj.id = childId childObj.descriptiveId = ObjectiveNaming.BRANCH + "_$childId" dto.objectives = listOf(rootObj, childObj) dto.rootObjectiveIds = listOf(rootId) - val edge = ControlDependenceGraphDto.DependencyEdgeDto() + val edge = DependencyEdgeDto() edge.parentObjectiveId = rootId edge.childObjectiveId = childId @@ -116,7 +118,7 @@ class MulticriteriaManagerTest { dto.className = "TestClass" dto.methodName = "testMethod" - val obj = ControlDependenceGraphDto.BranchObjectiveDto() + val obj = BranchObjectiveDto() obj.id = id obj.descriptiveId = ObjectiveNaming.BRANCH + "_$id" diff --git a/docs/reused_code.md b/docs/reused_code.md index b5cef3beea..ab7ae6f585 100644 --- a/docs/reused_code.md +++ b/docs/reused_code.md @@ -14,4 +14,30 @@ are listed here: Released under GNU Lesser General Public * _RegexDistanceUtilsTest.java_: from [EvoSuite](http://www.evosuite.org) unit test generator. - Released under GNU Lesser General Public \ No newline at end of file + Released under GNU Lesser General Public + +* The following files in the graphs module were adapted from [EvoSuite](http://www.evosuite.org) + unit test generator. Released under GNU Lesser General Public License: + + * _AnnotatedLabel.java_: Label wrapper with bytecode instruction metadata. + * _AnnotatedMethodNode.java_: MethodNode wrapper with additional analysis data. + * _CFGClassVisitor.java_: ASM class visitor for CFG construction. + * _CFGMethodVisitor.java_: ASM method visitor for CFG construction. + * _EvoMasterGraph.java_: Base graph class extending JGraphT + * _GraphPool.java_: Pool for storing and retrieving CFG/CDG instances. + * _ActualControlFlowGraph.java_: Reduced CFG containing only branch nodes. + * _BasicBlock.java_: Represents a basic block in the CFG. + * _BytecodeInstruction.java_: Represents a single bytecode instruction. + * _BytecodeInstructionPool.java_: Pool for bytecode instruction instances. + * _CFGGenerator.java_: Generates CFGs from bytecode. + * _ControlDependency.java_: Represents a control dependency relationship. + * _ControlFlowEdge.java_: Edge in the control flow graph. + * _ControlFlowGraph.java_: Abstract base class for control flow graphs. + * _EntryBlock.java_: Represents the entry point of a CFG. + * _ExitBlock.java_: Represents exit points of a CFG. + * _RawControlFlowGraph.java_: Full CFG with all bytecode instructions. + * _Branch.java_: Represents a branch in the CFG. + * _BranchPool.java_: Pool for branch instances. + * _ControlDependenceGraph.java_: Computes CDG from CFG using dominating frontiers. + * _DominatorNode.java_: Auxiliary data structure for dominator computation. + * _DominatorTree.java_: Computes immediate dominators using Lengauer-Tarjan algorithm. \ No newline at end of file From 2484d00e893cf2dcf029d844fd378d332906bd43 Mon Sep 17 00:00:00 2001 From: francastagna Date: Mon, 15 Dec 2025 12:59:36 -0300 Subject: [PATCH 17/21] updating comment --- .../evomaster/client/java/controller/api/dto/SutRunDto.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java index f585fcb760..d63f97394c 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java @@ -51,7 +51,10 @@ public class SutRunDto { public Boolean enableControlDependenceGraphs; /** - * Whether to write generated graphs (DOT/PNGs) to disk on the agent side + * Whether to write generated graphs (DOT/PNGs) to disk on the agent side. + *

+ * Debugging only: when enabled, graph files are written to a fixed location, + * so running multiple experiments in parallel will cause file overwrites. */ public Boolean writeCfg; From d509a3d673922b721f45a18931fc86cf775cb974 Mon Sep 17 00:00:00 2001 From: francastagna Date: Thu, 15 Jan 2026 21:06:43 -0300 Subject: [PATCH 18/21] change writeCfg config default value to false --- core/src/main/kotlin/org/evomaster/core/EMConfig.kt | 3 ++- docs/options.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 349f00a58d..dab3b75731 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -2391,8 +2391,9 @@ class EMConfig { * Enable writing Control-Flow and related graphs (e.g., DOT/PNG) on the agent side. * This controls only the persistence of graphs to disk; graph creation is controlled separately. */ + @Experimental @Cfg("Enable writing CFG/CDG graphs to disk on the agent side") - var writeCfg: Boolean = true + var writeCfg: Boolean = false @Cfg("Specify whether to employ smart database clean to clear data in the database if the SUT has." + "`null` represents to employ the setting specified on the EM driver side") diff --git a/docs/options.md b/docs/options.md index 9d4b7a0e39..daa4eaa922 100644 --- a/docs/options.md +++ b/docs/options.md @@ -233,7 +233,6 @@ There are 3 types of options: |`useResponseDataPool`| __Boolean__. Enable the collection of response data, to feed new individuals based on field names matching. *Default value*: `true`.| |`useTimeInFeedbackSampling`| __Boolean__. Whether to use timestamp info on the execution time of the tests for sampling (e.g., to reward the quickest ones). *Default value*: `true`.| |`weightBasedMutationRate`| __Boolean__. Whether to enable a weight-based mutation rate. *Default value*: `true`.| -|`writeCfg`| __Boolean__. Enable writing CFG/CDG graphs to disk on the agent side. *Default value*: `true`.| |`writeExtraHeuristicsFile`| __Boolean__. Whether we should collect data on the extra heuristics. Only needed for experiments. *Default value*: `false`.| |`writeStatistics`| __Boolean__. Whether or not writing statistics of the search process. This is only needed when running experiments with different parameter settings. *Default value*: `false`.| |`writeWFCReport`| __Boolean__. Output a JSON file representing statistics of the fuzzing session, written in the WFC Report format. This also includes a index.html web application to visualize such data. *Default value*: `true`.| @@ -327,5 +326,6 @@ There are 3 types of options: |`useWeightedSampling`| __Boolean__. When sampling from archive based on targets, decide whether to use weights based on properties of the targets (e.g., a target likely leading to a flag will be sampled less often). *Default value*: `false`.| |`vulnerableInputClassificationStrategy`| __Enum__. Strategy to classify inputs for potential vulnerability classes related to an REST endpoint. *Valid values*: `MANUAL, LLM`. *Default value*: `MANUAL`.| |`wbProbabilityUseDataPool`| __Double__. Specify the probability of using the data pool when sampling test cases. This is for white-box (wb) mode. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.2`.| +|`writeCfg`| __Boolean__. Enable writing CFG/CDG graphs to disk on the agent side. *Default value*: `false`.| |`writeSnapshotTestsIntervalInSeconds`| __Int__. The size (in seconds) of the interval that the snapshots will be printed, if enabled. *Default value*: `3600`.| |`xss`| __Boolean__. To apply XSS detection as part of security testing. *Default value*: `false`.| From f725bf996a13cd190b73f3d027ceaabcbc244c43 Mon Sep 17 00:00:00 2001 From: francastagna Date: Thu, 15 Jan 2026 21:09:23 -0300 Subject: [PATCH 19/21] improve error handling of IOException in EvoMasterGraph --- .../client/java/instrumentation/graphs/EvoMasterGraph.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/EvoMasterGraph.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/EvoMasterGraph.java index ec5336c1a6..c9eb7e2536 100755 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/EvoMasterGraph.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/EvoMasterGraph.java @@ -808,8 +808,7 @@ private void toDot(String filename) { SimpleLogger.info("exportet " + getName()); } } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + SimpleLogger.error("Failed to export graph " + getName() + " to DOT format", e); } } } From b5397213f948b9addefa89c7b34b410c38515aa9 Mon Sep 17 00:00:00 2001 From: francastagna Date: Thu, 15 Jan 2026 21:10:47 -0300 Subject: [PATCH 20/21] remove old comment in BranchPool --- .../java/instrumentation/graphs/cfg/branch/BranchPool.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/branch/BranchPool.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/branch/BranchPool.java index 2fbe794d44..cdc6917e30 100755 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/branch/BranchPool.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/cfg/branch/BranchPool.java @@ -53,10 +53,7 @@ public static void resetForTesting(ClassLoader classLoader) { pool.branchOrdinalCounters.clear(); pool.branchCounter = 0; } - } - // fill the pool - - + } /** * Called by the BytecodeInstructionPool whenever it detects an instruction From 6f8ebebd557edcb35b9ffc4f55bde7a3d9e448ec Mon Sep 17 00:00:00 2001 From: francastagna Date: Thu, 15 Jan 2026 21:12:24 -0300 Subject: [PATCH 21/21] replaced all System.out.println() calls with SimpleLogger calls in ControlDependenceGraphConfig --- .../graphs/ControlDependenceGraphConfig.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/ControlDependenceGraphConfig.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/ControlDependenceGraphConfig.java index 14b5e500ad..07fcc09432 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/ControlDependenceGraphConfig.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/graphs/ControlDependenceGraphConfig.java @@ -1,5 +1,7 @@ package org.evomaster.client.java.instrumentation.graphs; +import org.evomaster.client.java.utils.SimpleLogger; + /** * Runtime configuration for control-dependence graph instrumentation features. * Populated via the agent control channel. @@ -9,30 +11,23 @@ public class ControlDependenceGraphConfig { private static volatile boolean enableGraphs = false; private static volatile boolean writeCfg = false; - public static boolean isGraphsEnabled() { - System.out.println("enableGraphs: " + enableGraphs); + SimpleLogger.debug("enableGraphs: " + enableGraphs); return enableGraphs; } public static void setEnableGraphs(boolean value) { - System.out.println("----------------------------------------------"); - System.out.println("Setting enableGraphs to " + value); - System.out.println("----------------------------------------------"); + SimpleLogger.info("Setting enableGraphs to " + value); enableGraphs = value; } public static boolean isWriteCfgEnabled() { - System.out.println("----------------------------------------------"); - System.out.println("writeCfg: " + writeCfg); - System.out.println("----------------------------------------------"); + SimpleLogger.debug("writeCfg: " + writeCfg); return writeCfg; } public static void setWriteCfgEnabled(boolean value) { - System.out.println("----------------------------------------------"); - System.out.println("Setting writeCfg to " + value); - System.out.println("----------------------------------------------"); + SimpleLogger.info("Setting writeCfg to " + value); writeCfg = value; } }