/*
* 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
*
* (C) Copyright IBM Corporation 2006-2010.
*/
package x10.ast;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import polyglot.ast.Block;
import polyglot.ast.Catch;
import polyglot.ast.Expr;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Stmt;
import polyglot.ast.Stmt_c;
import polyglot.ast.Term;
import polyglot.ast.Try_c;
import polyglot.main.Reporter;
import polyglot.types.CodeDef;
import polyglot.types.Context;
import polyglot.types.Def;
import polyglot.types.FieldDef;
import polyglot.types.Flags;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.util.CodeWriter;
import polyglot.util.CollectionUtil; import polyglot.util.SubtypeSet;
import x10.util.CollectionFactory;
import polyglot.util.Position;
import polyglot.visit.CFGBuilder;
import polyglot.visit.ContextVisitor;
import polyglot.visit.ExceptionChecker;
import polyglot.visit.FlowGraph;
import polyglot.visit.NodeVisitor;
import polyglot.visit.PrettyPrinter;
import polyglot.visit.PruningVisitor;
import polyglot.visit.TypeBuilder;
import x10.errors.Errors;
import x10.types.AsyncDef;
import x10.types.X10ClassDef;
import x10.types.X10MemberDef;
import x10.types.X10MethodDef;
/**
* Created on Oct 5, 2004
*
* @author Christian Grothoff
* @author Philippe Charles
* @author vj
*/
public class Async_c extends Stmt_c implements Async {
protected Stmt body;
protected List<Expr> clocks;
protected boolean clocked; // should be equal to (clocks != null && clocks.size() > 0)
protected AsyncDef asyncDef;
public Async_c(Position pos, List<Expr> clocks, Stmt body) {
super(pos);
this.clocks = clocks;
this.body = body;
}
public Async_c(Position pos, Stmt body, boolean clocked) {
super(pos);
this.clocked = true;
this.body = body;
// temporary. Needs to be initialized with clock from environment.
this.clocks = new ArrayList<Expr>();
}
public boolean clocked() { return clocked;}
public Async_c(Position p) {
super(p);
}
/* (non-Javadoc)
* @see x10.ast.Future#body()
*/
public Stmt body() {
return body;
}
/** Expression */
public List<Expr> clocks() {
return this.clocks;
}
/** clock */
public Clocked clocks(List<Expr> clocks) {
Async_c n = (Async_c) copy();
n.clocks = clocks;
return n;
}
/**
* Set the body of the statement.
*/
public Async body(Stmt body) {
Async_c n = (Async_c) copy();
n.body = body;
return n;
}
public AsyncDef asyncDef() {
return this.asyncDef;
}
public Async asyncDef(AsyncDef ci) {
if (ci == this.asyncDef) return this;
Async_c n = (Async_c) copy();
n.asyncDef = ci;
return n;
}
/** Reconstruct the statement. */
protected Async reconstruct(List<Expr> clocks, Stmt body) {
if ( body != this.body || clocks != this.clocks) {
Async_c n = (Async_c) copy();
n.clocks = clocks;
n.body = body;
return n;
}
return this;
}
/** Visit the children of the statement. */
public Node visitChildren(NodeVisitor v) {
List<Expr> clocks = visitList(this.clocks, v);
Stmt body = (Stmt) visitChild(this.body, v);
return reconstruct(clocks, body);
}
@Override
public Node buildTypesOverride(TypeBuilder tb) {
TypeSystem ts = (TypeSystem) tb.typeSystem();
X10ClassDef ct = (X10ClassDef) tb.currentClass();
assert ct != null;
Def def = tb.def();
if (def instanceof FieldDef) {
// FIXME: is this possible?
FieldDef fd = (FieldDef) def;
def = fd.initializer();
}
if (!(def instanceof CodeDef)) {
Errors.issue(tb.job(), new Errors.CannotOccurOutsideCodeBody(Errors.CannotOccurOutsideCodeBody.Element.Async, position()));
// Fake it
def = ts.initializerDef(position(), Types.ref(ct.asType()), Flags.STATIC);
}
CodeDef code = (CodeDef) def;
AsyncDef mi = (AsyncDef) AtStmt_c.createDummyAsync(position(), ts, ct.asType(), code, code.staticContext(), true);
// Unlike methods and constructors, do not create new goals for resolving the signature and body separately;
// since closures don't have names, we'll never have to resolve the signature. Just push the code context.
TypeBuilder tb2 = tb.pushCode(mi);
Async_c n = (Async_c) this.del().visitChildren(tb2);
if (code instanceof X10MemberDef) {
assert mi.thisDef() == ((X10MemberDef) code).thisDef();
}
return n.asyncDef(mi);
}
@Override
public Node typeCheckOverride(Node parent, ContextVisitor tc) {
TypeSystem ts = (TypeSystem) tc.typeSystem();
NodeVisitor v = tc.enter(parent, this);
if (v instanceof PruningVisitor) {
return this;
}
// now that placeTerm is set in this node, continue visiting children
// enterScope will ensure that placeTerm is installed in the context.
return null;
}
/**
* The evaluation of place and list of clocks is not in the scope of the async.
*/
public Context enterChildScope(Node child, Context c) {
Reporter reporter = c.typeSystem().extensionInfo().getOptions().reporter;
if (reporter.should_report(TOPICS, 5))
reporter.report(5, "enter async scope");
if (child == this.body) {
c = c.pushAsync(asyncDef);
c.x10Kind = Context.X10Kind.Async;
return c;
}
return c;
}
public Node typeCheck(ContextVisitor tc) {
TypeSystem ts = (TypeSystem) tc.typeSystem();
NodeFactory nf = (NodeFactory) tc.nodeFactory();
Context c = tc.context();
if (clocked() && ! c.inClockedFinishScope())
Errors.issue(tc.job(),
new Errors.ClockedAsyncMustBeInvokedInsideAStaticallyEnclosingClockedFinish(position()));
for (Expr e : clocks()) {
Type t = e.type();
if (!t.isSubtype(ts.Clock(), tc.context())) {
Errors.issue(tc.job(),
new Errors.TypeMustBeX10LangClock(t, e.position()));
}
}
AsyncDef def = this.asyncDef();
//if (!def.capturedEnvironment().isEmpty()) {
// System.out.println(this.position() + ": " + this + " captures "+def.capturedEnvironment());
//}
Closure_c.propagateCapturedEnvironment(c, def);
return this;
}
public String toString() {
return "async { ... }";
}
/** Write the statement to an output file. */
public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
w.write("async ");
if (clocks != null && ! clocks.isEmpty()) {
w.write("clocked (");
w.begin(0);
for (Iterator<Expr> i = clocks.iterator(); i.hasNext(); ) {
Expr e = i.next();
print(e, w, tr);
if (i.hasNext()) {
w.write(",");
w.allowBreak(0, " ");
}
}
w.end();
w.write(")");
}
printSubStmt(body, w, tr);
}
/**
* Return the first (sub)term performed when evaluating this
* term.
*/
public Term firstChild() {
if (clocks() == null || clocks().isEmpty()) {
return body;
} else {
return (Term) clocks().get(0);
}
}
/**
* Visit this term in evaluation order.
* [IP] Treat this as a conditional to make sure the following
* statements are always reachable.
* FIXME: We should really build our own CFG, push a new context,
* and disallow uses of "continue", "break", etc. in asyncs.
*/
public <S> List<S> acceptCFG(CFGBuilder v, List<S> succs) {
if (clocks() == null || clocks().isEmpty()) {
v.push(this).visitCFG(body, this, EXIT);
} else {
v.visitCFGList(clocks, body, ENTRY);
v.push(this).visitCFG(body, this, EXIT);
}
v.edge(v,this,ENTRY,this,EXIT,FlowGraph.EDGE_KEY_FALSE); // a trick to make sure we treat Async like a conditional for the purpose of initialization. see InitChecker.
return succs;
}
/**
* Bypass all children when peforming an exception check.
* exceptionCheck(), called from ExceptionChecker.leave(),
* will handle visiting children.
*/
public NodeVisitor exceptionCheckEnter(ExceptionChecker ec) {
return new PruningVisitor();
}
/**
* Visit the clocks as normal. Add CheckedThrowable to the list of caught exceptions when visiting body.
*/
public Node exceptionCheck(ExceptionChecker ec) {
TypeSystem ts = ec.typeSystem();
List<Expr> clocks = this.visitList(this.clocks, ec);
ExceptionChecker newec = ec.push();
newec = newec.push(ts.CheckedThrowable());
Block body = (Block) this.visitChild(this.body, newec);
Async_c t = (Async_c)super.exceptionCheck(ec);
return t.reconstruct(clocks, body);
}
private static final Collection<String> TOPICS =
CollectionUtil.list(Reporter.types, Reporter.context);
}