package polyglot.frontend;
import polyglot.ast.*;
import polyglot.types.*;
import polyglot.util.*;
import polyglot.frontend.Compiler;
import java.util.*;
/**
* A <code>Job</code> encapsulates work done by the compiler. <code>Job</code>s
* are typically associated either with a nested class (<code>InnerJob</code>s)
* or a source file (<code>SourceJob</code>s). A Job contains all information
* carried between phases of the compiler. A Job consists of a set of scheduled
* passes over the associated code.
*/
public abstract class Job
{
/** Field used for storing extension-specific information. */
protected JobExt ext;
/** The language extension used for this job. */
protected ExtensionInfo lang;
/** The AST constructed from the source file. */
protected Node ast;
/** List of passes to be run on this job. */
protected ArrayList passes;
/** Index of the next pass to run. */
protected int nextPass;
/** The currently running pass, or null. */
protected Pass runningPass;
/** True if all passes run so far have been successful. */
protected boolean status;
/** Map from pass id to pass. */
protected Map passMap;
/** Initial count of errors before running the job. */
protected int initialErrorCount;
/** True if the the job has reported an error. */
protected boolean reportedErrors;
public Job(ExtensionInfo lang, JobExt ext, Node ast) {
this.lang = lang;
this.ext = ext;
this.ast = ast;
this.passes = null;
this.passMap = null;
this.nextPass = 0;
this.runningPass = null;
this.status = true;
this.initialErrorCount = 0;
this.reportedErrors = false;
}
public JobExt ext() {
return ext;
}
/**
* Return the last <code>BarrierPass</code> that this job completed;
* return <code>null</code> if no <code>BarrierPass</code>es have
* yet been completed
*/
public BarrierPass lastBarrier() {
for (int i = nextPass - 1; i >= 0; i--) {
Pass pass = (Pass) passes.get(i);
if (pass instanceof BarrierPass) {
return (BarrierPass)pass;
}
}
return null;
}
public void setRunningPass(Pass pass) {
// The pass is not-null iff the job is running
if (pass != null) {
// We're starting to run the pass.
// Record the initial error count.
this.initialErrorCount = compiler().errorQueue().errorCount();
}
else {
// We've stopped running a pass.
// Check if the error count changed.
int errorCount = compiler().errorQueue().errorCount();
if (errorCount > initialErrorCount) {
reportedErrors = true;
}
}
runningPass = pass;
}
public boolean isRunning() {
return runningPass != null;
}
public Pass runningPass() {
return runningPass;
}
/** Get the state's AST. */
public Node ast() {
return ast;
}
/** Set the state's AST. */
public void ast(Node ast) {
this.ast = ast;
}
/** True if some pass reported an error. */
public boolean reportedErrors() {
return reportedErrors;
}
public void dump(CodeWriter cw) {
if (ast != null) {
ast.dump(cw);
}
}
/**
* By default, a <code>Job</code> does not have a context associated
* with it. Subclasses may override this method.
*/
public Context context() {
return null;
}
/**
* Get the <code>SourceJob</code> associated with this <code>Job</code>.
* If this <code>Job</code> is a <code>SourceJob</code>, then this
* object should be returned; otherwise the most suitable
* <code>SourceJob</code> should be returned. See subclasses' documentation
* for more details of what the most suitable <code>SourceJob</code> is.
*/
public abstract SourceJob sourceJob();
/**
* Return the <code>Source</code> associated with the
* <code>SourceJob</code> returned by <code>sourceJob</code>.
*/
public Source source() {
return this.sourceJob().source();
}
/**
* Returns whether the source for this job was explicitly specified
* by the user, or if it was drawn into the compilation process due
* to some dependency.
*/
public boolean userSpecified() {
return this.source().userSpecified();
}
/**
* Get the initial list of passes that this <code>Job</code> should go
* through.
* This method is called only once, from <code>init</code>.
*/
protected abstract List getPasses();
/**
* Get the list of passes that this <code>Job</code> needs to go through.
* This list is initialized with the list returned by
* <code>getPasses</code>.
*/
public final List passes() {
if (passes == null) {
init();
}
return passes;
}
private Map passMap() {
if (passMap == null) {
init();
}
return passMap;
}
/**
* Initialize the <code>passes</code> field and the
* <code>passMap</code> field.
*/
protected void init() {
passes = new ArrayList(getPasses());
passMap = new HashMap();
for (int i = 0; i < passes.size(); i++) {
Pass pass = (Pass) passes.get(i);
passMap.put(pass.id(), new Integer(i));
}
}
/**
* Return true if all of the passes been completed.
*/
public boolean completed() {
return pendingPasses().isEmpty();
}
/**
* Return a list of passes that have been completed so far.
* The list returned by <code>completedPasses</code> concatenated with the
* list returned by <code>pendingPasses</code> should be equivalent to the
* list returned by <code>passes</code>.
*/
public List completedPasses() {
return passes().subList(0, nextPass);
}
/**
* Return a list of passes that still have to be performed.
* The list returned by <code>completedPasses</code> concatenated with the
* list returned by <code>pendingPasses</code> should be equivalent to the
* list returned by <code>passes</code>.
*/
public List pendingPasses() {
return passes().subList(nextPass, passes.size());
}
/**
* Return true if the pass <code>id</code> has been completed.
*/
public boolean completed(Pass.ID id) {
Integer i = (Integer) passMap().get(id);
return i != null && i.intValue() < nextPass;
}
/**
* Return true if the pass <code>id</code> is still pending.
*/
public boolean pending(Pass.ID id) {
Integer i = (Integer) passMap().get(id);
return i != null && i.intValue() >= nextPass;
}
/**
* Get the pass identified by <code>id</code>.
*/
public Pass passByID(Pass.ID id) {
Integer i = (Integer) passMap().get(id);
if (i != null) {
return (Pass) passes().get(i.intValue());
}
throw new InternalCompilerError("No pass named \"" + id + "\".");
}
/**
* Return the pass immediately before the pass identified by <code>id</code>.
* Return <code>null</code> if no such pass exists.
*/
public Pass getPreviousTo(Pass.ID id) {
Integer i = (Integer) passMap().get(id);
if (i != null) {
if (i.intValue() == 0)
return null;
return (Pass) passes().get(i.intValue() - 1);
}
throw new InternalCompilerError("No pass named \"" + id + "\".");
}
/**
* Return the next pass to be performed. Return null if there are no
* passes left to be performed.
*/
public Pass nextPass() {
if (nextPass < passes().size()) {
Pass pass = (Pass) passes().get(nextPass);
return pass;
}
else {
return null;
}
}
public boolean status() {
return status;
}
/**
* Inform this <code>Job</code> that pass <code>p</code> has finished.
* If <code>okay</code> is <code>true</code>, then the pass
* was completed successfully; if it is <code>false</code> the pass was not
* completed successfully.
*
* Pass <code>p</code> may be any pending pass.
*/
public void finishPass(Pass p, boolean okay) {
List passes = passes();
status &= okay;
for (int i = nextPass; i < passes.size(); i++) {
Pass pass = (Pass) passes.get(i);
if (pass == p) {
nextPass = i + 1;
return;
}
}
throw new InternalCompilerError("Pass " + p + " was not a pending " +
"pass.");
}
public ExtensionInfo extensionInfo() {
return lang;
}
public Compiler compiler() {
return lang.compiler();
}
/**
* Spawn a new job. All passes between the pass <code>begin</code>
* and <code>end</code> inclusive will be performed immediately on
* the AST <code>ast</code>, and the resulting AST returned.
*
* Spawn a new job. All passes between the pass <code>begin</code>
* and <code>end</code> inclusive will be performed immediately on
* the AST <code>ast</code>.
*
* @param c the context that the AST occurs in
* @param ast the AST the new Job is for.
* @param begin the first pass to perform for this job.
* @param end the last pass to perform for this job.
* @return the new job. The caller can check the result with
* <code>j.status()</code> and get the ast with <code>j.ast()</code>.
*/
public Job spawn(Context c, Node ast, Pass.ID begin, Pass.ID end) {
return lang.spawnJob(c, ast, this, begin, end);
}
}