Skip to content
EKnife.java 8.06 KiB
Newer Older
Carlos Galindo's avatar
Carlos Galindo committed
/*
 * e-Knife, a program slicing tool for Erlang based on the EDG
 * Copyright (c) 2021. David Insa, 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 <https://www.gnu.org/licenses/>.
 */

Carlos Galindo's avatar
Carlos Galindo committed
package eknife;

import edg.DotFactory;
import edg.EDGFactory;
Carlos Galindo's avatar
Carlos Galindo committed
import edg.PdfFactory;
Carlos Galindo's avatar
Carlos Galindo committed
import edg.graph.EDG;
import edg.graph.LAST;
Carlos Galindo's avatar
Carlos Galindo committed
import edg.graph.Node;
Sergio Pérez's avatar
Sergio Pérez committed
import edg.slicing.*;
Carlos Galindo's avatar
Carlos Galindo committed

import java.io.File;
import java.lang.reflect.InvocationTargetException;
Carlos Galindo's avatar
Carlos Galindo committed
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
Carlos Galindo's avatar
Carlos Galindo committed

Carlos Galindo's avatar
Carlos Galindo committed
public class EKnife {
	public enum Language {Java, Erlang, Php}
Carlos Galindo's avatar
Carlos Galindo committed

Carlos Galindo's avatar
Carlos Galindo committed
	public static void main(String[] args) {
		if (args.length == 0) {
			EKnife.printHelp();
			return;
		}

Carlos Galindo's avatar
Carlos Galindo committed
		final Args arguments = EKnife.processArguments(args);

		if (!arguments.isValid()) {
			EKnife.printHelp();
			System.exit(3);
		}
		EKnife.run(arguments);
Carlos Galindo's avatar
Carlos Galindo committed
	}

Carlos Galindo's avatar
Carlos Galindo committed
	private static Args processArguments(String[] args) {
Carlos Galindo's avatar
Carlos Galindo committed
		Args kArgs = new Args();

Carlos Galindo's avatar
Carlos Galindo committed
		for (int argIndex = 0; argIndex < args.length; argIndex++) {
Carlos Galindo's avatar
Carlos Galindo committed
			final String arg = args[argIndex];

Carlos Galindo's avatar
Carlos Galindo committed
			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":
Carlos Galindo's avatar
Carlos Galindo committed
					if (argIndex == args.length - 1) {
						System.out.printf("Parameter %s requires an argument\n", arg);
						printHelp();
						System.exit(1);
					}
Carlos Galindo's avatar
Carlos Galindo committed
			}
Carlos Galindo's avatar
Carlos Galindo committed
			switch (arg) {
Carlos Galindo's avatar
Carlos Galindo committed
				case "--help":
					printHelp();
					System.exit(0);
Carlos Galindo's avatar
Carlos Galindo committed
				case "-i":
				case "--input":
Carlos Galindo's avatar
Carlos Galindo committed
					kArgs.inputPath = args[argIndex + 1];
					break;
Carlos Galindo's avatar
Carlos Galindo committed
				case "-o":
				case "--output":
Carlos Galindo's avatar
Carlos Galindo committed
					kArgs.outputFile = new File(args[argIndex + 1]);
					break;
Carlos Galindo's avatar
Carlos Galindo committed
				case "-f":
				case "--file":
Carlos Galindo's avatar
Carlos Galindo committed
					kArgs.file = args[argIndex + 1];
					break;
Carlos Galindo's avatar
Carlos Galindo committed
				case "-l":
				case "--line":
Carlos Galindo's avatar
Carlos Galindo committed
					kArgs.line = Integer.parseInt(args[argIndex + 1]);
					break;
Carlos Galindo's avatar
Carlos Galindo committed
				case "-v":
				case "--var":
Carlos Galindo's avatar
Carlos Galindo committed
					kArgs.name = args[argIndex + 1];
					break;
Carlos Galindo's avatar
Carlos Galindo committed
				case "-n":
				case "--occurrence":
Carlos Galindo's avatar
Carlos Galindo committed
					kArgs.occurrence = Integer.parseInt(args[argIndex + 1]);
					break;
Carlos Galindo's avatar
Carlos Galindo committed
				case "-G":
				case "--print-graph":
Carlos Galindo's avatar
Carlos Galindo committed
					kArgs.setGraphFile(new File(args[argIndex + 1]));
					break;
				case "--ignore-constraints":
					kArgs.constrainedAlgorithm = false;
					break;
				case "--intraprocedural":
					kArgs.intraproceduralAlgorithm = true;
				case "--tabular":
					kArgs.tabular = true;
Carlos Galindo's avatar
Carlos Galindo committed
			}
		}

		kArgs.completeData();
		return kArgs;
	}
Carlos Galindo's avatar
Carlos Galindo committed

	private static void printHelp() {
Carlos Galindo's avatar
Carlos Galindo committed
		String help = "";

		help += "Use the following options:\n";
		help += "  -i,--input  <file/folder>   The file/folder where the source code is\n";
		help += "  -o,--output <file/folder>   The file/folder where the slice will be stored\n";
		help += "  -f,--file   <file>          The file (relative to -i) where the slicing criterion is\n";
		help += "  -l,--line   <num>           The line of the slicing criterion\n";
		help += "  -v,--var    <name>          The name of the slicing criterion (must be a variable)\n";
		help += "  -n,--occurrence <num>       The occurrence of the slicing criterion in that line\n";
		help += "  -G,--print-graph <file.dot> Exports the graph as a dot file\n";
		help += "  -G,--print-graph <file.pdf> Exports the graph as a PDF file\n";
		help += "  --ignore-constraints        Generates constraints but ignores when slicing\n";
		help += "  --intraprocedural           Makes the slice intraprocedural, the algorithm will not traverse function bounds\n";
		help += "  --tabular                   Uses the tabular slicing algorithm, skipping summary generation.\n";
Carlos Galindo's avatar
Carlos Galindo committed
		help += "  --help                      Show this message.\n";

		System.out.print(help);
	}

Carlos Galindo's avatar
Carlos Galindo committed
	private static void run(Args a) {
		final LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true);
		final EDG edg = new EDGFactory(last, !a.intraproceduralAlgorithm).createEDG();
Carlos Galindo's avatar
Carlos Galindo committed
		final SlicingCriterion slicingCriterion = new SlicingCriterion(a.file, a.line, a.name, a.occurrence);
		final Node SC;
			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) {
Carlos Galindo's avatar
Carlos Galindo committed
			System.out.println("Error: the slicing criterion could not be found! " + slicingCriterion);
			System.exit(1);
Carlos Galindo's avatar
Carlos Galindo committed
			return;
Carlos Galindo's avatar
Carlos Galindo committed
		}
		final SlicingAlgorithm slicingAlgorithm = a.getAlgorithm(edg);
		final Set<Node> slice = slicingAlgorithm.slice(SC);
Carlos Galindo's avatar
Carlos Galindo committed
		if (slice.isEmpty()) {
			System.out.println("Warning: no files will be written, as the slice is empty.");
			System.exit(2);
		}
Carlos Galindo's avatar
Carlos Galindo committed
		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;
			}
		}
Carlos Galindo's avatar
Carlos Galindo committed
		CodeFactory.createCode(Language.Erlang, a.outputFile, edg, slice);
	}

	static class Args {
Carlos Galindo's avatar
Carlos Galindo committed
		enum GraphFormat { PDF, DOT }

Carlos Galindo's avatar
Carlos Galindo committed
		String inputPath;
		File outputFile;
		String file;
		int line;
		String name;
		int occurrence = 1;
Carlos Galindo's avatar
Carlos Galindo committed
		File graphFile;
		GraphFormat graphFormat;
		boolean intraproceduralAlgorithm = false;
		boolean constrainedAlgorithm = true;
		Class<? extends SlicingAlgorithm> algClass;
Carlos Galindo's avatar
Carlos Galindo committed

		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;
		}
Carlos Galindo's avatar
Carlos Galindo committed

		void completeData() {
			if (file == null && inputPath != null && new File(inputPath).isFile())
				file = new File(inputPath).getName();
			if (intraproceduralAlgorithm)
				algClass = constrainedAlgorithm ? OnePassConstrainedAlgorithm.class : AdaptedStandardAlgorithm.class;
			else algClass = constrainedAlgorithm ? ConstrainedAlgorithm.class : StandardAlgorithm.class;
			if (tabular)
				algClass = constrainedAlgorithm ? ConstrainedTabularAlgorithm.class : TabularAlgorithm.class;
Carlos Galindo's avatar
Carlos Galindo committed
		}

		boolean isValid() {
			List<String> 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();
Carlos Galindo's avatar
Carlos Galindo committed
		}

		SlicingAlgorithm getAlgorithm(EDG edg) {
			try {
				return algClass.getConstructor(EDG.class).newInstance(edg);
			} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
				throw new RuntimeException(e);
			}
		}
Carlos Galindo's avatar
Carlos Galindo committed
	}
}