ABCExecutor.java

package edu.udel.cis.vsl.abc.main;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import org.antlr.runtime.CommonToken;

import edu.udel.cis.vsl.abc.analysis.IF.Analyzer;
import edu.udel.cis.vsl.abc.ast.IF.AST;
import edu.udel.cis.vsl.abc.ast.IF.DifferenceObject;
import edu.udel.cis.vsl.abc.ast.node.IF.SequenceNode;
import edu.udel.cis.vsl.abc.ast.node.IF.declaration.FunctionDeclarationNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.BlockItemNode;
import edu.udel.cis.vsl.abc.config.IF.Configuration;
import edu.udel.cis.vsl.abc.config.IF.Configurations;
import edu.udel.cis.vsl.abc.config.IF.Configurations.Language;
import edu.udel.cis.vsl.abc.err.IF.ABCException;
import edu.udel.cis.vsl.abc.front.IF.ASTBuilder;
import edu.udel.cis.vsl.abc.front.IF.ParseTree;
import edu.udel.cis.vsl.abc.front.IF.Parser;
import edu.udel.cis.vsl.abc.front.IF.Preprocessor;
import edu.udel.cis.vsl.abc.front.c.preproc.PreprocessorParser;
import edu.udel.cis.vsl.abc.main.TranslationTask.TranslationStage;
import edu.udel.cis.vsl.abc.program.IF.Program;
import edu.udel.cis.vsl.abc.token.IF.CivlcTokenSource;
import edu.udel.cis.vsl.abc.token.IF.FileIndexer;
import edu.udel.cis.vsl.abc.transform.IF.TransformRecord;
import edu.udel.cis.vsl.abc.transform.IF.Transformer;
import edu.udel.cis.vsl.abc.util.IF.ANTLRUtils;
import edu.udel.cis.vsl.abc.util.IF.Timer;

/**
 * <p>
 * An executor executes a {@link TranslationTask}. To use an executor, first
 * construct a {@link TranslationTask}. You can then create an executor for
 * executing that task using the constructor
 * {@link #ABCExecutor(TranslationTask)}. Then to actually execute that task,
 * invoke the executor's {@link #execute()} method.
 * </p>
 * 
 * <p>
 * Alternatively, the construction of the {@link ABCExecutor} and the execution
 * of the task can be accomplished in a single step using static method
 * {@link #execute()}, which also returns the new executor that was used to
 * execute the task.
 * </p>
 * 
 * <p>
 * The methods above all create a new {@link FrontEnd} instance to execute the
 * task. This can be expensive. If multiple tasks are to be executed, one can
 * re-use the same {@link FrontEnd} by using method {@link #getFrontEnd()} to
 * get the front end from the first executor and using static method
 * {@link #newExecutor(FrontEnd, TranslationTask)} to create a new executor that
 * re-uses that front end. Note however, that you can only re-use a front end
 * for a compatible task, that is, one that shares the same configuration
 * parameters on SV-COMP, GNUC, and Architecture.
 * </p>
 * 
 * @author siegel
 */
public class ABCExecutor {

	// Static members...

	/**
	 * Creates an ABCExecutor to execute a translation task, executes it, and
	 * then returns that executor.
	 * 
	 * @param task
	 *            the translation task
	 * @return the {@link ABCExecutor} used to execute that task
	 * @throws ABCException
	 *             if any preprocessing, parsing, syntax or semantic errors are
	 *             found in the process of carrying out the translation task
	 */
	public final static ABCExecutor execute(TranslationTask task)
			throws ABCException {
		ABCExecutor executor = new ABCExecutor(task);

		executor.execute();
		return executor;
	}

	/**
	 * Creates an ABCExecutor to execute a translation task using the given
	 * front end; executes the task; and returns that executor
	 * 
	 * @param frontEnd
	 *            the front end that will be used to carry out the translation
	 *            task steps
	 * @param task
	 *            the translation task
	 * @return the {@link ABCExecutor} created and used to execute the task
	 * @throws ABCException
	 *             if any preprocessing, parsing, syntax or semantic errors are
	 *             found in the process of carrying out the translation task, or
	 *             if <code>frontEnd</code> is incompatible with
	 *             <code>task</code>
	 */
	public final static ABCExecutor execute(FrontEnd frontEnd,
			TranslationTask task) throws ABCException {
		ABCExecutor executor = newExecutor(frontEnd, task);

		executor.execute();
		return executor;
	}

	/**
	 * Creates a new {@link ABCExecutor} for performing <code>task</code> and
	 * that uses the given <code>frontEnd</code>.
	 * 
	 * @param frontEnd
	 *            an existing front end that will be (re-)used by the new
	 *            executor to perform all translation tasks
	 * @param task
	 *            the translation task to be executed
	 * @return the new executor
	 * @throws ABCException
	 *             if <code>task</code> is incompatible with
	 *             <code>frontEnd</code> due to differing values on SVCOMP or
	 *             Architecture fields.
	 */
	public final static ABCExecutor newExecutor(FrontEnd frontEnd,
			TranslationTask task) throws ABCException {
		Configuration config = frontEnd.getConfiguration();

		if (config.getSVCOMP() != task.getSVCOMP())
			throw new ABCException(
					"Front end cannot be used to perform task due "
							+ "to incompatible SVCOMP values");
		if (config.getArchitecture() != task.getArchitecture())
			throw new ABCException(
					"Front end cannot be used to perform task due "
							+ "to incompatible Architecture values");
		if (config.getGNUC() != task.getGNUC())
			throw new ABCException(
					"Front end cannot be used to perform task due "
							+ "to incompatible GNUC values");
		return new ABCExecutor(frontEnd, task);
	}

	/**
	 * A bar used in printing output.
	 */
	private final static String bar = "===================";

	/**
	 * Computes a name for a source unit. The name is the concatenation of the
	 * names of the files comprising the unit, separated with "+". A "source
	 * unit" is a sequence of files which will be preprocessed as a single
	 * translation unit.
	 * 
	 * @param sourceUnit
	 *            sequence of files which will be preprocessed to create a
	 *            single translation unit
	 * @return a name for the sequence of files derived from their file names
	 */
	private final static String getName(File[] sourceUnit) {
		int numFiles = sourceUnit.length;
		String result = "";

		for (int i = 0; i < numFiles; i++) {
			if (i > 0)
				result += "+";
			result += sourceUnit[i].getName();
		}
		return result;
	}

	/**
	 * Prints file scope functions that are used but not defined.
	 * 
	 * @param program
	 *            a non-<code>null</code> {@link Program}
	 */
	private final static void printUnknownFunctions(PrintStream out,
			Program program) {
		SequenceNode<BlockItemNode> root = program.getAST().getRootNode();
		int i = 0;
		Set<String> functionNames = new HashSet<>();

		for (BlockItemNode item : root) {
			if (item instanceof FunctionDeclarationNode) {
				FunctionDeclarationNode function = (FunctionDeclarationNode) item;

				if (function.getEntity().getDefinition() == null) {
					String functionName = function.getName();

					if (!functionNames.contains(functionName)) {
						if (i == 0)
							out.println(
									"==== functions without definition ====");
						else
							out.print(",");
						out.print(functionName);
						functionNames.add(functionName);
						i++;
					}
				}
			}
		}
		if (i > 0)
			out.println();
		out.flush();
	}

	// Instance fields...

	/**
	 * The task to be executed. Set at construction.
	 */
	private TranslationTask task;

	/**
	 * The configuration. Typically determined from the task at construction, or
	 * from the {@link FrontEnd} provided at construction.
	 */
	private Configuration configuration;

	/**
	 * The {@link FrontEnd} that will be used to actually carry out the tasks
	 * specified by {@link #task}. This is either provided to a constructor, or
	 * it is created by the constructor.
	 */
	private FrontEnd frontEnd;

	/**
	 * Where to send output; copy of what's in {@link #task} for convenience.
	 */
	private PrintStream out;

	/**
	 * Print a lot of information? Copy of what's in {@link #task} for
	 * convenience.
	 */
	private boolean verbose;

	/**
	 * Report timing information? Copy of what's in {@link #task} for
	 * convenience.
	 */
	private boolean showTime;

	/**
	 * The {@link Timer} that will be used to take timings. If {@link #showTime}
	 * is <code>false</code>, this will be a (non-<code>null</code>) trivial
	 * {@link Timer} that does nothing.
	 */
	private Timer timer;

	/**
	 * The total number of known unit tasks, including those that have not yet
	 * been executed. Initially, this is the number of unit tasks specified at
	 * construction, but this number can grow as new unit tasks are created
	 * through executing unit tasks.
	 */
	private int numUnits;

	/**
	 * The total number of unit tasks that have been completed (executed). This
	 * number is necessarily less that or equal to {@link #numUnits}.
	 */
	private int numUnitTasksDone = 0;

	/**
	 * The unit tasks. These are the unit tasks that are executed. Each unit
	 * tasks corresponds to the processing of a single translation unit.
	 * Initially, these tasks are specified at construction, but this list can
	 * grow as new unit tasks are created during execution.
	 */
	private ArrayList<UnitTask> unitTasks;

	/**
	 * The results of preprocessing the input source units specified in the
	 * {@link #task}. The length of this array is the number of {@link UnitTask}
	 * s specified in the {@link #task}. Initially every entry is
	 * <code>null</code>; they are filled in as the unit tasks are executed
	 * through the preprocessing stage. Note that these sources have state: once
	 * they have been consumed their next token methods will just return EOF
	 * forever.
	 */
	private ArrayList<CivlcTokenSource> tokenSources = null;

	/**
	 * The results of parsing the preprocessor output for each source unit. The
	 * length of this array is the number of {@link UnitTask}s specified in the
	 * {@link #task}. Initially every entry is <code>null</code>; they are
	 * filled in as the unit tasks are executed through the parsing stage.
	 */
	private ArrayList<ParseTree> parseTrees = null;

	/**
	 * The ASTs for the translation units. The length of this array is the
	 * number of {@link UnitTask}s specified in the {@link #task}. Initially
	 * every entry is <code>null</code>; they are filled in as the unit tasks
	 * are executed through the AST-building stage.
	 */
	private ArrayList<AST> asts = null;

	/**
	 * The complete program. Initially null, this is filled in after linking and
	 * further modified after executing transformations.
	 */
	private Program program = null;

	// Constructors...

	/**
	 * <p>
	 * Constructs new executor for executing specified task, using given front
	 * end. The front end and the task must be consistent: the values returned
	 * by methods {@link TranslationTask#getSVCOMP()} and
	 * {@link TranslationTask#getArchitecture()} on <code>task</code> must be
	 * the same as the corresponding methods in the {@link Configuration} of
	 * <code>frontEnd</code>.
	 * </p>
	 * 
	 * <p>
	 * This constructor is for internal use only. Clients who want to re-use an
	 * existing front end should use method
	 * {@link ABCExecutor#newExecutor(FrontEnd, TranslationTask)}.
	 * </p>
	 * 
	 * @param frontEnd
	 *            the front end that will be used by the new executor
	 * @param task
	 *            the task that the new executor will be asked to perform
	 */
	private ABCExecutor(FrontEnd frontEnd, TranslationTask task) {
		this.frontEnd = frontEnd;
		this.configuration = frontEnd.getConfiguration();
		initialize(task);
		this.configuration.setLanguage(task.getLinkLanguage());
	}

	/**
	 * Constructs new executor for performing the specified translation task.
	 * The constructor does not perform the tasks, but it creates a new
	 * {@link FrontEnd} and initializes data structures. The task itself will be
	 * executed by invoking method {@link #execute()}. A new empty
	 * {@link FileIndexer} is created.
	 * 
	 * @param task
	 *            a translation task to execute
	 */
	public ABCExecutor(TranslationTask task) {
		this.configuration = Configurations.newMinimalConfiguration();
		this.configuration.setSVCOMP(task.getSVCOMP());
		this.configuration.setArchitecture(task.getArchitecture());
		this.configuration.setGNUC(task.getGNUC());
		this.frontEnd = new FrontEnd(configuration);
		initialize(task);
		this.configuration.setLanguage(task.getLinkLanguage());
	}

	/**
	 * Constructs new executor for performing the specified translation task and
	 * using the given {@link FileIndexer}. The constructor does not perform the
	 * tasks, but it creates a new {@link FrontEnd} and initializes data
	 * structures. The task itself will be executed by invoking method
	 * {@link #execute()}.
	 * 
	 * @param task
	 *            a translation task to execute
	 * @param fileIndexer
	 *            an existing non-{@code null} {@link FileIndexer} to use for
	 *            keeping track of all openened files
	 */
	public ABCExecutor(TranslationTask task, FileIndexer fileIndexer) {
		this.configuration = Configurations.newMinimalConfiguration();
		this.configuration.setSVCOMP(task.getSVCOMP());
		this.configuration.setArchitecture(task.getArchitecture());
		this.configuration.setGNUC(task.getGNUC());
		this.frontEnd = new FrontEnd(configuration, fileIndexer);
		initialize(task);
		this.configuration.setLanguage(task.getLinkLanguage());
	}

	// Helpers...

	/**
	 * Adds <code>n</code> <code>null</code> values to <code>vec</code>.
	 * 
	 * @param n
	 *            nonnegative integer
	 * @param vec
	 *            any array list
	 */
	private static <T> void addNulls(int n, ArrayList<T> vec) {
		for (int i = 0; i < n; i++)
			vec.add(null);
	}

	/**
	 * Adds <code>n</code> <code>null</code> values to each of the lists
	 * {@link #tokenSources}, {@link #parseTrees}, and {@link #asts}.
	 * 
	 * @param n
	 *            a nonnegative integer
	 */
	private void addNulls(int n) {
		addNulls(n, tokenSources);
		addNulls(n, parseTrees);
		addNulls(n, asts);
	}

	/**
	 * Initializes internal data structures. To be used by constructors.
	 * 
	 * @param task
	 *            the translation task that was used as the argument to one of
	 *            the constructors
	 */
	private void initialize(TranslationTask task) {
		this.task = task;
		this.timer = task.getShowTables()
				? new Timer(task.getOut())
				: new Timer();
		this.out = task.getOut();
		this.verbose = task.getVerbose();
		this.showTime = task.getShowTime();
		this.numUnits = task.getUnitTasks().length;
		this.unitTasks = new ArrayList<UnitTask>();
		for (UnitTask t : task.getUnitTasks())
			this.unitTasks.add(t);
		this.tokenSources = new ArrayList<CivlcTokenSource>();
		this.parseTrees = new ArrayList<ParseTree>();
		this.asts = new ArrayList<AST>();
		addNulls(numUnits);
	}

	/**
	 * Prints the program, symbol table, and type information to the given
	 * output stream in a plain-text, human-readable format.
	 */
	private void printProgram() {
		if (task.getPrettyPrint())
			program.prettyPrint(out);
		else
			program.print(out);
		if (task.getShowTables()) {
			out.println("\n\nSymbol Table:\n");
			program.printSymbolTable(out);
			out.println("\n\nTypes:\n");
			frontEnd.getTypeFactory().printTypes(out);
		}
		out.println();
		out.flush();
	}

	/**
	 * Executes a comparison. This is a very special kind of task that involves
	 * exactly two translation units, which are processed and compared
	 * syntactically. This method will print a human readable summary declaring
	 * either that the ASTs are identical or describing some difference between
	 * them.
	 * 
	 * This method should be invoked after the two unit tasks have been executed
	 * and the two ASTs are available.
	 * 
	 * @throws ABCException
	 *             if {@link #numUnits} is not exactly 2
	 */
	private void executeComparison() throws ABCException {
		assert task.getShowDiff();

		UnitTask[] unitTasks = task.getUnitTasks();
		int numUnits = unitTasks.length;

		if (numUnits != 2)
			throw new ABCException(
					"-showDiff requires exactly two source units.");

		DifferenceObject diffObj = asts.get(0).diff(asts.get(1));

		if (diffObj == null && !task.isSilent())
			out.println("The AST of " + getName(unitTasks[0].getSourceFiles())
					+ " is equivalent to that of "
					+ getName(unitTasks[1].getSourceFiles()) + ".");
		else
			diffObj.print(out);
		out.flush();
	}

	/**
	 * Executes a single unit task.
	 * 
	 * @param index
	 *            the index of the unit task in the array of unit tasks
	 *            associated to this task
	 * @throws ABCException
	 *             if any I/O, syntax, or semantic problem arises in processing
	 *             the translation unit as specified in the unit task
	 */
	private void executeUnit(int index) throws ABCException {
		TranslationStage stage = task.getStage();
		UnitTask unitTask = unitTasks.get(index);
		File[] sourceFiles = unitTask.getSourceFiles();
		String name = getName(sourceFiles);
		Language language = unitTask.getLanguage();
		Preprocessor preprocessor = frontEnd.getPreprocessor(language);
		int numFiles = sourceFiles.length;

		for (int j = 0; j < numFiles; j++) {
			File file = sourceFiles[j];
			String filename = file.getName();

			if (verbose) {
				out.println(bar + " File " + filename + " " + bar);
				try {
					ANTLRUtils.source(out, file);
				} catch (IOException e) {
					throw new ABCException("Could not open file: " + file);
				}
				out.println();
				out.flush();
			}
		}
		timer.markTime("print source for " + name);

		CivlcTokenSource tokens = preprocessor.preprocess(
				unitTask.getSystemIncludes(), unitTask.getUserIncludes(),
				unitTask.getMacros(), sourceFiles);

		tokenSources.set(index, tokens);
		timer.markTime("construct preprocess tree");
		if (stage == TranslationStage.PREPROCESS)
			return;
		if (stage == TranslationStage.PREPROCESS_CONSUME) {
			CommonToken token;
			int type;

			if (verbose)
				out.println(
						bar + " Preprocessor output for " + name + " " + bar);
			if (showTime) {
				do {
					token = (CommonToken) tokens.nextToken();
					type = token.getType();
				} while (type != PreprocessorParser.EOF);
				timer.markTime("preprocess " + name);
			} else {
				while (true) {
					token = (CommonToken) tokens.nextToken();
					type = token.getType();
					if (type == PreprocessorParser.EOF)
						break;
					if (type == PreprocessorParser.COMMENT)
						out.print(" ");
					else {
						if (task.getPreprocTokens()) {
							out.println(token);
						} else {
							out.print(token.getText());
						}
					}
				}
				out.println();
				out.flush();
				timer.markTime("preprocess and write " + name);
			}
			return;
		}

		// go beyond preprocessing...
		Parser parser = frontEnd.getParser(language);
		ParseTree parseTree = parser.parse(tokens);

		parseTrees.set(index, parseTree);
		timer.markTime("preprocess, parse, and build ANTLR tree");
		if (verbose) {
			out.println(bar + " ANTLR Tree for " + name + " " + bar);
			ANTLRUtils.printTree(out, parseTree.getRoot());
			out.println();
			out.flush();
			timer.markTime("print ANTLR tree");
		}
		if (stage == TranslationStage.PARSE)
			return;

		ASTBuilder builder = frontEnd.getASTBuilder(language);
		AST ast = builder.getTranslationUnit(parseTree);

		asts.set(index, ast);
		timer.markTime("build AST for " + name);
		if (verbose) {
			out.println(bar + " Raw Translation Unit for " + name + " " + bar);
			if (task.getPrettyPrint())
				ast.prettyPrint(out, false);
			else
				ast.print(out);
			out.println();
			out.flush();
			timer.markTime("print AST for " + name);
		}
		if (stage == TranslationStage.GENERATE_ASTS)
			return;

		Analyzer analyzer = frontEnd.getStandardAnalyzer(language);
		boolean change = true;

		// if you are going to link, there is no need to do final
		// analysis because the linker will do it anyway...
		if (stage.compareTo(TranslationStage.TRANSFORM_ASTS) >= 0) {
			for (TransformRecord record : unitTask.getTransformRecords()) {
				Transformer transformer = record
						.create(frontEnd.getASTFactory());

				if (change) {
					analyzer.clear(ast);
					analyzer.analyze(ast);
				}

				AST ast2 = transformer.transform(ast);

				change = (ast != ast2);
				ast = ast2;
			}
			asts.set(index, ast);
			if (stage.compareTo(TranslationStage.LINK) >= 0)
				return;
		}
		if (change) {
			analyzer.clear(ast);
			analyzer.analyze(ast);
		}
	}

	// Public methods...

	/**
	 * Executes the complete translation task.
	 * 
	 * @throws ABCException
	 *             if there are any problems with preprocessing or parsing, or
	 *             syntax or semantics violations in the source code
	 */
	public void execute() throws ABCException {
		while (numUnitTasksDone < numUnits) {
			for (int i = numUnitTasksDone; i < numUnits; i++) {
				executeUnit(i);
				numUnitTasksDone++;
			}
			if (task.getDynamicTask() != null) {
				UnitTask[] newUnitTasks = task.getDynamicTask().generateTasks();
				int numNew = newUnitTasks.length;

				if (numNew == 0)
					break;
				numUnits += numNew;
				for (int j = 0; j < numNew; j++) {
					unitTasks.add(newUnitTasks[j]);
				}
				addNulls(numNew);
			}
		}
		if (task.getShowDiff()) {
			executeComparison();
			return;
		}
		if (task.getStage().compareTo(TranslationStage.LINK) < 0)
			return;

		program = frontEnd.link(asts.toArray(new AST[numUnits]),
				task.getLinkLanguage());
		timer.markTime("link " + numUnits + " translation units");
		if (verbose) {
			out.println(bar + " Program " + bar);
			timer.markTime("print linked program");
		}
		if (task.getStage() == TranslationStage.LINK) {
			// nothing more to do
		} else { // apply post-linking transformations...
			for (TransformRecord record : task.getTransformRecords()) {
				Transformer transformer = record
						.create(frontEnd.getASTFactory());

				if (verbose) {
					printProgram();
					out.println();
					out.println(
							bar + " Program after " + transformer + " " + bar);
					out.flush();
				}
				program.apply(transformer);
				timer.markTime("apply transformer "
						+ transformer.getShortDescription());
			}
			if (!showTime && !task.isSilent())
				printProgram();
			if (task.getShowUndefinedFunctions())
				printUnknownFunctions(out, program);
			if (!task.isSilent())
				frontEnd.getFileIndexer().print(out);
			out.flush();
		}
		if (task.getSummarize()) {
			new Summarizer(program.getAST()).print(out);
		}
	}

	/**
	 * Returns the {@link FrontEnd} used by this executor to carry out the
	 * components of the task.
	 * 
	 * @return the front end used by this executor
	 */
	public FrontEnd getFrontEnd() {
		return frontEnd;
	}

	/**
	 * Returns the preprocessing output token source for translation unit
	 * <code>index</code>. May be <code>null</code> if the task has not been
	 * executed. May be empty because the stream was fully consumed if the task
	 * extends beyond preprocessing only.
	 * 
	 * @param index
	 *            the index of the unit task
	 * @return the preprocessing output token source for that translation unit
	 */
	public CivlcTokenSource getTokenSource(int index) {
		return tokenSources.get(index);
	}

	/**
	 * Returns the parse tree for translation unit <code>index</code>. May be
	 * <code>null</code> if the task did not involve creating the parse tree.
	 * 
	 * @param index
	 *            the index of the unit task
	 * @return the parse tree for the <code>index</code>-th translation unit
	 */
	public ParseTree getParseTree(int index) {
		return parseTrees.get(index);
	}

	/**
	 * Returns the AST for translation unit <code>index</code>. May be
	 * <code>null</code> if the task did not involve AST construction.
	 * 
	 * @param index
	 *            the index of the unit task
	 * @return the AST for the <code>index</code>-th translation unit
	 */
	public AST getAST(int index) {
		return asts.get(index);
	}

	/**
	 * Returns the whole program. May be <code>null</code> if the task did not
	 * involve linking.
	 * 
	 * @return the whole program
	 */
	public Program getProgram() {
		return program;
	}

	/**
	 * Gets the current number of unit tasks. This number may increase as
	 * executor proceeds due to {@link DynamicTask}s.
	 * 
	 * @return current number of unit tasks
	 */
	public int getNumUnitTasks() {
		return numUnits;
	}

	/**
	 * Gets the number of unit tasks which have been completely executed.
	 * 
	 * @return number of unit tasks that have been executed
	 */
	public int getNumCompleteUnitTasks() {
		return numUnitTasksDone;
	}

	/**
	 * Gets the unit task of given index. Indexes run from 0 to
	 * {@link #getNumUnitTasks()} - 1.
	 * 
	 * @param index
	 *            index of unit task
	 * @return that unit task
	 */
	public UnitTask getUnitTask(int index) {
		return unitTasks.get(index);
	}

}