/*
* 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 extends SlicingAlgorithm> 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);
}
}
}
}