diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..67e8aa5 Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index 9ab9d6b..396c4aa 100644 --- a/README.md +++ b/README.md @@ -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 +``` diff --git a/build.sh b/build.sh deleted file mode 100755 index 6a2db76..0000000 --- a/build.sh +++ /dev/null @@ -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 "" diff --git a/lib/.DS_Store b/lib/.DS_Store new file mode 100644 index 0000000..9b5c16a Binary files /dev/null and b/lib/.DS_Store differ diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..bb290c5 --- /dev/null +++ b/run.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ "$#" -lt 1 ]; then + echo "Usage: ./run.sh [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 "$@" diff --git a/src/.DS_Store b/src/.DS_Store index a1d0d38..418f72f 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/CFGBuilder.java b/src/CFGBuilder.java new file mode 100644 index 0000000..92edbef --- /dev/null +++ b/src/CFGBuilder.java @@ -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 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 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 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 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 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 getAllLineNumbers() { + Set lines = new TreeSet<>(); + for (Integer line : nodeToLine.values()) { + if (line > 0) lines.add(line); + } + return new ArrayList<>(lines); + } + + public List findNodesAtLine(ControlFlowGraph cfg, int lineNumber) { + List 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> 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> entry : lineToNodes.entrySet()) { + System.out.println("Line " + entry.getKey() + ":"); + for (String label : entry.getValue()) { + System.out.println(" " + label); + } + } + System.out.println(); + } +} diff --git a/src/PDGTool.java b/src/PDGTool.java new file mode 100644 index 0000000..97a3552 --- /dev/null +++ b/src/PDGTool.java @@ -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 [line-number] + */ +public class PDGTool { + + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("Usage: java PDGTool [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 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 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 allImpacted = new HashSet<>(); + for (Node node : nodesAtLine) { + Set impacted = pdg.computeForwardSlice(node.label()); + allImpacted.addAll(impacted); + } + + // Convert to line numbers + Set 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"); + } + } +} diff --git a/src/Test.java b/src/Test.java deleted file mode 100644 index 8bdf63f..0000000 --- a/src/Test.java +++ /dev/null @@ -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 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 ==="); - } -} diff --git a/src/org/.DS_Store b/src/org/.DS_Store index da72000..4207eaa 100644 Binary files a/src/org/.DS_Store and b/src/org/.DS_Store differ diff --git a/src/org/lsmr/.DS_Store b/src/org/lsmr/.DS_Store index b298281..a2f9343 100644 Binary files a/src/org/lsmr/.DS_Store and b/src/org/lsmr/.DS_Store differ diff --git a/src/org/lsmr/cfg/.DS_Store b/src/org/lsmr/cfg/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/src/org/lsmr/cfg/.DS_Store differ diff --git a/src/pdg/PDG.java b/src/pdg/PDG.java index f5a1e42..465c734 100644 --- a/src/pdg/PDG.java +++ b/src/pdg/PDG.java @@ -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 pdg; - private Map cfgToPdgMap; - // Analysis results - private Map> postDominators; - private Map> controlDependence; - private Map> dataDependence; + private ControlFlowGraph cfg; + private Map> controlDeps; + private Map> 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 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 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 newPostDom = new HashSet<>(); - newPostDom.add(node); // Node post-dominates itself - - Set 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 postDoms = postDominators.get(node); - return postDoms != null && postDoms.contains(dominator); - } - - private void computeDataDependence() { - Map> defs = new HashMap<>(); - Map> uses = new HashMap<>(); + // 1. Find definitions and uses for each node + Map> defs = new HashMap<>(); + Map> 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> reachingDefs = computeReachingDefinitions(defs); + + // 2. Compute reaching definitions + Map>> reaching = computeReachingDefinitions(defs); + + // 3. Build data dependencies for (Node node : cfg.nodes()) { - dataDependence.put(node, new HashSet<>()); - } - for (Node use : cfg.nodes()) { - Set usedVars = uses.get(use); - Set reaching = reachingDefs.get(use); + String label = node.label(); + Set 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 extractDefs(Node node) { - Set 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 extractUses(Node node) { - Set 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 extractVariablesFromExpression(String expr) { + /** + * Extract variable definitions from a statement simple pattern matching for Java 1.4 + */ + private Set extractDefs(String statement) { Set 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 extractUses(String statement) { + Set 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 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> computeReachingDefinitions(Map> defs) { - Map> reachingDefs = new HashMap<>(); + /** + * Compute reaching definitions using iterative data flow analysis + */ + private Map>> computeReachingDefinitions( + Map> defs) { - // Initialize + Map>> 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 newReaching = new HashSet<>(); - - // Union of reaching definitions from all predecessors - Set inEdges = node.inEdges(); - for (Edge edge : inEdges) { - Node pred = edge.source(); - Set predReaching = new HashSet<>(reachingDefs.get(pred)); - - // Kill definitions of variables defined in pred - Set 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> oldReaching = new HashMap<>(reaching.get(label)); + Map> newReaching = new HashMap<>(); + for (Edge inEdge : node.inEdges()) { + Node pred = inEdge.source(); + String predLabel = pred.label(); + if (reaching.containsKey(predLabel)) { + for (Map.Entry> 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> m1, Map> 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> deps) { + int count = 0; + for (Set set : deps.values()) { + count += set.size(); + } + return count; + } + + + public Set getDependencies(String nodeLabel) { + Set 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 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 slice = new HashSet<>(); + Queue worklist = new LinkedList<>(); - // Compute forward slice from a given node. - private Set computeForwardSliceFromNode(Node startNode) { - PDGNode startPDGNode = cfgToPdgMap.get(startNode); - Set impactedLabels = new HashSet<>(); - Set visited = new HashSet<>(); - Queue 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 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 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 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 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 getAllNodes() { + Set 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 getGraph() { - return pdg; + public void printPDG() { + System.out.println("CONTROL DEPENDENCIES:"); + for (Map.Entry> 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> entry : dataDeps.entrySet()) { + if (!entry.getValue().isEmpty()) { + System.out.println(" " + entry.getKey()); + for (String dep : entry.getValue()) { + System.out.println(" <- " + dep); + } + } + } + } + + public Map> 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> getDataDependencies() { + return new HashMap<>(dataDeps); } } diff --git a/src/pdg/PDGEdge.java b/src/pdg/PDGEdge.java deleted file mode 100644 index bcc6ca1..0000000 --- a/src/pdg/PDGEdge.java +++ /dev/null @@ -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(); - } -} diff --git a/src/pdg/PDGNode.java b/src/pdg/PDGNode.java deleted file mode 100644 index 3fb672a..0000000 --- a/src/pdg/PDGNode.java +++ /dev/null @@ -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 defs; - private Set uses; - - public PDGNode(Node cfgNode) { - this.cfgNode = cfgNode; - this.defs = new HashSet<>(); - this.uses = new HashSet<>(); - } - - public Node getCfgNode() { - return cfgNode; - } - - public Set getDefs() { - return defs; - } - - public void setDefs(Set defs) { - this.defs = defs; - } - - public Set getUses() { - return uses; - } - - public void setUses(Set 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); - } -} diff --git a/tests/Example1.java b/tests/Example1.java new file mode 100644 index 0000000..340126d --- /dev/null +++ b/tests/Example1.java @@ -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; + } + } +}