/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* This file was originally derived from the Polyglot extensible compiler framework.
*
* (C) Copyright 2000-2007 Polyglot project group, Cornell University
* (C) Copyright IBM Corporation 2007-2012.
*/
package polyglot.frontend;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import polyglot.ast.NodeFactory;
import polyglot.ast.PackageNode;
import polyglot.ast.SourceFile;
import polyglot.types.QName;
import polyglot.types.TypeSystem;
import polyglot.types.reflect.ClassFileLoader;
import polyglot.util.CodeWriter;
import polyglot.util.ErrorInfo;
import polyglot.util.ErrorLimitError;
import polyglot.util.ErrorQueue;
import polyglot.util.InternalCompilerError;
import polyglot.util.OptimalCodeWriter;
import polyglot.util.SimpleCodeWriter;
import polyglot.util.StdErrorQueue;
import x10.optimizations.inlining.DeclStore;
import x10.optimizations.inlining.Inliner;
import x10.util.CollectionFactory;
/**
* This is the main entry point for the compiler. It contains a work list that
* contains entries for all classes that must be compiled (or otherwise worked
* on).
*/
public class Compiler
{
/** The extension info */
private ExtensionInfo extensionInfo;
/** A list of all extension infos active in this compiler. */
private List<ExtensionInfo> allExtensions;
/** The error queue handles outputting error messages. */
private ErrorQueue eq;
/** AST information retained for use by the Inliner.
*
* This needs to be preserved until all Job's have been inlined.
* It may be blown away after the code-gen barrier.
*
*/
private DeclStore inlinerData;
public DeclStore getInlinerData(Job job, TypeSystem ts, NodeFactory nf) {
if (null == inlinerData)
inlinerData = new DeclStore(ts, nf);
inlinerData.startJob(job);
return inlinerData;
}
/**
* Do this only after all inlining is over (i.e. after the code-gen barrier)!
*/
public void purgeInlinerData() {
inlinerData = null;
}
/** FIXME: TEMPRORARY Inliner hack: Errors in speculative compilation for inlining should not be fatal
* @depricated DO NOT USE
* TODO remove this, if inlining results in an error, compilation should fail!
*/
public ErrorQueue swapErrorQueue (ErrorQueue newEq) {
ErrorQueue oldEq = eq;
eq = newEq;
return oldEq;
}
/**
* Class file loader. There should be only one of these so we can cache
* across type systems.
*/
private ClassFileLoader loader;
/**
* The output files generated by the compiler. This is used to to call the
* post-compiler (e.g., javac).
*/
private Map<String,Collection<String>> outputFiles = CollectionFactory.newHashMap();
/**
* Initialize the compiler.
*
* @param extensionInfo the <code>ExtensionInfo</code> this compiler is for.
*/
public Compiler(ExtensionInfo extensionInfo) {
this(extensionInfo, new StdErrorQueue(System.err,
extensionInfo.getOptions().error_count,
extensionInfo.compilerName()));
}
/**
* Initialize the compiler.
*
* @param extensionInfo the <code>ExtensionInfo</code> this compiler is for.
*/
public Compiler(ExtensionInfo extensionInfo, ErrorQueue eq) {
this.extensionInfo = extensionInfo;
this.eq = eq;
this.allExtensions = new ArrayList<ExtensionInfo>(2);
loader = new ClassFileLoader(extensionInfo);
// This must be done last.
extensionInfo.initCompiler(this);
}
/*** Compiler statistics gatherer. */
public Stats stats;
/** Return a mapping from input files to all output files produced from that input file
* */
public Map<String, Collection<String>> outputFiles() {
return outputFiles;
}
public Collection<String> flatOutputFiles() {
Set<String> ans = CollectionFactory.newHashSet();
for (Collection<String> files : outputFiles.values()) {
ans.addAll(files);
}
return ans;
}
/**
* Add an output file
* @param source the source file
* @param output the output file
*/
public void addOutputFile(String source, String output) {
Collection<String> outputs = outputFiles.get(source);
if (outputs == null) {
outputs = CollectionFactory.newHashSet();
outputFiles.put(source, outputs);
}
outputs.add(output);
}
/**
* Add an output file
* @param source the source
* @param output the output file
*/
public void addOutputFile(SourceFile source, String output) {
PackageNode pkg = source.package_();
String key = pkg == null ? "" : pkg.package_().get().fullName().toString() + ".";
key += source.source().name().substring(0, source.source().name().lastIndexOf(".x10"));
key = key.replace('.', File.separatorChar);
addOutputFile(key, output);
}
/**
* Add an output file
* @param gname the qualified name of the source entity
* @param output the output file
*/
public void addOutputFile(QName qname, String output) {
String key = qname.toString().replace('.', File.separatorChar);
addOutputFile(key, output);
}
/**
* Compile all the files listed in the set of strings <code>source</code>.
* Return true on success. The method <code>outputFiles</code> can be
* used to obtain the output of the compilation. This is the main entry
* point for the compiler, called from main().
*/
public boolean compileFiles(Collection<String> filenames) {
List<Source> sources = new ArrayList<Source>(filenames.size());
// Construct a list of sources from the list of file names.
try {
try {
SourceLoader source_loader = sourceExtension().sourceLoader();
for (String sourceName : filenames) {
// mark this source as being explicitly specified
// by the user.
FileSource source = source_loader.fileSource(sourceName, true);
sources.add(source);
}
}
catch (FileNotFoundException e) {
eq.enqueue(ErrorInfo.IO_ERROR,
"Cannot find source file \"" + e.getMessage() + "\".");
eq.flush();
return false;
}
catch (IOException e) {
eq.enqueue(ErrorInfo.IO_ERROR, e.getMessage());
eq.flush();
return false;
}
catch (InternalCompilerError e) {
// Report it like other errors, but rethrow to get the stack
// trace.
try {
eq.enqueue(ErrorInfo.INTERNAL_ERROR, e.message(),
e.position());
}
catch (ErrorLimitError e2) {
}
eq.flush();
throw e;
}
catch (RuntimeException e) {
// Flush the error queue, then rethrow to get the stack trace.
eq.flush();
throw e;
}
}
catch (ErrorLimitError e) {
eq.flush();
return false;
}
return compile(sources);
}
/**
* Compile all the files listed in the set of Sources <code>source</code>.
* Return true on success. The method <code>outputFiles</code> can be
* used to obtain the output of the compilation. This is the main entry
* point for the compiler, called from main().
*/
public boolean compile(Collection<Source> sources) {
boolean okay = false;
try {
try {
Scheduler scheduler = sourceExtension().scheduler();
// clearing state
final x10.ExtensionInfo x10ext = (x10.ExtensionInfo) extensionInfo;
x10ext.warningSet().clear(); // again, to clear caching of warnings (to prevent duplicates)
x10ext.errorSet().clear();
scheduler.clearAll(sources); // to clear the fail flag of the scheduler
List<Job> jobs = new ArrayList<Job>();
// Create a job for each source file.
for (Source source : sources) {
if (scheduler.sourceHasJob(source)) continue; // in case we invoke compile again and we already compiled this source
// Add a new SourceJob for the given source. If a Job for the source
// already exists, then we will be given the existing job.
Job job = scheduler.addJob(source);
jobs.add(job);
}
scheduler.setCommandLineJobs(jobs);
for (Job job : jobs) {
scheduler.addDependenciesForJob(job, true);
}
// Compile the files to completion.
okay = scheduler.runToCompletion();
}
catch (InternalCompilerError e) {
// Report it like other errors, but rethrow to get the stack trace.
try {
eq.enqueue(ErrorInfo.INTERNAL_ERROR, e.message() != null ? e.message() : "InternalCompilerError", e.position());
}
catch (ErrorLimitError e2) {
}
eq.flush();
throw e;
}
catch (StackOverflowError e) {
// Flush the error queue, then rethrow to get the stack trace.
eq.flush();
throw e;
}
catch (RuntimeException e) {
// Flush the error queue, then rethrow to get the stack trace.
eq.flush();
throw e;
}
}
catch (ErrorLimitError e) {
}
eq.flush();
return okay;
}
/** Get the compiler's class file loader. */
public ClassFileLoader loader() {
return this.loader;
}
/** Should fully qualified class names be used in the output? */
public boolean useFullyQualifiedNames() {
return extensionInfo.getOptions().fully_qualified_names;
}
/** Return a list of all languages extensions active in the compiler. */
public void addExtension(ExtensionInfo ext) {
allExtensions.add(ext);
}
/** Return a list of all languages extensions active in the compiler. */
public List<ExtensionInfo> allExtensions() {
return allExtensions;
}
/** Get information about the language extension being compiled. */
public ExtensionInfo sourceExtension() {
return extensionInfo;
}
/** Maximum number of characters on each line of output */
public int outputWidth() {
return extensionInfo.getOptions().output_width;
}
/** Should class info be serialized into the output? */
public boolean serializeClassInfo() {
return extensionInfo.getOptions().serialize_type_info;
}
/** Get the compiler's error queue. */
public ErrorQueue errorQueue() {
return eq;
}
static {
// FIXME: if we get an io error (due to too many files open, for example)
// it will throw an exception. but, we won't be able to do anything with
// it since the exception handlers will want to load
// polyglot.util.CodeWriter and polyglot.util.ErrorInfo to print and
// enqueue the error; but the classes must be in memory since the io
// can't open any files; thus, we force the classloader to load the class
// file.
try {
ClassLoader loader = Compiler.class.getClassLoader();
// loader.loadClass("polyglot.util.CodeWriter");
// loader.loadClass("polyglot.util.ErrorInfo");
loader.loadClass("polyglot.util.StdErrorQueue");
}
catch (ClassNotFoundException e) {
throw new InternalCompilerError(e.getMessage());
}
}
public static CodeWriter createCodeWriter(OutputStream w) {
return createCodeWriter(w, Globals.Options().output_width);
}
public static CodeWriter createCodeWriter(OutputStream w, int width) {
if (Globals.Options().use_simple_code_writer)
return new SimpleCodeWriter(w, width);
else
return new OptimalCodeWriter(w, width);
}
public static CodeWriter createCodeWriter(Writer w) {
return createCodeWriter(w, Globals.Options().output_width);
}
public static CodeWriter createCodeWriter(Writer w, int width) {
if (Globals.Options().use_simple_code_writer)
return new SimpleCodeWriter(w, width);
else
return new OptimalCodeWriter(w, width);
}
}