package tfm; import com.github.javaparser.JavaParser; import com.github.javaparser.ast.Modifier; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.stmt.ThrowStmt; import com.github.javaparser.ast.stmt.TryStmt; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import tfm.exec.GraphLog; import tfm.exec.PDGLog; import tfm.graphs.CFG; import tfm.graphs.CFG.ACFG; import tfm.graphs.PDG; import tfm.graphs.PDG.APDG; import tfm.graphs.PDG.PPDG; import tfm.nodes.GraphNode; import tfm.slicing.GraphNodeCriterion; import tfm.slicing.Slice; import tfm.slicing.SlicingCriterion; import tfm.utils.Logger; import tfm.visitors.cfg.CFGBuilder; import tfm.visitors.pdg.ControlDependencyBuilder; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; public class PDGTests { static { JavaParser.getStaticConfiguration().setAttributeComments(false); } private boolean error = false; @ParameterizedTest(name = "[{index}] {0} ({1})") @MethodSource("tfm.FileFinder#findAllMethodDeclarations") public void ppdgTest(File file, String methodName, MethodDeclaration root) throws IOException { runPdg(file, methodName, root, PDGLog.PPDG); } @ParameterizedTest(name = "[{index}] {0} ({1})") @MethodSource("tfm.FileFinder#findAllMethodDeclarations") public void apdgTest(File file, String methodName, MethodDeclaration root) throws IOException { runPdg(file, methodName, root, PDGLog.APDG); } @ParameterizedTest(name = "[{index}] {0} ({1})") @MethodSource("tfm.FileFinder#findAllMethodDeclarations") public void pdgTest(File file, String methodName, MethodDeclaration root) throws IOException { runPdg(file, methodName, root, PDGLog.PDG); } private void runPdg(File file, String methodName, Node root, int type) throws IOException { GraphLog graphLog = new PDGLog(type); graphLog.visit(root); graphLog.log(); try { graphLog.generateImages(file.getPath() + "-" + methodName); } catch (Exception e) { System.err.println("Could not generate PNG"); System.err.println(e.getMessage()); } } @ParameterizedTest(name = "[{index}] {0} ({1})") @MethodSource("tfm.FileFinder#findAllMethodDeclarations") public void pdgCompare(File file, String methodName, MethodDeclaration root) throws IOException { ControlDependencyBuilder ctrlDepBuilder; if (containsUnsupportedStatements(root)) { System.err.println("Contains unsupported instructions"); } // Create PDG CFG cfg = new CFG(); root.accept(new CFGBuilder(cfg), null); PDG pdg = new PDG(cfg); ctrlDepBuilder = new ControlDependencyBuilder(pdg, cfg); ctrlDepBuilder.analyze(); // Create APDG ACFG acfg = new ACFG(); root.accept(new CFGBuilder(acfg), null); APDG apdg = new APDG(acfg); ctrlDepBuilder = new ControlDependencyBuilder(apdg, acfg); ctrlDepBuilder.analyze(); // Create PPDG PPDG ppdg = new PPDG(acfg); ctrlDepBuilder = new ControlDependencyBuilder(ppdg, acfg); ctrlDepBuilder.analyze(); // Print graphs (commented to decrease the test's time) String filePathNoExt = file.getPath().substring(0, file.getPath().lastIndexOf('.')); // String name = filePathNoExt + "/" + methodName; // new PDGLog(pdg).generateImages(name); // new PDGLog(apdg).generateImages(name); // new PDGLog(ppdg).generateImages(name); // Compare List slicedMethods = compareGraphs(pdg, apdg, ppdg); // Write sliced methods to a java file. ClassOrInterfaceDeclaration clazz = new ClassOrInterfaceDeclaration(); slicedMethods.forEach(clazz::addMember); clazz.setName(methodName); clazz.setModifier(Modifier.Keyword.PUBLIC, true); try (PrintWriter pw = new PrintWriter(new File("./out/" + filePathNoExt + "/" + methodName + ".java"))) { pw.println(clazz); } catch (Exception e) { Logger.log("Error! Could not write classes to file"); } assert !error; } public static boolean containsUnsupportedStatements(Node node) { return node.findFirst(TryStmt.class).isPresent() || node.findFirst(ThrowStmt.class).isPresent(); } /** Slices both graphs on every possible node and compares the result */ public List compareGraphs(PDG... pdgs) { List slicedMethods = new LinkedList<>(); assert pdgs.length > 0; for (GraphNode node : pdgs[0].getNodes().stream() .sorted(Comparator.comparingInt(GraphNode::getId)) .collect(Collectors.toList())) { // Skip start of graph if (node.getAstNode() instanceof MethodDeclaration) continue; // Perform slices SlicingCriterion sc = new GraphNodeCriterion(node, "x"); Slice[] slices = Arrays.stream(pdgs).map(p -> p.slice(sc)).toArray(Slice[]::new); // Compare slices boolean ok = true; Slice referenceSlice = slices[0]; for (Slice slice : slices) { ok = referenceSlice.equals(slice); error |= !ok; if (!ok) break; } // Display slice Logger.log("Slicing on " + node.getId()); if (!ok) Logger.log("FAILED!"); printSlices(pdgs[0], slices); // Save slices as MethodDeclaration int i = 0; for (Slice s : slices) { i++; try { MethodDeclaration m = ((MethodDeclaration) s.getAst()); m.setName(m.getName() + "_slice" + node.getId() + "_pdg" + i); slicedMethods.add(m); } catch (RuntimeException e) { Logger.log("Error: " + e.getMessage()); } } } return slicedMethods; } public final void printSlices(PDG pdg, Slice... slices) { pdg.getNodes().stream() .sorted(Comparator.comparingInt(GraphNode::getId)) .forEach(n -> Logger.format("%3d: %s %s", n.getId(), Arrays.stream(slices) .map(s -> s.contains(n) ? "x" : " ") .reduce((a, b) -> a + " " + b).orElse("--error--"), n.getData())); } }