/* * EDG, a library to generate and slice Expression Dependence Graphs. * Copyright (c) 2021-2023. David Insa, Carlos Galindo, Sergio PĂ©rez, Josep Silva, Salvador Tamarit. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package eknife; import edg.DotFactory; import edg.EDGFactory; import edg.PdfFactory; import edg.graph.EDG; import edg.graph.LAST; import edg.graph.Node; import edg.slicing.*; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.util.LinkedList; import java.util.List; import java.util.Set; public class EKnife { public enum Language {Java, Erlang, Php} public static void main(String[] args) { if (args.length == 0) { EKnife.printHelp(); return; } final Args arguments = EKnife.processArguments(args); if (!arguments.isValid()) { EKnife.printHelp(); System.exit(3); } EKnife.run(arguments); } private static Args processArguments(String[] args) { Args kArgs = new Args(); for (int argIndex = 0; argIndex < args.length; argIndex++) { final String arg = args[argIndex]; boolean shift = false; switch (arg) { case "-i": case "--input": case "-o": case "--output": case "-f": case "--file": case "-l": case "--line": case "-v": case "--var": case "-n": case "--occurrence": case "-G": case "--print-graph": if (argIndex == args.length - 1) { System.out.printf("Parameter %s requires an argument\n", arg); printHelp(); System.exit(1); } shift = true; } switch (arg) { case "--help": printHelp(); System.exit(0); case "-i": case "--input": kArgs.inputPath = args[argIndex + 1]; break; case "-o": case "--output": kArgs.outputFile = new File(args[argIndex + 1]); break; case "-f": case "--file": kArgs.file = args[argIndex + 1]; break; case "-l": case "--line": kArgs.line = Integer.parseInt(args[argIndex + 1]); break; case "-v": case "--var": kArgs.name = args[argIndex + 1]; break; case "-n": case "--occurrence": kArgs.occurrence = Integer.parseInt(args[argIndex + 1]); break; case "-G": case "--print-graph": kArgs.setGraphFile(new File(args[argIndex + 1])); break; case "--ignore-constraints": kArgs.constrainedAlgorithm = false; break; case "--single-function": kArgs.singleFunction = true; break; case "--ignore-calling-context": kArgs.pdgAlgorithm = true; break; default: System.out.println("Unknown parameter " + arg); printHelp(); System.exit(1); } if (shift) argIndex++; } kArgs.completeData(); return kArgs; } private static void printHelp() { String help = ""; help += "Use the following options:\n"; help += " -i,--input The file/folder where the source code is\n"; help += " -o,--output The file/folder where the slice will be stored\n"; help += " -f,--file The file (relative to -i) where the slicing criterion is\n"; help += " -l,--line The line of the slicing criterion\n"; help += " -v,--var The name of the slicing criterion (must be a variable)\n"; help += " -n,--occurrence The occurrence of the slicing criterion in that line\n"; help += " -G,--print-graph Exports the graph as a dot file\n"; help += " -G,--print-graph Exports the graph as a PDF file\n"; help += " --ignore-constraints Generates constraints but ignores when slicing\n"; help += " --ignore-calling-context Use a context-insensitive algorithm (will cross through functions).\n"; help += " --single-function The graph and the algorithm will not cross function bounds\n"; help += " --help Show this message.\n"; System.out.print(help); } private static void run(Args a) { final LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true); final EDG edg = new EDGFactory(last, !a.singleFunction).createEDG(); final SlicingCriterion slicingCriterion = new SlicingCriterion(a.file, a.line, a.name, a.occurrence); final Node SC; try { SC = edg.getNode(slicingCriterion); if (SC == null) { System.out.println("Error: the slicing criterion could not be found! " + slicingCriterion); System.exit(1); } } catch (IllegalArgumentException e) { System.out.println("Error: the slicing criterion could not be found! " + slicingCriterion); System.exit(1); return; } final SlicingAlgorithm slicingAlgorithm = a.getAlgorithm(edg); final Set slice = slicingAlgorithm.slice(SC); if (slice.isEmpty()) { System.out.println("Warning: no files will be written, as the slice is empty."); System.exit(2); } if (a.graphFile != null) { switch (a.graphFormat) { case DOT: DotFactory.createDot(a.graphFile, edg, SC, slice); break; case PDF: PdfFactory.createPdf(a.graphFile, edg, SC, slice); break; } } CodeFactory.createCode(Language.Erlang, a.outputFile, edg, slice); } static class Args { enum GraphFormat { PDF, DOT } String inputPath; File outputFile; String file; int line; String name; int occurrence = 1; File graphFile; GraphFormat graphFormat; boolean singleFunction = false; boolean pdgAlgorithm = false; boolean constrainedAlgorithm = true; Class algClass; void setGraphFile(File graphFile) { if (graphFile.getName().endsWith(".dot")) graphFormat = GraphFormat.DOT; else if (graphFile.getName().endsWith(".pdf")) graphFormat = GraphFormat.PDF; else { System.out.println("The graph file must end in .dot or .pdf, you set it to " + graphFile); System.exit(1); } this.graphFile = graphFile; } void completeData() { if (file == null && inputPath != null && new File(inputPath).isFile()) file = new File(inputPath).getName(); if (pdgAlgorithm || singleFunction) algClass = constrainedAlgorithm ? OnePassConstrainedAlgorithm.class : OnePassStandardAlgorithm.class; else algClass = constrainedAlgorithm ? ConstrainedTabularAlgorithm.class : TabularAlgorithm.class; } boolean isValid() { List errors = new LinkedList<>(); if (inputPath == null) errors.add("You must specify a file to analyze with '-i'."); else if (!new File(inputPath).exists()) errors.add("The input file you've specified does not exist or isn't readable(" + inputPath + ")."); else if (file == null) errors.add("The input file is a folder, so you must specify a the file that contains the slicing criterion with '-f'."); if (outputFile == null) errors.add("You must specify a location for the output with '-o'."); else if (!outputFile.exists() && !outputFile.getAbsoluteFile().getParentFile().isDirectory()) errors.add("The output file's parent folder does not exist, or the output folder does not exist."); if (line <= 0) errors.add("You must specify a line number greater than 0 with '-l'."); if (name == null || name.isEmpty()) errors.add("You must specify a valid variable name with '-v'."); if (occurrence <= 0) errors.add("You must specify an occurrence greater than 0 with '-n'."); for (String error : errors) System.out.println(error); return errors.isEmpty(); } SlicingAlgorithm getAlgorithm(EDG edg) { try { return algClass.getConstructor(EDG.class).newInstance(edg); } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } } }