we are moving form post dominators.

This commit is contained in:
Mann Patel
2025-12-04 18:41:14 -07:00
parent ab9d95be1f
commit c021b1eabf
16 changed files with 526 additions and 564 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -1,12 +1,14 @@
# Final Project
---
### Things
1. Forward Slice => All statements that could be affected if you change a given statement.Think of it like ripple effects in water:
1. To run the program please run the script:
```bash
chmod +x build.sh
./build.sh
```
2. To run the program with bash run the command:
```bash
bash build.sh
```
### Run
```bash
# Show all dependencies for a file
./run.sh examples/Example1.java
# Analyze impact of line 3
./run.sh examples/Example1.java 3
```

View File

@@ -1,43 +0,0 @@
#!/bin/bash
set -e
GREEN="\033[32m"
RESET="\033[0m"
echo ""
echo "==== CPSC 449 Project ===="
# Create lib directory
mkdir -p bin
echo "1. Checking dependencies"
if [ ! -f "lib/antlr-4.9.3-complete.jar" ]; then
echo " Please Download ANTLR"
fi
if [ ! -f "lib/jgrapht-core-1.5.1.jar" ]; then
echo " Please Download JGraphT"
fi
if [ ! -f "lib/jgrapht-io-1.5.1.jar" ]; then
echo " Please Download JGraphT IO"
fi
echo " [x] All dependencies acounted for"
CP="lib/antlr-4.9.3-complete.jar:lib/jgrapht-core-1.5.1.jar:lib/jgrapht-io-1.5.1.jar"
echo "2. Compiling CFG packages"
javac -d bin -cp "$CP" src/org/lsmr/cfg/*.java
echo " [x] CFG compiled"
echo "3. Compiling PDG packages"
javac -d bin -cp "$CP:bin" src/pdg/*.java
echo " [x] PDG compiled"
echo ""
echo -e "${GREEN}==== Compilation Complete! ====${RESET}"
echo -e "${GREEN}Compiled classes are in: bin/${RESET}"
echo ""
echo -e "${GREEN}>> To run this program:${RESET}"
echo -e "${GREEN} java -cp bin:$CP YourMainClass${RESET}"
echo ""

BIN
lib/.DS_Store vendored Normal file

Binary file not shown.

27
run.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/bin/bash
if [ "$#" -lt 1 ]; then
echo "Usage: ./run.sh <file> [line]"
echo "Example: ./run.sh examples/Example1.java 3"
exit 1
fi
# Check for antlr jar
if [ ! -f "lib/antlr-4.13.2-complete.jar" ]; then
echo "ERROR: Put antlr-4.13.2-complete.jar in lib/"
exit 1
fi
# Build if needed be
if [ ! -d "bin/pdg" ]; then
echo "Building..."
mkdir -p bin
CP="lib/antlr-4.13.2-complete.jar"
javac -d bin -cp "$CP" src/org/lsmr/cfg/*.java || exit 1
javac -d bin -cp "$CP:bin" src/pdg/PDG.java || exit 1
javac -d bin -cp "$CP:bin" src/CFGBuilder.java src/PDGTool.java || exit 1
echo "Build complete!"
fi
# Run
java -cp "bin:lib/antlr-4.13.2-complete.jar" PDGTool "$@"

BIN
src/.DS_Store vendored

Binary file not shown.

130
src/CFGBuilder.java Normal file
View File

@@ -0,0 +1,130 @@
import org.lsmr.cfg.*;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import java.io.*;
import java.util.*;
public class CFGBuilder {
private Map<Node, Integer> nodeToLine = new HashMap<>();
/**
* Build CFG from a Java file
*/
public ControlFlowGraph buildFromFile(String filename) throws IOException {
System.out.println("\n\tBuilding CFG from " + filename);
// Parse the file
CharStream input = CharStreams.fromFileName(filename);
Java1_4Lexer lexer = new Java1_4Lexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
Java1_4Parser parser = new Java1_4Parser(tokens);
parser.removeErrorListeners();
ParseTree tree = parser.compilationUnit();
// build CFG using professor's NodeBuilder
NodeBuilder builder = new NodeBuilder();
builder.visit(tree);
List<ControlFlowGraph> cfgs = builder.getCFGs();
if (cfgs.isEmpty()) {
throw new RuntimeException("No methods found in file");
}
if (cfgs.size() > 1) {
System.out.println("Note: File has " + cfgs.size() + " methods, using first method");
}
ControlFlowGraph cfg = cfgs.get(0);
extractLineNumbers(cfg, tree);
System.out.println("CFG built: " + cfg.nodes().size() + " nodes, " + cfg.edges().size() + " edges");
return cfg;
}
private void extractLineNumbers(ControlFlowGraph cfg, ParseTree tree) {
Map<String, Integer> labelToLine = new HashMap<>();
collectLineNumbers(tree, labelToLine);
for (Node node : cfg.nodes()) {
String label = node.label();
Integer line = labelToLine.get(label);
if (line == null) {
for (Map.Entry<String, Integer> entry : labelToLine.entrySet()) {
if (label.contains(entry.getKey()) || entry.getKey().contains(label)) {
line = entry.getValue();
break;
}
}
}
nodeToLine.put(node, line != null ? line : -1);
}
}
private void collectLineNumbers(ParseTree tree, Map<String, Integer> map) {
if (tree instanceof ParserRuleContext) {
ParserRuleContext ctx = (ParserRuleContext) tree;
if (ctx.start != null) {
String text = ctx.getText();
if (text != null && text.length() < 200 && text.length() > 0) {
map.put(text, ctx.start.getLine());
}
}
}
for (int i = 0; i < tree.getChildCount(); i++) {
collectLineNumbers(tree.getChild(i), map);
}
}
public int getLineNumber(Node node) {
return nodeToLine.getOrDefault(node, -1);
}
public List<Integer> getAllLineNumbers() {
Set<Integer> lines = new TreeSet<>();
for (Integer line : nodeToLine.values()) {
if (line > 0) lines.add(line);
}
return new ArrayList<>(lines);
}
public List<Node> findNodesAtLine(ControlFlowGraph cfg, int lineNumber) {
List<Node> result = new ArrayList<>();
for (Node node : cfg.nodes()) {
if (getLineNumber(node) == lineNumber) {
result.add(node);
}
}
return result;
}
public void printLineMapping(ControlFlowGraph cfg) {
System.out.println("\n Line Number Mapping");
Map<Integer, List<String>> lineToNodes = new TreeMap<>();
for (Node node : cfg.nodes()) {
int line = getLineNumber(node);
if (line > 0) {
lineToNodes.putIfAbsent(line, new ArrayList<>());
lineToNodes.get(line).add(node.label());
}
}
for (Map.Entry<Integer, List<String>> entry : lineToNodes.entrySet()) {
System.out.println("Line " + entry.getKey() + ":");
for (String label : entry.getValue()) {
System.out.println(" " + label);
}
}
System.out.println();
}
}

106
src/PDGTool.java Normal file
View File

@@ -0,0 +1,106 @@
import org.lsmr.cfg.*;
import pdg.PDG;
import java.io.*;
import java.util.*;
/**
* PDG Tool: Change Impact Analysis
* Usage: java PDGTool <java-file> [line-number]
*/
public class PDGTool {
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("Usage: java PDGTool <java-file> [line-number]");
System.exit(1);
}
String filename = args[0];
Integer targetLine = args.length > 1 ? Integer.parseInt(args[1]) : null;
try {
analyzePDG(filename, targetLine);
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
e.printStackTrace();
System.exit(1);
}
}
private static void analyzePDG(String filename, Integer targetLine) throws IOException {
System.out.println("PDG Analysis Tool");
System.out.println("File: " + filename);
if (targetLine != null) {
System.out.println("Target line: " + targetLine);
}
// Step 1: Build CFG
CFGBuilder cfgBuilder = new CFGBuilder();
ControlFlowGraph cfg = cfgBuilder.buildFromFile(filename);
// Step 2: Build PDG
PDG pdg = new PDG(cfg);
// Step 3: Show results
System.out.println("\n=== Available Lines ===");
List<Integer> lines = cfgBuilder.getAllLineNumbers();
System.out.println("Lines with statements: " + lines);
// If specific line requested, compute impact
if (targetLine != null) {
computeImpact(cfg, pdg, cfgBuilder, targetLine);
} else {
// Show all dependencies
System.out.println("\n=== All Dependencies ===");
pdg.printPDG();
cfgBuilder.printLineMapping(cfg);
}
}
private static void computeImpact(ControlFlowGraph cfg, PDG pdg,
CFGBuilder cfgBuilder, int targetLine) {
System.out.println("\n=== Impact Analysis for Line " + targetLine + " ===");
// Find nodes at target line
List<Node> nodesAtLine = cfgBuilder.findNodesAtLine(cfg, targetLine);
if (nodesAtLine.isEmpty()) {
System.out.println("WARNING: No executable statement at line " + targetLine);
return;
}
System.out.println("\nStatement(s) at line " + targetLine + ":");
for (Node node : nodesAtLine) {
System.out.println(" " + node.label());
}
// Compute forward slice
Set<String> allImpacted = new HashSet<>();
for (Node node : nodesAtLine) {
Set<String> impacted = pdg.computeForwardSlice(node.label());
allImpacted.addAll(impacted);
}
// Convert to line numbers
Set<Integer> impactedLines = new TreeSet<>();
for (Node node : cfg.nodes()) {
if (allImpacted.contains(node.label())) {
int line = cfgBuilder.getLineNumber(node);
if (line > 0) {
impactedLines.add(line);
}
}
}
System.out.println("\n=== IMPACTED LINES ===");
if (impactedLines.isEmpty()) {
System.out.println(" (none)");
} else {
for (int line : impactedLines) {
System.out.println(" Line " + line);
}
System.out.println("\nTotal: " + impactedLines.size() + " lines impacted");
}
}
}

View File

@@ -1,55 +0,0 @@
import org.lsmr.cfg.ControlFlowGraph;
import org.lsmr.cfg.Node;
import org.lsmr.cfg.Edge;
import pdg.PDG;
import java.util.Set;
public class Test {
public static void main(String[] args) {
System.out.println("=== PDG Test ===\n");
// Create a simple CFG manually
ControlFlowGraph cfg = new ControlFlowGraph("testMethod");
// Create nodes
Node n1 = cfg.buildNode("int x = 5");
Node n2 = cfg.buildNode("int y = x + 10");
Node n3 = cfg.buildNode("int z = y * 2");
Node n4 = cfg.buildNode("System.out.println(z)");
// Connect nodes: entry -> n1 -> n2 -> n3 -> n4 -> exit
cfg.buildEdge(cfg.entry, n1, Edge.EdgeLabel.BLANK);
cfg.buildEdge(n1, n2, Edge.EdgeLabel.BLANK);
cfg.buildEdge(n2, n3, Edge.EdgeLabel.BLANK);
cfg.buildEdge(n3, n4, Edge.EdgeLabel.BLANK);
cfg.buildEdge(n4, cfg.normalExit, Edge.EdgeLabel.BLANK);
System.out.println("CFG created with " + cfg.nodes().size() + " nodes");
// Create PDG from CFG
PDG pdg = new PDG(cfg);
// Print CFG structure
pdg.printCFG();
// Print PDG structure
pdg.printPDG();
// Test impact analysis
System.out.println("\n=== Impact Analysis ===");
String[] testNodes = {"int x = 5", "int y = x + 10", "int z = y * 2"};
for (String nodeLabel : testNodes) {
Set<String> impacted = pdg.computeForwardSlice(nodeLabel);
System.out.println("\nChanging '" + nodeLabel + "' impacts:");
for (String label : impacted) {
if (!label.startsWith("*")) { // Skip special nodes
System.out.println(" - " + label);
}
}
}
System.out.println("\n=== Test Complete ===");
}
}

BIN
src/org/.DS_Store vendored

Binary file not shown.

BIN
src/org/lsmr/.DS_Store vendored

Binary file not shown.

BIN
src/org/lsmr/cfg/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -1,454 +1,309 @@
// program Dependence Graph implementation using the Profs CFG.
// combines control dependence and data dependence.
package pdg;
import org.lsmr.cfg.ControlFlowGraph;
import org.lsmr.cfg.Node;
import org.lsmr.cfg.Edge;
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.lsmr.cfg.*;
import java.util.*;
/**
* Simple Program Dependence Graph (PDG)
* Computes control and data dependencies for a CFG
*/
public class PDG {
private ControlFlowGraph cfg;
private Graph<PDGNode, PDGEdge> pdg;
private Map<Node, PDGNode> cfgToPdgMap;
// Analysis results
private Map<Node, Set<Node>> postDominators;
private Map<Node, Set<Node>> controlDependence;
private Map<Node, Set<Node>> dataDependence;
private ControlFlowGraph cfg;
private Map<String, Set<String>> controlDeps;
private Map<String, Set<String>> dataDeps;
public PDG(ControlFlowGraph cfg) {
this.cfg = cfg;
this.pdg = new DefaultDirectedGraph<>(PDGEdge.class);
this.cfgToPdgMap = new HashMap<>();
this.postDominators = new HashMap<>();
this.controlDependence = new HashMap<>();
this.dataDependence = new HashMap<>();
this.controlDeps = new HashMap<>();
this.dataDeps = new HashMap<>();
buildPDG();
System.out.println("Building PDG...");
computeControlDependencies();
computeDataDependencies();
System.out.println("PDG complete!");
}
// build the complete PDG.
private void buildPDG() {
// Step 1: create PDG nodes from CFG nodes
createPDGNodes();
// Step 2: compute control dependence
computeControlDependence();
// Step 3: compute data dependence
computeDataDependence();
// Step 4: add edges to PDG
addPDGEdges();
}
/**
* Compute control dependencies using simple reachability
*/
private void computeControlDependencies() {
System.out.println("\tComputing control dependencies...");
// Create a PDG node for each CFG node.
private void createPDGNodes() {
for (Node cfgNode : cfg.nodes()) {
PDGNode pdgNode = new PDGNode(cfgNode);
pdg.addVertex(pdgNode);
cfgToPdgMap.put(cfgNode, pdgNode);
}
}
// Compute control dependence using post-dominator analysis.
// Source: https://pages.cs.wisc.edu/~fischer/cs701.f14/lectures/L6.4up.pdf
private void computeControlDependence() {
// If, compute post-dominators
computePostDominators();
// Then, compute control dependence
for (Node node : cfg.nodes()) {
controlDependence.put(node, new HashSet<>());
}
Set<String> deps = new HashSet<>();
// for each edge X -> Y in the CFG
for (Node x : cfg.nodes()) {
for (Edge edge : x.outEdges()) {
Node y = edge.target();
if (y == null) continue;
// find all nodes that are control-dependent on X via this edge
for (Node node : cfg.nodes()) {
// Node is control-dependent on X if:
// 1. Node post-dominates Y (the successor)
// 2. Node does NOT post-dominate X
if (postDominates(node, y) && !postDominates(node, x)) {
controlDependence.get(x).add(node);
for (Edge inEdge : node.inEdges()) {
Node pred = inEdge.source();
if (pred.outEdges().size() > 1) {
deps.add(pred.label());
if (controlDeps.containsKey(pred.label())) {
deps.addAll(controlDeps.get(pred.label()));
}
}
}
controlDeps.put(node.label(), deps);
}
System.out.println("\t\tFound " + countDependencies(controlDeps) + " control dependencies");
}
// compute post-dominators using fixed-point algorithm.
// node Y post-dominates X if all paths from X to exit go through Y.
private void computePostDominators() {
List<Node> allNodes = cfg.nodes();
Node exitNode = cfg.normalExit;
for (Node node : allNodes) {
postDominators.put(node, new HashSet<>(allNodes));
}
/**
* Compute data dependencies using reaching definitions
*/
private void computeDataDependencies() {
System.out.println(" Computing data dependencies...");
if (exitNode != null) {
postDominators.put(exitNode, new HashSet<>(Arrays.asList(exitNode)));
}
boolean changed = true;
while (changed) {
changed = false;
for (Node node : allNodes) {
if (node.equals(exitNode)) continue;
Set<Node> newPostDom = new HashSet<>();
newPostDom.add(node); // Node post-dominates itself
Set<Edge> successorEdges = node.outEdges();
if (!successorEdges.isEmpty()) {
boolean first = true;
for (Edge edge : successorEdges) {
Node successor = edge.target();
if (successor == null) continue;
if (first) {
newPostDom.addAll(postDominators.get(successor));
first = false;
} else {
newPostDom.retainAll(postDominators.get(successor));
}
}
}
if (!newPostDom.equals(postDominators.get(node))) {
postDominators.put(node, newPostDom);
changed = true;
}
}
}
}
private boolean postDominates(Node dominator, Node node) {
Set<Node> postDoms = postDominators.get(node);
return postDoms != null && postDoms.contains(dominator);
}
private void computeDataDependence() {
Map<Node, Set<String>> defs = new HashMap<>();
Map<Node, Set<String>> uses = new HashMap<>();
// 1. Find definitions and uses for each node
Map<String, Set<String>> defs = new HashMap<>();
Map<String, Set<String>> uses = new HashMap<>();
for (Node node : cfg.nodes()) {
defs.put(node, extractDefs(node));
uses.put(node, extractUses(node));
PDGNode pdgNode = cfgToPdgMap.get(node);
pdgNode.setDefs(defs.get(node));
pdgNode.setUses(uses.get(node));
String label = node.label();
defs.put(label, extractDefs(label));
uses.put(label, extractUses(label));
}
Map<Node, Set<Definition>> reachingDefs = computeReachingDefinitions(defs);
// 2. Compute reaching definitions
Map<String, Map<String, Set<String>>> reaching = computeReachingDefinitions(defs);
// 3. Build data dependencies
for (Node node : cfg.nodes()) {
dataDependence.put(node, new HashSet<>());
}
for (Node use : cfg.nodes()) {
Set<String> usedVars = uses.get(use);
Set<Definition> reaching = reachingDefs.get(use);
String label = node.label();
Set<String> deps = new HashSet<>();
for (Definition def : reaching) {
if (usedVars.contains(def.variable)) {
dataDependence.get(def.node).add(use);
for (String var : uses.get(label)) {
if (reaching.containsKey(label) && reaching.get(label).containsKey(var)) {
deps.addAll(reaching.get(label).get(var));
}
}
dataDeps.put(label, deps);
}
System.out.println(" Found " + countDependencies(dataDeps) + " data dependencies");
}
// Extract variable definitions from a CFG node.
private Set<String> extractDefs(Node node) {
Set<String> defs = new HashSet<>();
String label = node.label();
if (label.startsWith("*")) { // Skip special nodes
return defs;
}
if (label.contains("=") && !label.contains("==")) { // Look for assignments: variable = ...
try {
String[] parts = label.split("=");
if (parts.length >= 2) {
String varPart = parts[0].trim();
// handle "int v" or just "v"
String[] tokens = varPart.split("\\s+");
String varName = tokens[tokens.length - 1].replaceAll("[^a-zA-Z0-9_]", "");
if (varName.length() > 0 && Character.isJavaIdentifierStart(varName.charAt(0))) {
defs.add(varName);
}
}
} catch (Exception e) {
// parsing failed, ignore
}
}
return defs;
}
// Extract variable uses from a CFG node.
private Set<String> extractUses(Node node) {
Set<String> uses = new HashSet<>();
String label = node.label();
// Skip special nodes
if (label.startsWith("*")) {
return uses;
}
// For assignments, extract from right-hand side
if (label.contains("=") && !label.contains("==")) {
String[] parts = label.split("=", 2);
if (parts.length >= 2) {
String rhs = parts[1].trim().replace(";", "");
uses.addAll(extractVariablesFromExpression(rhs));
}
} else {
// For other statements, extract all variables
uses.addAll(extractVariablesFromExpression(label));
}
return uses;
}
// Extract variable names from an expression.
private Set<String> extractVariablesFromExpression(String expr) {
/**
* Extract variable definitions from a statement simple pattern matching for Java 1.4
*/
private Set<String> extractDefs(String statement) {
Set<String> vars = new HashSet<>();
// Remove common operators and split
String cleaned = expr.replaceAll("[+\\-*/()\\[\\]{}<>=!&|;,.]", " ");
String[] tokens = cleaned.split("\\s+");
// Skip special nodes
if (statement.startsWith("*")) return vars;
for (String token : tokens) {
token = token.trim();
if (token.isEmpty()) continue;
if (statement.contains("=") && !statement.contains("==")) {
String[] parts = statement.split("=");
if (parts.length > 0) {
String lhs = parts[0].trim();
String[] tokens = lhs.split("\\s+");
if (tokens.length > 0) {
String varName = tokens[tokens.length - 1];
varName = varName.replaceAll("[\\[\\].();,]", "");
if (!varName.isEmpty() && Character.isJavaIdentifierStart(varName.charAt(0))) {
vars.add(varName);
}
}
}
}
// Check if it's a valid identifier (not a number, not a keyword)
if (isValidIdentifier(token) && !isKeyword(token)) {
vars.add(token);
if (statement.contains("for") && statement.contains("(")) {
String forPart = statement.substring(statement.indexOf("("));
String[] forParts = forPart.split(";");
if (forParts.length >= 3) {
if (forParts[0].contains("=")) {
String[] initParts = forParts[0].split("=");
if (initParts.length > 0) {
String var = initParts[0].trim().replaceAll("[^a-zA-Z0-9_]", "");
if (!var.isEmpty()) vars.add(var);
}
}
String update = forParts[forParts.length - 1];
if (update.contains("++") || update.contains("--")) {
String var = update.replaceAll("[^a-zA-Z0-9_]", "");
if (!var.isEmpty()) vars.add(var);
}
}
}
return vars;
}
private boolean isValidIdentifier(String s) {
if (s == null || s.isEmpty()) return false;
/**
* Extract variable uses from a statement
*/
private Set<String> extractUses(String statement) {
Set<String> vars = new HashSet<>();
// Check if it's a number
try {
Integer.parseInt(s);
return false;
} catch (NumberFormatException e) {
// Not a number, continue
if (statement.startsWith("*")) return vars;
String[] tokens = statement.split("[\\s+\\-*/=<>!&|(){}\\[\\];,.]");
for (String token : tokens) {
token = token.trim();
if (!token.isEmpty() &&
Character.isJavaIdentifierStart(token.charAt(0)) &&
!isKeyword(token) &&
!token.matches("\\d+")) { r
vars.add(token);
}
}
vars.removeAll(extractDefs(statement));
if (!Character.isJavaIdentifierStart(s.charAt(0))) return false;
for (int i = 1; i < s.length(); i++) {
if (!Character.isJavaIdentifierPart(s.charAt(i))) return false;
}
return true;
return vars;
}
private boolean isKeyword(String s) {
/**
* Check if a word is a Java keyword
*/
private boolean isKeyword(String word) {
Set<String> keywords = new HashSet<>(Arrays.asList(
"if", "else", "while", "for", "return", "int", "boolean", "void", "class",
"public", "private", "static", "new", "this", "true", "false", "null",
"break", "continue", "switch", "case", "default", "try", "catch", "finally",
"throw", "throws", "extends", "implements", "abstract", "final", "native",
"synchronized", "transient", "volatile", "strictfp", "package", "import"
"if", "else", "while", "for", "do", "return", "break", "continue",
"int", "double", "float", "char", "boolean", "void", "String",
"new", "null", "true", "false", "class", "public", "private",
"static", "final", "this", "super", "try", "catch", "throw", "throws"
));
return keywords.contains(s);
return keywords.contains(word);
}
// Compute reaching definitions using fixed-point analysis.
private Map<Node, Set<Definition>> computeReachingDefinitions(Map<Node, Set<String>> defs) {
Map<Node, Set<Definition>> reachingDefs = new HashMap<>();
/**
* Compute reaching definitions using iterative data flow analysis
*/
private Map<String, Map<String, Set<String>>> computeReachingDefinitions(
Map<String, Set<String>> defs) {
// Initialize
Map<String, Map<String, Set<String>>> reaching = new HashMap<>();
for (Node node : cfg.nodes()) {
reachingDefs.put(node, new HashSet<>());
reaching.put(node.label(), new HashMap<>());
}
// Fixed-point iteration
boolean changed = true;
while (changed) {
int iterations = 0;
while (changed && iterations < 100) {
changed = false;
iterations++;
for (Node node : cfg.nodes()) {
Set<Definition> newReaching = new HashSet<>();
// Union of reaching definitions from all predecessors
Set<Edge> inEdges = node.inEdges();
for (Edge edge : inEdges) {
Node pred = edge.source();
Set<Definition> predReaching = new HashSet<>(reachingDefs.get(pred));
// Kill definitions of variables defined in pred
Set<String> predDefs = defs.get(pred);
predReaching.removeIf(def -> predDefs.contains(def.variable));
// Gen: Add new definitions from pred
for (String var : predDefs) {
predReaching.add(new Definition(pred, var));
String label = node.label();
Map<String, Set<String>> oldReaching = new HashMap<>(reaching.get(label));
Map<String, Set<String>> newReaching = new HashMap<>();
for (Edge inEdge : node.inEdges()) {
Node pred = inEdge.source();
String predLabel = pred.label();
if (reaching.containsKey(predLabel)) {
for (Map.Entry<String, Set<String>> entry : reaching.get(predLabel).entrySet()) {
String var = entry.getKey();
newReaching.putIfAbsent(var, new HashSet<>());
newReaching.get(var).addAll(entry.getValue());
}
}
newReaching.addAll(predReaching);
// Add predecessor's definitions
for (String var : defs.get(predLabel)) {
newReaching.putIfAbsent(var, new HashSet<>());
newReaching.get(var).clear();
newReaching.get(var).add(predLabel);
}
}
if (!newReaching.equals(reachingDefs.get(node))) {
reachingDefs.put(node, newReaching);
reaching.put(label, newReaching);
if (!mapsEqual(oldReaching, newReaching)) {
changed = true;
}
}
}
return reachingDefs;
return reaching;
}
// Add control and data dependence edges to PDG.
private void addPDGEdges() {
// Add control dependence edges
for (Node from : controlDependence.keySet()) {
PDGNode fromPDG = cfgToPdgMap.get(from);
for (Node to : controlDependence.get(from)) {
PDGNode toPDG = cfgToPdgMap.get(to);
PDGEdge edge = new PDGEdge(PDGEdge.EdgeType.CONTROL);
try {
pdg.addEdge(fromPDG, toPDG, edge);
} catch (IllegalArgumentException e) {
// Edge might already exist
}
}
}
// Add data dependence edges
for (Node from : dataDependence.keySet()) {
PDGNode fromPDG = cfgToPdgMap.get(from);
for (Node to : dataDependence.get(from)) {
PDGNode toPDG = cfgToPdgMap.get(to);
PDGEdge edge = new PDGEdge(PDGEdge.EdgeType.DATA);
try {
pdg.addEdge(fromPDG, toPDG, edge);
} catch (IllegalArgumentException e) {
// Edge might already exist
}
}
private boolean mapsEqual(Map<String, Set<String>> m1, Map<String, Set<String>> m2) {
if (m1.size() != m2.size()) return false;
for (String key : m1.keySet()) {
if (!m2.containsKey(key)) return false;
if (!m1.get(key).equals(m2.get(key))) return false;
}
return true;
}
private int countDependencies(Map<String, Set<String>> deps) {
int count = 0;
for (Set<String> set : deps.values()) {
count += set.size();
}
return count;
}
public Set<String> getDependencies(String nodeLabel) {
Set<String> all = new HashSet<>();
if (controlDeps.containsKey(nodeLabel)) {
all.addAll(controlDeps.get(nodeLabel));
}
if (dataDeps.containsKey(nodeLabel)) {
all.addAll(dataDeps.get(nodeLabel));
}
return all;
}
/**
* Compute forward slice from a given node label.
* Returns all nodes that could be impacted by a change to the given node.
**/
public Set<String> computeForwardSlice(String nodeLabel) {
Node startCFGNode = cfg.findNode(nodeLabel); // Find the CFG node with this label
if (startCFGNode == null) {
return Collections.emptySet();
}
return computeForwardSliceFromNode(startCFGNode);
}
Set<String> slice = new HashSet<>();
Queue<String> worklist = new LinkedList<>();
// Compute forward slice from a given node.
private Set<String> computeForwardSliceFromNode(Node startNode) {
PDGNode startPDGNode = cfgToPdgMap.get(startNode);
Set<String> impactedLabels = new HashSet<>();
Set<PDGNode> visited = new HashSet<>();
Queue<PDGNode> queue = new LinkedList<>();
queue.add(startPDGNode);
visited.add(startPDGNode);
while (!queue.isEmpty()) {
PDGNode current = queue.poll();
impactedLabels.add(current.getCfgNode().label());
// Follow all outgoing edges (both control and data)
Set<PDGEdge> outEdges = pdg.outgoingEdgesOf(current);
for (PDGEdge edge : outEdges) {
PDGNode target = pdg.getEdgeTarget(edge);
if (!visited.contains(target)) {
visited.add(target);
queue.add(target);
slice.add(nodeLabel);
worklist.add(nodeLabel);
while (!worklist.isEmpty()) {
String current = worklist.poll();
for (String node : getAllNodes()) {
if (!slice.contains(node)) {
Set<String> deps = getDependencies(node);
if (deps.contains(current)) {
slice.add(node);
worklist.add(node);
}
}
}
}
return impactedLabels;
return slice;
}
// Print the PDG structure.
public void printPDG() {
System.out.println("\n=== PDG Structure ===");
List<PDGNode> nodes = new ArrayList<>(pdg.vertexSet());
nodes.sort(Comparator.comparing(n -> n.getCfgNode().label()));
for (PDGNode node : nodes) {
System.out.print("Node: " + node.getCfgNode().label());
System.out.print(" [Defs: " + node.getDefs() + ", Uses: " + node.getUses() + "]");
System.out.println();
Set<PDGEdge> outEdges = pdg.outgoingEdgesOf(node);
for (PDGEdge edge : outEdges) {
PDGNode target = pdg.getEdgeTarget(edge);
System.out.println(" -> " + edge.getType() + " dep on: " + target.getCfgNode().label());
}
}
}
// Print CFG structure for debugging.
public void printCFG() {
System.out.println("\n=== CFG Structure ===");
System.out.println("Entry: " + cfg.entry.label());
System.out.println("Normal Exit: " + cfg.normalExit.label());
System.out.println("Abrupt Exit: " + cfg.abruptExit.label());
System.out.println("\nNodes:");
private Set<String> getAllNodes() {
Set<String> nodes = new HashSet<>();
for (Node node : cfg.nodes()) {
System.out.println(" " + node.label());
for (Edge edge : node.outEdges()) {
String target = edge.target() != null ? edge.target().label() : "null";
System.out.println(" -> " + edge.label() + " to " + target);
}
nodes.add(node.label());
}
return nodes;
}
// Get the PDG graph.
public Graph<PDGNode, PDGEdge> getGraph() {
return pdg;
public void printPDG() {
System.out.println("CONTROL DEPENDENCIES:");
for (Map.Entry<String, Set<String>> entry : controlDeps.entrySet()) {
if (!entry.getValue().isEmpty()) {
System.out.println(" " + entry.getKey());
for (String dep : entry.getValue()) {
System.out.println(" <- " + dep);
}
}
}
System.out.println("\nDATA DEPENDENCIES:");
for (Map.Entry<String, Set<String>> entry : dataDeps.entrySet()) {
if (!entry.getValue().isEmpty()) {
System.out.println(" " + entry.getKey());
for (String dep : entry.getValue()) {
System.out.println(" <- " + dep);
}
}
}
}
public Map<String, Set<String>> getControlDependencies() {
return new HashMap<>(controlDeps);
}
// Get the underlying CFG.
public ControlFlowGraph getCFG() {
return cfg;
}
//Helper class for tracking definitions
private static class Definition {
Node node;
String variable;
Definition(Node node, String variable) {
this.node = node;
this.variable = variable;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Definition)) return false;
Definition that = (Definition) o;
return node.equals(that.node) && variable.equals(that.variable);
}
@Override
public int hashCode() {
return Objects.hash(node, variable);
}
public Map<String, Set<String>> getDataDependencies() {
return new HashMap<>(dataDeps);
}
}

View File

@@ -1,29 +0,0 @@
/**
* Edge in the PDG representing either control or data dependence.
*/
package pdg;
import org.jgrapht.graph.DefaultEdge;
public class PDGEdge extends DefaultEdge {
public enum EdgeType {
CONTROL, // Control dependence
DATA // Data dependence
}
private EdgeType type;
public PDGEdge(EdgeType type) {
this.type = type;
}
public EdgeType getType() {
return type;
}
@Override
public String toString() {
return type.toString();
}
}

View File

@@ -1,60 +0,0 @@
/**
* PDG Node that wraps a CFG node (org.lsmr.cfg.Node) and adds def-use information.
*/
package pdg;
import org.lsmr.cfg.Node;
import java.util.*;
public class PDGNode {
private Node cfgNode;
private Set<String> defs;
private Set<String> uses;
public PDGNode(Node cfgNode) {
this.cfgNode = cfgNode;
this.defs = new HashSet<>();
this.uses = new HashSet<>();
}
public Node getCfgNode() {
return cfgNode;
}
public Set<String> getDefs() {
return defs;
}
public void setDefs(Set<String> defs) {
this.defs = defs;
}
public Set<String> getUses() {
return uses;
}
public void setUses(Set<String> uses) {
this.uses = uses;
}
@Override
public String toString() {
return "PDGNode{" + cfgNode.label() +
", defs=" + defs + ", uses=" + uses + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PDGNode)) return false;
PDGNode pdgNode = (PDGNode) o;
return cfgNode.equals(pdgNode.cfgNode);
}
@Override
public int hashCode() {
return Objects.hash(cfgNode);
}
}

29
tests/Example1.java Normal file
View File

@@ -0,0 +1,29 @@
public class Example1 {
public void test() {
int x = 5;
int y = x + 10;
int z = y * 2;
int result = z + x;
}
public void test1(int a) {
int x = 5;
int y = 0;
if (a > 10) {
y = x + a;
} else {
y = x - a;
}
int result = y * 2;
}
public void test2() {
int sum = 0;
int i = 0;
while (i < 10) {
sum = sum + i;
i = i + 1;
}
}
}