Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.evomaster.client.java.instrumentation;

import java.io.Serializable;
import java.util.Objects;

/**
* Parsed representation of a Branch objective descriptive id produced by ObjectiveNaming.branchObjectiveName.
* Fields mirror what is encoded in the id.
*/
public class BranchTargetDescriptor implements Serializable {

private static final long serialVersionUID = 43L;

public final String classNameDots;
public final int line;
public final int positionInLine;
public final boolean thenBranch;
public final int opcode;

public BranchTargetDescriptor(String classNameDots, int line, int positionInLine, boolean thenBranch, int opcode) {
this.classNameDots = Objects.requireNonNull(classNameDots);
this.line = line;
this.positionInLine = positionInLine;
this.thenBranch = thenBranch;
this.opcode = opcode;
if (line <= 0 || positionInLine < 0) {
throw new IllegalArgumentException("Invalid line/position for branch target");
}
}
}


Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package org.evomaster.client.java.instrumentation;

import org.evomaster.client.java.instrumentation.object.ClassToSchema;
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.cfg.CFGRecorder;
import org.evomaster.client.java.instrumentation.cfg.ControlFlowGraph;
import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming;

import java.util.ArrayList;
import java.util.Collection;
Expand Down Expand Up @@ -146,4 +147,97 @@ public static void extractSpecifiedDto(List<String> dtoNames){
UnitsInfoRecorder.registerSpecifiedDtoSchema(ExtractJvmClass.extractAsSchema(dtoNames));
}

/**
* Expose the set of discovered Control Flow Graphs for instrumented methods.
*/
public static List<ControlFlowGraph> getControlFlowGraphs(){
return CFGRecorder.getAll();
}

/**
* Compute and return ALL branch target numeric ids based on the complete CFG set.
* This includes targets that have not been executed yet.
*/
public static List<Integer> getAllBranchTargetIds(){
List<Integer> ids = new ArrayList<>();
for (ControlFlowGraph cfg : CFGRecorder.getAll()){
String className = cfg.getClassName(); // bytecode name
// Build per-line indices
java.util.Set<Integer> allLines = new java.util.LinkedHashSet<>(cfg.getInstructionIndexToLineNumber().values());
for (Integer line : allLines){
java.util.List<Integer> branchIndices = cfg.getBranchInstructionIndicesForLine(line);
for (int pos = 0; pos < branchIndices.size(); pos++){
int insnIdx = branchIndices.get(pos);
Integer opcode = cfg.getInstructionIndexToOpcode().get(insnIdx);
if (opcode == null) continue;
// create both true/false sides
String dTrue = org.evomaster.client.java.instrumentation.shared.ObjectiveNaming.branchObjectiveName(
className, line, pos, true, opcode);
String dFalse = org.evomaster.client.java.instrumentation.shared.ObjectiveNaming.branchObjectiveName(
className, line, pos, false, opcode);
int idT = ObjectiveRecorder.getMappedId(dTrue);
int idF = ObjectiveRecorder.getMappedId(dFalse);
ids.add(idT);
ids.add(idF);
}
}
}
return ids;
}

/**
* Convenience: get coverage TargetInfo for all branch targets in the CFGs.
*/
public static List<TargetInfo> getAllBranchTargetInfos(){
List<Integer> ids = getAllBranchTargetIds();
return getTargetInfos(ids, false, true);
}

/**
* Parse a branch descriptive id created by ObjectiveNaming.branchObjectiveName into a structured descriptor.
* Expected format:
* Branch_at_<class.with.dots>_at_line_00019_position_<pos>_(trueBranch|falseBranch)_<opcode>
*/
public static BranchTargetDescriptor parseBranchDescriptiveId(String descriptiveId){
if (descriptiveId == null || !descriptiveId.startsWith(ObjectiveNaming.BRANCH + "_at_")) {
throw new IllegalArgumentException("Not a branch descriptive id: " + descriptiveId);
}
try {
// strip "Branch_at_"
String rest = descriptiveId.substring((ObjectiveNaming.BRANCH + "_at_").length());
// split class and remainder
int idxAtLine = rest.indexOf("_at_line_");
String classDots = rest.substring(0, idxAtLine);
String afterLine = rest.substring(idxAtLine + "_at_line_".length());
// line is 5 digits padded; read until next underscore
int idxPos = afterLine.indexOf("_position_");
String linePadded = afterLine.substring(0, idxPos);
int line = Integer.parseInt(linePadded);
String afterPos = afterLine.substring(idxPos + "_position_".length());
// afterPos: "<pos>_trueBranch_<opcode>" or "<pos>_falseBranch_<opcode>"
int idxBranchTag = afterPos.indexOf("_" + ObjectiveNaming.TRUE_BRANCH.substring(1));
boolean thenBranch;
int posEndIdx;
if (idxBranchTag >= 0) {
thenBranch = true;
posEndIdx = idxBranchTag;
} else {
String falseTag = "_" + ObjectiveNaming.FALSE_BRANCH.substring(1);
idxBranchTag = afterPos.indexOf(falseTag);
if (idxBranchTag < 0) {
throw new IllegalArgumentException("Missing branch tag in id: " + descriptiveId);
}
thenBranch = false;
posEndIdx = idxBranchTag;
}
int position = Integer.parseInt(afterPos.substring(0, posEndIdx));
// opcode after last underscore
int lastUnderscore = afterPos.lastIndexOf('_');
int opcode = Integer.parseInt(afterPos.substring(lastUnderscore + 1));
return new BranchTargetDescriptor(classDots, line, position, thenBranch, opcode);
} catch (RuntimeException ex){
throw new IllegalArgumentException("Failed to parse branch descriptive id: " + descriptiveId, ex);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@



import org.evomaster.client.java.instrumentation.cfg.CFGGenerator;
import org.evomaster.client.java.instrumentation.coverage.visitor.classv.CoverageClassVisitor;
import org.evomaster.client.java.instrumentation.coverage.visitor.classv.ThirdPartyClassVisitor;
import org.evomaster.client.java.instrumentation.shared.ClassName;
Expand Down Expand Up @@ -66,6 +67,14 @@ public byte[] transformBytes(ClassLoader classLoader, ClassName className, Class
reader.accept(cn, readFlags);

if(canInstrumentForCoverage(className)){
// Build and store CFGs for the class methods (best-effort, limited to in-scope classes)
try {
CFGGenerator.computeAndRegister(cn);
} catch (Throwable t) {
// keep instrumentation robust: a failure to build CFGs must not prevent coverage instrumentation
SimpleLogger.warn("Failed to build CFG for " + className.getFullNameWithDots() + " : " + t.getMessage());
}

cv = new CoverageClassVisitor(cv, className);
} else {
cv = new ThirdPartyClassVisitor(cv, className);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package org.evomaster.client.java.instrumentation.cfg;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;

/**
* A basic block groups a consecutive range of bytecode instructions with a single entry and single exit,
* and tracks control-flow successors and predecessors at block granularity.
*/
public class BasicBlock {

private final int id;
private final int startInstructionIndex;
private final int endInstructionIndex;

private final Set<Integer> successorBlockIds = new LinkedHashSet<>();
private final Set<Integer> predecessorBlockIds = new LinkedHashSet<>();

public BasicBlock(int id, int startInstructionIndex, int endInstructionIndex) {
if (startInstructionIndex < 0 || endInstructionIndex < startInstructionIndex) {
throw new IllegalArgumentException("Invalid instruction index range for basic block");
}
this.id = id;
this.startInstructionIndex = startInstructionIndex;
this.endInstructionIndex = endInstructionIndex;
}

public int getId() {
return id;
}

public int getStartInstructionIndex() {
return startInstructionIndex;
}

public int getEndInstructionIndex() {
return endInstructionIndex;
}

public void addSuccessor(int blockId) {
successorBlockIds.add(blockId);
}

public void addPredecessor(int blockId) {
predecessorBlockIds.add(blockId);
}

public Set<Integer> getSuccessorBlockIds() {
return Collections.unmodifiableSet(successorBlockIds);
}

public Set<Integer> getPredecessorBlockIds() {
return Collections.unmodifiableSet(predecessorBlockIds);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BasicBlock)) return false;
BasicBlock that = (BasicBlock) o;
return id == that.id;
}

@Override
public int hashCode() {
return Objects.hash(id);
}

@Override
public String toString() {
return "BasicBlock{" +
"id=" + id +
", start=" + startInstructionIndex +
", end=" + endInstructionIndex +
", succ=" + successorBlockIds +
", pred=" + predecessorBlockIds +
'}';
}
}


Loading
Loading