/*
* 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.visit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import polyglot.ast.ArrayAccess;
import polyglot.ast.Assert;
import polyglot.ast.Assign;
import polyglot.ast.Binary;
import polyglot.ast.Block;
import polyglot.ast.BooleanLit;
import polyglot.ast.Branch;
import polyglot.ast.Call;
import polyglot.ast.Case;
import polyglot.ast.Cast;
import polyglot.ast.Catch;
import polyglot.ast.Conditional;
import polyglot.ast.ConstructorCall;
import polyglot.ast.ConstructorDecl;
import polyglot.ast.Do;
import polyglot.ast.Do_c;
import polyglot.ast.Empty;
import polyglot.ast.Eval;
import polyglot.ast.Expr;
import polyglot.ast.Field;
import polyglot.ast.FieldAssign;
import polyglot.ast.FieldDecl;
import polyglot.ast.For;
import polyglot.ast.ForInit;
import polyglot.ast.ForUpdate;
import polyglot.ast.Id;
import polyglot.ast.If;
import polyglot.ast.Instanceof;
import polyglot.ast.Labeled;
import polyglot.ast.Lit;
import polyglot.ast.Local;
import polyglot.ast.LocalAssign;
import polyglot.ast.LocalDecl;
import polyglot.ast.Local_c;
import polyglot.ast.Loop;
import polyglot.ast.New;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.ProcedureCall;
import polyglot.ast.Return;
import polyglot.ast.SourceFile;
import polyglot.ast.Special;
import polyglot.ast.Special_c;
import polyglot.ast.Stmt;
import polyglot.ast.Switch;
import polyglot.ast.SwitchBlock;
import polyglot.ast.Throw;
import polyglot.ast.Try;
import polyglot.ast.TypeNode;
import polyglot.ast.Unary;
import polyglot.ast.While;
import polyglot.ast.While_c;
import polyglot.frontend.Job;
import polyglot.frontend.Source;
import polyglot.types.ClassType;
import polyglot.types.Flags;
import polyglot.types.Name;
import polyglot.types.TypeSystem;
import polyglot.util.InternalCompilerError;
import polyglot.util.Position;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import x10.ast.AssignPropertyCall;
import x10.ast.Async;
import x10.ast.AtEach;
import x10.ast.AtExpr;
import x10.ast.AtStmt;
import x10.ast.Atomic;
import x10.ast.Closure;
import x10.ast.ClosureCall;
import x10.ast.Finish;
import x10.ast.ForLoop;
import x10.ast.HasZeroTest;
import x10.ast.Here;
import x10.ast.Here_c;
import x10.ast.Next;
import x10.ast.Offer;
import x10.ast.ParExpr;
import x10.ast.RemoteActivityInvocation;
import x10.ast.SettableAssign;
import x10.ast.StmtExpr;
import x10.ast.StmtSeq;
import x10.ast.SubtypeTest;
import x10.ast.Tuple;
import x10.ast.When;
import x10.ast.X10ClassDecl;
import x10.errors.Warnings;
import x10.extension.X10Ext;
import x10.types.X10FieldInstance;
import x10.util.AltSynthesizer;
/**
* @author Bowen Alpern
*
*/
public final class ExpressionFlattener extends ContextVisitor {
private static final boolean DEBUG = false;
private static final boolean XTENLANG_2055 = true; // bug work around: don't flatten Marshall.x10
private static final boolean XTENLANG_2336 = true; // bug work around: don't flatten Runtime.x10
private final TypeSystem xts;
private AltSynthesizer syn; // move functionality to Synthesizer
private final SideEffectDetector sed;
/*
* When flattening For statements we introduce a new label for the body of the
* for loop. Continue statements within the For statement must be rewritten as
* breaks to the new label. The Continues are processed before the For statements
* so we must create the new labels on enter in case it is needed. Other loops
* are not affected.
*/
private class labelInfoStruct {
Id oldLabel;
Id newLabel;
boolean oldLabelUsed;
boolean newLabelUsed;
boolean loop;
}
private ArrayList<labelInfoStruct> labelInfo = new ArrayList<labelInfoStruct>(20);
/**
* @param job the job to run
* @param ts the type system object
* @param nf the factory to produce AST nodes
*/
public ExpressionFlattener(Job job, TypeSystem ts, NodeFactory nf) {
super(job, ts, nf);
xts = ts;
syn = new AltSynthesizer(ts, nf);
sed = new SideEffectDetector(job, ts, nf);
}
@Override
public NodeVisitor begin() {
sed.begin();
return super.begin();
}
/**
* Don't visit nodes that cannot be flattened.
*
* @param n the node to be visited (or not)
* @return n if the node is NOT to be visited, otherwise null
*/
@Override
public Node override(Node n) {
if (n instanceof X10ClassDecl) {
if (DEBUG) System.out.println("DEBUG: flattening: " +((X10ClassDecl) n).classDef()+ " (@" +((X10ClassDecl) n).position()+ ")");
return null;
}
if (cannotFlatten(n)) return n;
return null;
}
/**
* Is this Node unflattenable?
*
* Nodes that cannot be flattened:
* Assert requires the Java back-end handle StmtExpr's to achieve the right semantics.
* When requires its tests to be evaluated atomically.
* Constructors and class initializers cannot be flattened because of Java's initialization rules (X10 proto would fix
* this but the Java back-end must be able to handle the new code).
* Case Expr's are constant expressions that will eventually get evaluated at compile time.
*
* @param n an AST node that might be flattened
* @return true if the node cannot be flattened, false otherwise
*/
public static boolean cannotFlatten(Node n) {
Position pos = n.position(); // for DEBUGGING
if (n instanceof SourceFile){
Source s = ((SourceFile) n).source();
if (XTENLANG_2336 && s.name().equals("Runtime.x10")) { // BUG: cannot flatten Runtime
return true;
}
if (XTENLANG_2055 && s.name().equals("Marshal.x10")) { // BUG: can't flatten Marshal
return true;
}
}
if (n instanceof ConstructorDecl) { // can't flatten constructors unless local assignments can precede super() and this() in Java
ClassType type = ((ConstructorDecl) n).constructorDef().container().get().toClass();
if (ConstructorSplitterVisitor.isUnsplittable(type))
return true;
}
if (n instanceof FieldDecl) { // can't flatten class initializes until assignments can precede field declarations
return true;
}
if (n instanceof Assert) { // can't flatten assert's until Java can handle StmtExpr's
return true;
}
if (n instanceof When) { // can't flatten when's because tests need to be evaluated atomically
return true;
}
if (n instanceof Case) { // case Expr's will become constants, don't screw with them.
return true;
}
return false;
}
/**
* @param job
* @return
*/
public static boolean javaBackend(Job job) {
if (job.extensionInfo() instanceof x10c.ExtensionInfo)
return true;
return false;
}
@Override
protected NodeVisitor enterCall(Node parent, Node child) {
if (child instanceof Loop) {
labelInfoStruct lblInfo = new labelInfoStruct();
if (child instanceof For) {
lblInfo.newLabel = syn.createLabel(child.position());
}
if (parent instanceof Labeled) {
lblInfo.oldLabel = ((Labeled) parent).labelNode();
}
lblInfo.loop = true;
labelInfo.add(lblInfo);
} else if (child instanceof Block && parent instanceof Labeled) {
labelInfoStruct lblInfo = new labelInfoStruct();
lblInfo.oldLabel = ((Labeled) parent).labelNode();
labelInfo.add(lblInfo);
}
return this;
}
/* (non-Javadoc)
* @see polyglot.visit.ErrorHandlingVisitor#leaveCall(polyglot.ast.Node, polyglot.ast.Node, polyglot.visit.NodeVisitor)
*
* Flatten statements (Stmt) and expressions (Expr).
*/
@Override
public Node leaveCall(Node parent, Node old, Node n, NodeVisitor v) {
if (n instanceof Labeled)
return flattenLabeled((Labeled) n);
if (n instanceof Loop) {
Node returnNode = flattenLoop(n, parent instanceof Labeled);
labelInfo.remove(labelInfo.size()-1);
return returnNode;
}
if (n instanceof Expr) return flattenExpr((Expr) n);
if (n instanceof Block) {
// After StmtExpr Blocks have been flattened
Node returnNode = flattenBlock((Block) n, parent instanceof Labeled);
if (parent instanceof Labeled) {
labelInfo.remove(labelInfo.size() - 1);
}
return returnNode;
}
if (n instanceof Stmt) return flattenStmt((Stmt) n);
return n;
}
/**
* @param stmt
* @return
*/
private Stmt flattenLabeled(Labeled stmt) {
return stmt.statement();
}
/**
* @param n
* @param list
* @return
*/
private Node flattenLoop(Node n, boolean labeled) {
if (n instanceof For) return flattenFor((For) n, labeled);
if (n instanceof ForLoop) return flattenForLoop((ForLoop) n, labeled);
if (n instanceof While) return flattenWhile((While_c) n, labeled); // TODO: fix polyglot to allow getters in the interface
if (n instanceof Do) return flattenDo((Do_c) n, labeled); // TODO: fix polyglot to allow getters in the interface
if (n instanceof AtEach) return flattenAtEach((AtEach) n, labeled);
assert false;
return null;
}
/**
* Flatten Expr's. (Flat expressions do not contain sub-expressions that arn't literal constants or final variables.)
* The result may be a StmtExpr. (Statement flattening will eliminating these.)
*
* @param expr the Expr to be flattened
* @return a flat Expr equivalent to expr (often a StmtExpr)
*/
private Expr flattenExpr(Expr expr) {
if (expr instanceof Lit) return flattenPrimary(expr);
else if (expr instanceof Local_c) return flattenPrimary(expr);
else if (expr instanceof Special_c) return flattenPrimary(expr);
else if (expr instanceof Here_c) return flattenPrimary(expr);
else if (expr instanceof ParExpr) return flattenParExpr((ParExpr) expr);
else if (expr instanceof Field) return flattenField((Field) expr);
else if (expr instanceof ArrayAccess) return flattenArrayAccess((ArrayAccess) expr);
else if (expr instanceof SettableAssign) return flattenSettableAssign((SettableAssign) expr);
else if (expr instanceof FieldAssign) return flattenFieldAssign((FieldAssign) expr);
else if (expr instanceof LocalAssign) return flattenLocalAssign((LocalAssign) expr);
else if (expr instanceof Call) return flattenMethodCall((Call) expr);
else if (expr instanceof New) return flattenNew((New) expr);
else if (expr instanceof Tuple) return flattenTuple((Tuple) expr);
else if (expr instanceof Binary) return flattenBinary((Binary) expr);
else if (expr instanceof Unary) return flattenUnary((Unary) expr);
else if (expr instanceof Cast) return flattenCast((Cast) expr);
else if (expr instanceof Instanceof) return flattenInstanceof((Instanceof) expr);
else if (expr instanceof AtExpr) return flattenAtExpr((AtExpr) expr);
else if (expr instanceof SubtypeTest) return flattenSubtypeTest((SubtypeTest) expr);
else if (expr instanceof HasZeroTest) return expr;
else if (expr instanceof ClosureCall) return flattenClosureCall((ClosureCall) expr);
else if (expr instanceof Conditional) return flattenConditional((Conditional) expr);
else if (expr instanceof StmtExpr) return flattenStmtExpr((StmtExpr) expr);
else if (expr instanceof Closure) return expr;
else {
if (DEBUG) System.err.println("INFO: ExpressionFlattener.flattenExpr: default class: " +expr.getClass()+ " (" +expr+ ") at" +expr.position());
return expr;
}
}
/**
* Flatten a primary expression.
* (no-op: Primary expressions are already flat!)
*
* @param expr the primary expression to be flattened
* @return (the already flat) expr
*/
private Expr flattenPrimary(Expr expr) {
return expr;
}
/**
* Flatten a statement expression (which almost certainly will already be flat).
* <pre>
* ({S; ({s1; t1}) }) -> ({S; s1; t1})
* </pre>
*
* @param expr a statement expression to be flattened
* @return a flat expression equivalent to expr
*/
private Expr flattenStmtExpr(StmtExpr expr) {
if (expr.result() instanceof StmtExpr) { // the inliner produces StmtExpr's
expr = expr.append(getStatements(expr.result()));
expr = expr.result(getResult(expr.result()));
}
return expr;
}
/**
* Flatten a offer statement.
* <pre>
* offer ({s1; e1}); -> s1; val t1 = e1; offer t1;
* </pre>
*
* @param stmt the offer statement to be flattened.
* @return a flat statement with the same semantics as stmt
*/
private StmtSeq flattenOffer(Offer stmt){
List<Stmt> stmts = new ArrayList<Stmt>();
Expr primary = getPrimaryAndStatements(stmt.expr(), stmts);
stmts.add(stmt.expr(primary));
return syn.createStmtSeq(stmt.position(), stmts);
}
/**
* Flatten a parenthesized expression.
* (This should never happen, but it does.)
* <pre>
* (({s1; e1})) -> ({s1; val t1=e1; t1})
* (e) -> e
* </pre>
*
* @param expr the parenthesized expression to be flattened
* @return a flat expression equivalent to expr (without the parens)
*/
private Expr flattenParExpr(ParExpr expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr primary = expr;
while (primary instanceof ParExpr) {
primary = getPrimaryAndStatements(((ParExpr) primary).expr(), stmts);
}
return toFlatExpr(expr.position(), stmts, primary);
}
/**
* Flatten an array access.
* (Note: the StmtExpr must end in an array access in case it gets bumped: a(i)++.)
* {s1; e1}({s2; e2}) -> {s1; val t1=e1; s2; val t2=e2; t1(t2)}
*
* @param expr
* @return
*/
private Expr flattenArrayAccess(ArrayAccess expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr array = getPrimaryAndStatements(expr.array(), stmts);
Expr index = getPrimaryAndStatements(expr.index(), stmts);
return toFlatExpr(expr.position(), stmts, expr.array(array).index(index));
}
/**
* Flatten an object (or struct) allocation.
* <pre>
* new T(({s1; e1}), ..., ({sk; ek})) -> ({s1; val t1 = e1; ... sk; val tk = ek; val t = new T(t1, ..., tk); t})
* ({s1; e1}).new T(({s2; e2}), ..., ({sk; ek})) -> ({s1; val t1 = e1; ... sk; val tk = ek; val t = t1.new T(t2, ..., tk); t})
* </pre>
*
* @param expr the New expression to be flattened
* @return a flat expression equivalent to expr
*/
private Expr flattenNew(New expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
List<Expr> args = new ArrayList<Expr>();
for (Expr e : expr.arguments()) {
Expr primary = getPrimaryAndStatements(e, stmts);
args.add(primary);
}
return toFlatExpr(expr.position(), stmts, (Expr) expr.arguments(args));
}
/**
* Flatten a field access.
* <pre>
* T.f -> T.f
* ({s1; e1}).f -> ({s1; val t1 = e1; t1.f})
* </pre>
*
* @param expr the field access to be flattened
* @return a flat expression equivalent to expr
*/
private Expr flattenField(Field expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
if (expr.target() instanceof TypeNode) {
return expr;
}
Expr target = getPrimaryAndStatements((Expr) expr.target(), stmts);
return toFlatExpr(expr.position(), stmts, expr.target(target));
}
/**
* Flatten a unary expression.
* If the unary operator (++ or --) updates its argument:
* <pre>
* op ({s1; e1}) -> ({s1; op e1})
* ({s1; e1}) op -> ({s1; e1 op})
* </pre>,
* otherwise
* <pre>
* ! ({s1; true}) -> ({s1; false})
* ! ({s1; false}) -> ({s1; true})
* op ({s1; e1}) -> ({s1; val t1 = e1; op t1})
* </pre>
*
* @param expr the unary expression to flatten
* @return a flat expression equivalent to expr
*/
private Expr flattenUnary(Unary expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr result;
if (isUpdateOp(expr.operator())) {
stmts.addAll(getStatements(expr.expr()));
result = getResult(expr.expr());
} else {
result = getPrimaryAndStatements(expr.expr(), stmts);
if (result instanceof BooleanLit) {
assert (Unary.NOT == expr.operator());
if (((BooleanLit) result).value()) {
result = syn.createFalse(expr.position());
} else {
result = syn.createTrue(expr.position());
}
return toFlatExpr(expr.position(), stmts, result);
}
}
return toFlatExpr(expr.position(), stmts, expr.expr(result));
}
/**
* Does the given unary operator update it arguement? (Is it "++" or "--"?)
*
* @param op the unary operator in question
* @return true, if op updates its argument; false, otherwise
*/
private boolean isUpdateOp(polyglot.ast.Unary.Operator op) {
return op.equals(Unary.PRE_DEC) || op.equals(Unary.PRE_INC) || op.equals(Unary.POST_DEC) || op.equals(Unary.POST_INC);
}
/**
* Flatten a cast expression.
* <pre>
* ({s1; e1}) as T -> ({s1; val t1 = e1; t1 as T})
* </pre>
*
* @param expr the cast to be flattened
* @return a flat expression equivalent to expr
*/
private Expr flattenCast(Cast expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr primary = getPrimaryAndStatements(expr.expr(), stmts);
return toFlatExpr(expr.position(), stmts, expr.expr(primary));
}
/**
* Flatten an instanceof expression.
* <pre>
* ({s1; e1}) instanceof T -> ({s1; val t1 = e1; t1 instanceof T})
* </pre>
*
* @param expr the instanceof expression to be flattened
* @return a flat expression equivalent to expr
*/
private Expr flattenInstanceof(Instanceof expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr primary = getPrimaryAndStatements(expr.expr(), stmts);
return toFlatExpr(expr.position(), stmts, expr.expr(primary));
}
/**
* Flatten an At expression.
* (Only the Place needs to be flattened; the body is already flat.)
* <pre>
* at (({s1; e1})) {S; ({s2; e2})} -> ({s1; val t1 = e1; at (t1) {S; s2; e2} })
* </pre>
*
* @param expr the at expression to be flattened
* @return a flat expression equivalent to expr
*/
private Expr flattenAtExpr(AtExpr expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
return toFlatExpr(expr.position(), stmts, (Expr) flattenRemoteActivityInvocation(expr, stmts));
}
/**
* Flatten a sub-type test. This is a no-op.
* <pre>
* T1 <: T2 -> T1 <: T2
* </pre>
*
* @param expr the sub-type text to be flattened
* @return a flat expression equivalent to expr
* TODO: need source code to test this
*/
private Expr flattenSubtypeTest(SubtypeTest expr) {
return expr;
}
/**
* Flatten a method call expression.
* <pre>
* ({s1; e1}).m(({s2; e2}), ..., ({sk; ek})) -> ({s1; val t1 = e1; ... sk; val tk = ek; t1.m(t2, ..., tk) })
* ({s1; e1}).m(({s2; e2}), ..., ({sk; ek})) -> ({s1; val t1 = e1; ... sk; val tk = ek; Eval(t1.m(t2, ..., tk)) }) // (void call)
* T.m(({s1; e1}), ..., ({sk; ek})) -> ({s1; val t1 = e1; ... sk; val tk = ek; T.m(t1, ..., tk) })
* T.m(({s1; e1}), ..., ({sk; ek})) -> ({s1; val t1 = e1; ... sk; val tk = ek; Eval( T.m(t1, ..., tk)) }) // (void call)
*</pre>
*
* @param expr the method call to flatten
* @return a flat expression equivalent to expr
*/
private Expr flattenMethodCall(Call expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
if (expr.target() instanceof Expr) {
Expr target = getPrimaryAndStatements((Expr) expr.target(), stmts);
expr = expr.target(target);
}
List<Expr> args = new ArrayList<Expr>();
for (Expr e : expr.arguments()) {
Expr primary = getPrimaryAndStatements(e, stmts);
args.add(primary);
};
return toFlatExpr(expr.position(), stmts, (Expr) expr.arguments(args));
}
/**
* Flatten a closure call expression.
* <pre>
* ({s1; e1})(({s2; e2}), ..., ({sk; ek})) -> ({s1; val t1 = e1; ... sk; val tk = ek; t1(t2, ..., tk) })
* ({s1; e1})(({s2; e2}), ..., ({sk; ek})) -> ({s1; val t1 = e1; ... sk; val tk = ek; Eval(t1(t2, ..., tk)) }) // (void call)
*</pre>
*
* @param expr the closure call to flatten
* @return a flat expression equivalent to expr
*/
private Expr flattenClosureCall(ClosureCall expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr primary = getPrimaryAndStatements(expr.target(), stmts);
expr = expr.target(primary);
List<Expr> args = new ArrayList<Expr>();
for (Expr e : expr.arguments()) {
Expr primary2 = getPrimaryAndStatements(e, stmts);
args.add(primary2);
}
return toFlatExpr(expr.position(), stmts, (Expr) expr.arguments(args));
}
/**
* Flatten a tuple expression.
* <pre>
* [({s1; e1}), ..., ({sk; ek})] -> {s1; val t1 = e1; ... sk; val tk = ek; [t1, ..., tk] })
* </pre>
*
* @param expr the tuple to flatten
* @return a flat expression equivalent to expr
*/
private Expr flattenTuple(Tuple expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
List<Expr> args = new ArrayList<Expr>();
for (Expr e : expr.arguments()) {
Expr primary = getPrimaryAndStatements(e, stmts);
args.add(primary);
}
return toFlatExpr(expr.position(), stmts, expr.arguments(args));
}
/**
* Flatten a binary expression.
* (Short-circuit AND (&&) and OR (||) are special.)
* <pre>
*
* ({s1; false} && {s2; e2}) -> ({s1; false})
* ({s1; true} && {s2; e2}) -> ({s1; s2; e2})
* ({s1; e1} && {s2; e2}) -> ({s1; var t = e1; if ( t){s2; t = e2;}; t})
* ({s1; false} || {s2; e2}) -> ({s1; s2; e2})
* ({s1; true} || {s2; e2}) -> ({s1; true})
* ({s1; e1} || {s2; e2}) -> ({s1; var t = e1; if (!t){s2; t =be2;}; t})
* ({s1; e1} op {s2; e2}) -> ({s1; val t1 = e1; s2; val t2 = e2; t1 op t2})
* </pre>
*
* @param expr the binary expression to flatten
* @return a flat expression equivalent to expr
*/
private Expr flattenBinary(Binary expr) {
Position pos = expr.position();
List<Stmt> stmts = new ArrayList<Stmt>();
if (expr.operator().equals(Binary.COND_AND)) {
stmts.addAll(getStatements(expr.left()));
Expr left = getResult(expr.left());
if (left instanceof BooleanLit) {
if (false == ((BooleanLit) left).value())
return toFlatExpr(pos, stmts, left);
stmts.addAll(getStatements(expr.right()));
return toFlatExpr(pos, stmts, getResult(expr.right()));
}
LocalDecl tmpLDecl = syn.createLocalDecl( pos,
Flags.NONE,
syn.createTemporaryName(),
xts.Boolean(),
left );
stmts.add(tmpLDecl);
List<Stmt> andStmts = new ArrayList<Stmt>();
andStmts.addAll(getStatements(expr.right()));
Expr right = getResult(expr.right());
andStmts.add(syn.createAssignment( pos,
syn.createLocal(pos, tmpLDecl),
Assign.ASSIGN,
right,
this ));
stmts.add(syn.createIf( pos,
syn.createLocal(pos, tmpLDecl),
syn.createBlock(pos, andStmts),
null ));
return toFlatExpr(pos, stmts, syn.createLocal(pos, tmpLDecl));
} else if (expr.operator().equals(Binary.COND_OR)) {
stmts.addAll(getStatements(expr.left()));
Expr left = getResult(expr.left());
if (left instanceof BooleanLit) {
if (true == ((BooleanLit) left).value())
return toFlatExpr(pos, stmts, left);
stmts.addAll(getStatements(expr.right()));
return toFlatExpr(pos, stmts, getResult(expr.right()));
}
LocalDecl tmpLDecl = syn.createLocalDecl( pos,
Flags.NONE,
syn.createTemporaryName(),
xts.Boolean(),
left );
stmts.add(tmpLDecl);
List<Stmt> orStmts = new ArrayList<Stmt>();
orStmts.addAll(getStatements(expr.right()));
Expr right = getResult(expr.right());
orStmts.add(syn.createAssignment( pos,
syn.createLocal(pos, tmpLDecl),
Assign.ASSIGN,
right,
this ));
stmts.add(syn.createIf( pos,
syn.createNot(syn.createLocal(pos, tmpLDecl), this),
syn.createBlock(pos, orStmts),
null ));
return toFlatExpr(pos, stmts, syn.createLocal(pos, tmpLDecl));
}
Expr left = getPrimaryAndStatements(expr.left(), stmts);
Expr right = getPrimaryAndStatements(expr.right(), stmts);
return toFlatExpr(pos, stmts, expr.left(left).right(right));
}
/**
* Flatten an assignment to a field.
* <pre>
* T.f op= ({s1; e1}) -> ({s1; val t1 = e1; T.f op= t1})
* ({s1; e1}).f op= ({s2; e2}) -> ({s1; val t1 = e1; s2; val t2 = e2; t1.f op= t2})
* </pre>
*
* @param expr the field assignment to be flattened
* @return a flat expression equivalent to expr
*/
private Expr flattenFieldAssign(FieldAssign expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
if (expr.target() instanceof Expr) {
Expr target = getPrimaryAndStatements((Expr) expr.target(), stmts);
expr = expr.target(target);
}
X10FieldInstance fi = (X10FieldInstance) expr.fieldInstance();
if (!fi.annotationsMatching(typeSystem().Embed()).isEmpty()) {
return expr;
}
Expr right = getPrimaryAndStatements(expr.right(), stmts);
return toFlatExpr(expr.position(), stmts, expr.right(right));
}
/**
* Flatten an assignment to a local variable.
* <pre>
* x op= ({s1; e1}) -> ({s1; val t1 = e1; x op= t1})
* </pre>
*
* @param expr the local assignment to flatten
* @return a flat expression equivalent to expr
*/
private Expr flattenLocalAssign(LocalAssign expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr right = getPrimaryAndStatements(expr.right(), stmts);
return toFlatExpr(expr.position(), stmts, expr.right(right));
}
/**
* Flatten an array element assignment.
* <pre>
* ({s1; e1})(({s2; e2}), ... ({se; tk})) op= ({sk+1; ek+1}) ->
* ({s1; val t1 = e1; ... sk+1; val tk+1 = ek+1; t1(t2, ..., tk) op= tk+1 })
* </pre>
*
* @param expr the array element assignment to flatten
* @return a flat expression equivalent to expr
*/
private Expr flattenSettableAssign(SettableAssign expr) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr array = getPrimaryAndStatements(expr.array(), stmts);
List<Expr> exprs = new ArrayList<Expr>();
for (Expr e : expr.index()) {
Expr index = getPrimaryAndStatements(e, stmts);
exprs.add(index);
}
Expr right = getPrimaryAndStatements(expr.right(), stmts);
return toFlatExpr(expr.position(), stmts, expr.array(array).index(exprs).right(right));
}
/**
* Flatten a conditional expression.
* <pre>
* ({s1; e1}) ? ({s2; e2}) : ({s3; e3} -> ({s1; val t1 = e1; var t; if (t1){s2; t = e2} else {s3; t = e3}; t})
* </pre>
*
* @param expr the conditional expression to flatten
* @return a flat expression equivalent to expr
*/
private Expr flattenConditional(Conditional expr) {
assert (null != expr.alternative());
List<Stmt> stmts = new ArrayList<Stmt>();
Expr primary = getPrimaryAndStatements(expr.cond(), stmts);
LocalDecl tmpLDecl = syn.createLocalDecl(expr.position(), Flags.NONE, syn.createTemporaryName(), expr.type());
stmts.add(tmpLDecl);
List<Stmt> thenStmts = new ArrayList<Stmt>();
thenStmts.addAll(getStatements(expr.consequent()));
thenStmts.add(syn.createAssignment( expr.position(),
syn.createLocal(expr.position(), tmpLDecl),
Assign.ASSIGN,
getResult(expr.consequent()),
this ));
List<Stmt> elseStmts = new ArrayList<Stmt>();
elseStmts.addAll(getStatements(expr.alternative()));
elseStmts.add(syn.createAssignment( expr.position(),
syn.createLocal(expr.position(), tmpLDecl),
Assign.ASSIGN,
getResult(expr.alternative()),
this ));
stmts.add(syn.createIf( expr.position(),
primary,
syn.createBlock(expr.consequent().position(), thenStmts),
syn.createBlock(expr.alternative().position(), elseStmts) ));
return toFlatExpr(expr.position(), stmts, syn.createLocal(expr.position(), tmpLDecl));
}
/**
* Produce a statement in which all sub-expressions have been flattened.
* Transform any embedded statement expressions into statements and expressions.
* (All children of this statement have already been flattened.)
*
* @param stmt the statement to be flattened
* @return a flat statement (possibly a Block) semantically equivalent to stmt
*/
private Stmt flattenStmt(Stmt stmt) {
if (stmt instanceof Eval) return flattenEval((Eval) stmt);
else if (stmt instanceof LocalDecl) return flattenLocalDecl((LocalDecl) stmt);
else if (stmt instanceof If) return flattenIf((If) stmt);
else if (stmt instanceof Return) return flattenReturn((Return) stmt);
else if (stmt instanceof Throw) return flattenThrow((Throw) stmt);
else if (stmt instanceof Offer) return flattenOffer((Offer) stmt);
else if (stmt instanceof Switch) return flattenSwitch((Switch) stmt);
else if (stmt instanceof Assert) return flattenAssert((Assert) stmt);
else if (stmt instanceof Async) return flattenAsync((Async) stmt);
else if (stmt instanceof AtStmt) return flattenAtStmt((AtStmt) stmt);
else if (stmt instanceof When) return flattenWhen((When) stmt);
else if (stmt instanceof AssignPropertyCall) return flattenAssignPropertyCall((AssignPropertyCall) stmt);
else if (stmt instanceof ConstructorCall) return flattenConstructorCall((ConstructorCall) stmt);
else if (stmt instanceof Branch) return inspectBranch((Branch) stmt);
else if (stmt instanceof Finish) return syn.createStmtSeq(stmt);
else if (stmt instanceof Next) return syn.createStmtSeq(stmt);
else if (stmt instanceof Try) {
if (((Try) stmt).tryBlock() instanceof StmtSeq) {
List<Stmt> stmts = ((StmtSeq)((Try) stmt).tryBlock()).statements();
assert (1 == stmts.size());
return ((Try) stmt).tryBlock((Block) stmts.get(0));
}
return stmt;
}
else {
if (DEBUG && !(stmt instanceof Try || stmt instanceof Catch || stmt instanceof Atomic || stmt instanceof Empty))
System.err.println("INFO: ExpressionFlattener.flattenStmt: default class: " +stmt.getClass()+ " (" +stmt+ ") at " +stmt.position() );
return stmt;
}
}
/**
* Flatten a block.
* If this is a labeled block, we stick the labels on it because flattenLabeled
* is about to rip them off our parent.
* (Otherwise, the only thing we flatten is a block nested in a block,
* the only block with a value, StmtExpr, has already been handled.)
* <pre>
* { S; ({s1; e1}) } -> { S; s1; e1 }
* </pre>
*
* @param stmt the block to be flattened
* @return a block with all of its constituent statements flattened.
*/
private Stmt flattenBlock(Block stmt, boolean labeled) {
assert (!(stmt instanceof StmtExpr));
List<Stmt> bodyStmts = stmt.statements();
if ((bodyStmts.size() == 1) && bodyStmts.get(0) instanceof Block) {
Block inner = (Block) bodyStmts.get(0);
boolean outerStmtSeq = stmt instanceof StmtSeq;
boolean innerStmtSeq = inner instanceof StmtSeq;
if ((!outerStmtSeq || innerStmtSeq) && !(stmt instanceof SwitchBlock)
&& ((X10Ext) inner.ext()).annotations().isEmpty()) {
stmt = stmt.statements(inner.statements());
}
}
// we're about to strip off our parents label in flattenLabeled.
// So, if this block is labeled we'd better cram it on here.
return label(stmt, labeled);
}
/**
* Flatten an assertion.
* The semantics of a flat assertion require the backend to be able to handle a StmtExpr. The java back-end cannot.
* For the time being, assertions are not flattened.
* <pre>
* assert ({s1; e1}); -> assert ({s1; e1}); // no-op
* </pre>
*
* @param stmt the assertion to be flattened
* @return a flat stmt with the same semantics as stmt
* TODO: handle StmtExpr's in the Java back end.
*/
private StmtSeq flattenAssert(Assert stmt) {
assert false;
return syn.createStmtSeq(stmt);
}
/**
* Flatten an when statement.
* The semantics of a flat assertion require the backend to be able to handle a StmtExpr. The java back-end cannot.
* For the time being when statements are not flattened.
* <pre>
* when (({s1; e1})) { S1 } ... or (({sk; ek})) { Sk }; -> when (({s1; e1})) { S1 } ... or (({sk; ek})) { Sk }; // no-op
* </pre>
*
* @param stmt the when statement to flatten
* @return a flat statement with the same semantics as stmt
* TODO: handle StmtExpr's in the Java back end.
*/
private StmtSeq flattenWhen(When stmt) {
assert false;
return syn.createStmtSeq(stmt);
}
/**
* Flatten an AtEach statement.
* <pre>
* ateach (x in ({s1; e1})) S -> s1; val t1 = e1; ateach (x in t1) S;
* </pre>
*
* @param stmt the AtEach statement to flatten
* @return a flat statement with the same semantics as stmt
*/
private Block flattenAtEach(AtEach stmt, boolean labeled) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr domain = getPrimaryAndStatements(stmt.domain(), stmts);
stmt = (AtEach) stmt.domain(domain);
stmts.add(label(stmt, labeled));
return syn.createBlock(stmt.position(), stmts);
}
/**
* Flatten an At statement.
* <pre>
* at (x in ({s1; e1})) S -> s1; val t1 = e1; at (x in t1) S;
* </pre>
*
* @param stmt the AtEach statement to flatten
* @return a flat statement with the same semantics as stmt
*/
private StmtSeq flattenAtStmt(AtStmt stmt) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr place = getPrimaryAndStatements(stmt.place(), stmts);
stmts.add((Stmt) stmt.place(place));
return syn.createStmtSeq(stmt.position(), stmts);
}
/**
* Flatten the declaration of a local variable.
* <pre>
* val x = ({s1; e1}) -> s1; x = e1;
* </pre>
*
* @param stmt the local variable declaration to be flattened
* @return a flat statement with the same semantics as stmt
*/
private StmtSeq flattenLocalDecl(LocalDecl stmt) {
List<Stmt> stmts = new ArrayList<Stmt>();
stmts.addAll(getStatements(stmt.init()));
stmts.add(stmt.init(getResult(stmt.init())));
return syn.createStmtSeq(syn.createStmtSeq(stmt.position(), stmts));
}
/**
* Flatten a conditional statement.
* <pre>
* if (({s1; e1})) S1 else S2; -> s1; if (t1) S1 else S2;
* </pre>
*
* @param stmt the conditional statement to be flattened
* @return a flat statement with the same semantics as stmt
*/
private StmtSeq flattenIf(If stmt) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr cond = getPrimaryAndStatements(stmt.cond(), stmts);
stmts.add(stmt.cond(cond));
return syn.createStmtSeq(stmt.position(), stmts);
}
/**
* Flatten a for-loop.
* <pre>
* for (x in ({s1; e1})) S; -> s1; val t1 = e1; for (x in t1) S;
* </pre>
*
* @param stmt the for-loop to be flattened
* @return a flat statement with
*/
private Stmt flattenForLoop(ForLoop stmt, boolean labeled) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr primary = getPrimaryAndStatements(stmt.domain(), stmts);
stmt = (ForLoop) stmt.domain(primary);
stmts.add(label(stmt, labeled));
return toStmt(stmt.position(), stmts);
}
/**
* Flatten a throw statement.
* <pre>
* throw ({s1; e1}); -> s1; val t1 = e1; throw t1;
* </pre>
*
* @param stmt the throw statement to be flattened.
* @return a flat statement with the same semantics as stmt
*/
private StmtSeq flattenThrow(Throw stmt) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr primary = getPrimaryAndStatements(stmt.expr(), stmts);
stmts.add(stmt.expr(primary));
return syn.createStmtSeq(stmt.position(), stmts);
}
/**
* Flatten a switch statement.
* <pre>
* switch (({s1; e1})) { S; } -> s1; val t1 = e1; switch (t1) { S; }
* </pre>
*
* @param stmt the switch statement to be flattened.
* @return a flat statement with the same semantics as stmt
*/
private StmtSeq flattenSwitch(Switch stmt) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr primary = getPrimaryAndStatements(stmt.expr(), stmts);
stmts.add(stmt.expr(primary));
return syn.createStmtSeq(stmt.position(), stmts);
}
/**
* Flatten an Async statement.
* <pre>
* async S; -> async S; // no-op
* async (({s1; e1})) S; -> s1; val t1 = e1; async (t1) S;
* </pre>
*
* @param stmt the Async statement to flatten
* @return a flat statement with the same semantics as stmt
*/
private StmtSeq flattenAsync(Async stmt) {
List<Stmt> stmts = new ArrayList<Stmt>();
stmts.add((Async) stmt);
return syn.createStmtSeq(stmt.position(), stmts);
}
/**
* Flatten a return statements.
* <pre>
* return; -> return; // no-op
* return ({s1; e1}); -> s1; val t1 = e1; return t1;
* </pre>
*
* @param stmt the return statement to be flattened
* @return a flat statement with the same semantics as stmt
*/
private StmtSeq flattenReturn(Return stmt) {
if (null == stmt.expr()) return syn.createStmtSeq(stmt);
List<Stmt> stmts = new ArrayList<Stmt>();
Expr expr = getPrimaryAndStatements(stmt.expr(), stmts);
stmts.add(stmt.expr(expr));
return syn.createStmtSeq(stmt.position(), stmts);
}
/**
* Flatten the evaluation of an expression.
* <pre>
* ({s1; e1}); -> s1; if "e1" cannot have side-effects
* ({s1; e1}); -> s1; Eval(e1); if "e1" might have side-effects and it's type is void
* ({s1; e1)); -> s1; val v = e1; if "e1" might have side-effects and it's type isn't void
* </pre>
*
* @param stmt the evaluation to be flattened.
* @return a flat statement with the same semantics as stmt
*/
private StmtSeq flattenEval(Eval stmt) {
List<Stmt> stmts = new ArrayList<Stmt>();
stmts.addAll(getStatements(stmt.expr()));
Expr result = getResult(stmt.expr());
while (result instanceof ParExpr)
result = ((ParExpr) result).expr();
if (sed.hasSideEffects(result)) {
if (result instanceof ProcedureCall || result instanceof Assign || result instanceof Unary) {
stmts.add(syn.createEval(result));
} else if (!result.type().typeEquals(xts.Void(), context())){
stmts.add(syn.createLocalDecl(result.position(), Flags.FINAL, Name.makeFresh("dummy"), result));
} else {
Warnings.issue(job, "DEBUG: eval : " +result, result.position());
throw new InternalCompilerError("Cannot flatten " +result+ " at " +result.position()+ " (was " +stmt+ ")");
}
}
return syn.createStmtSeq(syn.createStmtSeq(stmt.position(), stmts));
}
// ASK IGOR: how to handle early constructor rules?
static final boolean JAVA_CONSTRUCTOR_RULES = false;
/**
* Flatten a property initializer call.
* (This might entail handling X10 class constructors differently than Java class constructiors.
* For the time being, X10 constructors are not flattened.)
* <pre>
* property(({s1; e1}), ..., ({sk, ek})); -> s1; val t1 = e1; ... sk; val tk = ek; property(t1, ..., tk);
* </pre>
*
* @param stmt the property call to be flattened
* @return a flat statements with the same semantics as stmt
*/
private StmtSeq flattenAssignPropertyCall(AssignPropertyCall stmt) {
if (JAVA_CONSTRUCTOR_RULES) return syn.createStmtSeq(stmt);
List<Stmt> stmts = new ArrayList<Stmt>();
List<Expr> args = new ArrayList<Expr>();
for (Expr arg : stmt.arguments()) {
Expr primary = getPrimaryAndStatements(arg, stmts);
args.add(primary);
}
stmts.add(stmt.arguments(args));
return syn.createStmtSeq(syn.createBlock(stmt.position(), stmts));
}
/**
* Flatten a special method call (e.g. super() or this()) in a constructor.
* (This would entail handling X10 class constructors differently than Java class constructiors.
* For the time being, X10 constructors are not flattened.)
* <pre>
* this (({s1; e1}), ..., ({sk; ek})); -> {s1; val t1 = e1; ... sk; val tk = ek; this(t1, ... tk)}
* super(({s1; e1}), ..., ({sk; ek})); -> {s1; val t1 = e1; ... sk; val tk = ek; super(t1, ... tk)}
* </pre>
*
* @param stmt the special call to be flattened
* @return a flat statement with the same semantics as stmt
*/
private StmtSeq flattenConstructorCall(ConstructorCall stmt) {
if (JAVA_CONSTRUCTOR_RULES) return syn.createStmtSeq(stmt);
List<Stmt> stmts = new ArrayList<Stmt>();
if (null != stmt.qualifier()) {
Expr qualifier = getPrimaryAndStatements(stmt.qualifier(), stmts);
stmt = stmt.qualifier(qualifier);
}
List<Expr> args = new ArrayList<Expr>();
for (Expr arg : stmt.arguments()) {
Expr primary = getPrimaryAndStatements(arg, stmts);
args.add(primary);
}
stmts.add((ConstructorCall) stmt.arguments(args));
return syn.createStmtSeq(syn.createBlock(stmt.position(), stmts));
}
/**
* Flatten a while loop.
* <pre>
* while (true) S; -> while (true) S;
* while (false) S; -> while (false) S;
* while (({s1; e1})) S; -> while (true) { s1; val t1 = e1; if (!t1) break; S;}
* </pre>
*
* @param stmt the while loop to be flattened
* @return a flat statement with the same semantics as stmt
*/
private Stmt flattenWhile(While_c stmt, boolean labeled) {
if ( !(stmt.cond() instanceof BooleanLit) ) {
List<Stmt> stmts = new ArrayList<Stmt>();
Expr primary = getPrimaryAndStatements(stmt.cond(), stmts);
stmts.add(syn.createIf( stmt.cond().position(),
syn.createNot(stmt.cond().position(), primary, this),
syn.createBreak(stmt.cond().position()),
null) );
stmt = (While_c) stmt.cond(syn.createTrue(stmt.cond().position()));
stmts.add(stmt.body());
stmt = (While_c) stmt.body(syn.createBlock(stmt.position(), stmts));
}
return label(stmt, labeled);
}
/**
* Flatten a do loop.
* <pre>
* do { S; } while(true); -> do { S; } while(true);
* do { S; } while(false); -> do { S; } while(false);
* do { S; } while(({s1; e1})); -> {var t = false; do { S; s1; val t1 = e1; t = t1} while(t);}
* </pre>
*
* @param stmt the do loop to be flattened
* @return a flat statement with the same semantics as stmt
*/
private Stmt flattenDo(Do_c stmt, boolean labeled) {
if (null == stmt.cond() || stmt.cond() instanceof BooleanLit)
return label(stmt, labeled);
List<Stmt> stmts = new ArrayList<Stmt>();
Position pos = stmt.position();
LocalDecl tmpLDecl = syn.createLocalDecl( pos,
Flags.NONE,
syn.createTemporaryName(),
xts.Boolean(),
syn.createFalse(pos) );
stmts.add(tmpLDecl);
List<Stmt> bodyStmts = new ArrayList<Stmt>();
bodyStmts.add(stmt.body());
Expr primary = getPrimaryAndStatements(stmt.cond(), bodyStmts);
bodyStmts.add(syn.createAssignment(pos, syn.createLocal(pos, tmpLDecl), Assign.ASSIGN, primary, this));
stmt = (Do_c) stmt.cond(syn.createLocal(pos, tmpLDecl));
stmt = (Do_c) stmt.body(syn.createBlock(pos, bodyStmts));
stmts.add(label(stmt, labeled));
return syn.createBlock(pos, stmts);
}
/**
* Flatten a (traditional) for-loop.
* <pre>
* for (S1; true; S2) S3; -> {S1; for (;true;) {S3; S2;}}
* for (S1; ({s1; e1}); S2) S3; -> {S1; for (;true;) {s1; var t1=e1; if (!t1) break; S3; S2;}}
* </pre>
*
* @param stmt the for-loop to be flattened
* @return a flat statement with the same semantics as stmt
*/
private Stmt flattenFor(For stmt, boolean labeled) {
List<Stmt> stmts = new ArrayList<Stmt>();
Position pos = stmt.position();
stmts.addAll(stmt.inits());
List<Stmt> bodyStmts = new ArrayList<Stmt>();
if ((null!=stmt.cond()) && !((stmt.cond() instanceof BooleanLit) && ((BooleanLit) stmt.cond()).value()) ) {
Expr primary = getPrimaryAndStatements(stmt.cond(), bodyStmts);
bodyStmts.add(syn.createIf( stmt.cond().position(),
syn.createNot(stmt.cond().position(), primary, this),
syn.createBreak(stmt.cond().position()),
null) );
stmt = stmt.cond(syn.createTrue(pos));
}
if (labelInfo.get(labelInfo.size()-1).newLabelUsed) {
bodyStmts.add(syn.createLabeledStmt(stmt.body().position(),
labelInfo.get(labelInfo.size()-1).newLabel,
stmt.body()));
} else {
bodyStmts.add(stmt.body());
}
bodyStmts.add(syn.createBlock( pos, new ArrayList<Stmt>(stmt.iters())));
stmt = stmt.inits(Collections.<ForInit>emptyList());
stmt = stmt.iters(Collections.<ForUpdate>emptyList());
stmt = stmt.body(syn.createBlock(pos, bodyStmts));
stmts.add(label(stmt, labeled));
// Id dummy = xnf.Id(Position.COMPILER_GENERATED, "dummy");
// return syn.toStmtSeq(xnf.Labeled(pos, dummy, syn.toStmtSeq(syn.createBlock(pos, stmts))));
return syn.createBlock(pos, stmts);
}
/**
* Inspect branch statements. Redirect branches targeting For headers.
* <pre>
* continue Label1; -> break new-Label1; // if Label1 is on a For
* continue; -> break new-Label1; // if enclosing loop is a For
* </pre>
*
* @param stmt the branch statement to possibly be redirected
* @return either the original branch or the redirected branch with
* the same semantics as stmt
*/
private StmtSeq inspectBranch(Branch stmt) {
Id branchTarget = stmt.labelNode();
int i = labelInfo.size() - 1;
boolean saveoldLabelUsed = true;
if (branchTarget != null) {
// Find the target in our label stack. It must be there.
for ( ; ; i--) {
Id oldLbl = labelInfo.get(i).oldLabel;
if (oldLbl == null) continue;
if (branchTarget.id().equals(oldLbl.id())) {
saveoldLabelUsed = labelInfo.get(i).oldLabelUsed;
labelInfo.get(i).oldLabelUsed = true;
break;
}
}
}
if (stmt.kind() == Branch.CONTINUE) {
// If the continue did not have a taget and we had labeled blocks,
// we are not yet at the scope we want to target.
for (; !labelInfo.get(i).loop; i--)
;
// A null newLabel => loop was not a "For" - don't redirect
if (labelInfo.get(i).newLabel != null) {
if (DEBUG) {
System.out.println("rewriting a continue from old label " + labelInfo.get(i).oldLabel);
System.out.println("to break with label " + labelInfo.get(i).newLabel);
}
labelInfo.get(i).newLabelUsed = true;
labelInfo.get(i).oldLabelUsed = saveoldLabelUsed;
return syn.createStmtSeq(syn.createBreak(stmt.position(), labelInfo.get(i).newLabel.toString()));
}
}
return syn.createStmtSeq(stmt);
}
/**
* Flatten a remote activity invocation (Future, At, or Async).
*
* @param rai the remote activity invocation to flatten
* @param stmts a list of statements on which flattening assignments may be appended
* @return a flat remote activity invocation equal to rai (after whatever assignments are appended to stmts as a side effect)
*/
private RemoteActivityInvocation flattenRemoteActivityInvocation (RemoteActivityInvocation rai, List<Stmt> stmts) {
if (null == rai.place()) return rai;
Expr primary = getPrimaryAndStatements(rai.place(), stmts);
return rai.place(primary);
}
// Helper methods to interact with flat StmtExprs
//
// Flat StmtExpr invariant:
// The result Expr of a StmtExpr is a flat Expr, amd
// The Stmt's of a StmtExpr contain no non-flat sub-expressions.
//
// An Expr is primary iff it is a literal constant or a final variable instance (or here or this or super).
// Literal constants, named variables, and calls to this(), super(), and here() are primary.
/**
* Extract the preliminary statements from a compound statement expression.
*
* @param expr the expression from which statements are to be extracted
* @return a list of statements taken from expr (this list will be empty unless expr is a compound expression)
*/
private List<Stmt> getStatements(Expr expr) {
if (expr instanceof StmtExpr) return ((StmtExpr) expr).statements();
return new ArrayList<Stmt>();
}
/**
* Extract the result expression from a compound statement expression.
*
* @param expr the exression from which the result is to be extracted
* @return the result expression of expr
*/
private Expr getResult(Expr expr) {
if (expr instanceof StmtExpr) return ((StmtExpr) expr).result();
return expr;
}
/**
* Extract a final variable or literal constant as the result of a statement expression.
* As a side effect, produce a list of statements to be executed before the primary is evaluated.
* (This list will usually include the assignment to the primary if it is a variable.)
*
* @param expr the expression from which the primary and statements are to be extracted.
* return the primary extracted from expr
*/
private Expr getPrimaryAndStatements(Expr expr, List<Stmt> stmts) {
if (expr instanceof StmtExpr) {;
stmts.addAll(getStatements(expr));
expr = getResult(expr);
}
if (!isPrimary(expr)) {
LocalDecl tmpLDecl = syn.createLocalDecl(expr.position(), Flags.FINAL, syn.createTemporaryName(), expr);
stmts.add(tmpLDecl);
expr = syn.createLocal(expr.position(), tmpLDecl);
}
return expr;
}
/**
* A primary is an Expr that does not have any sub-expressions. Is the given Expr primary?
*
* @param expr the given Expr
* @return true if expr has no sub-expressions; otherwise, false
* TODO: it may be expedient to consider other kinds of Expr primary on an interim basis.
*/
static boolean isPrimary(Expr expr) {
if (null == expr) return true; // DEBUG
if (expr instanceof Lit) return true;
if (expr instanceof Local) return ((Local) expr).flags().contains(Flags.FINAL);
if (expr instanceof Special) return true;
if (expr instanceof Here_c) return true;
return false;
}
/**
* Create a flat expression from a possibly empty list of flat statements and a flat expression.
*
* @param pos the Position of the flat expression in source code
* @param stmts the sequence of flat statement to precede evaluation of the flat expression
* @param expr the result expression
* @return expr, if stmts is empty; a compound expression comprising stmts and expr, otherwise
*/
private Expr toFlatExpr(Position pos, List<Stmt> stmts, Expr expr) {
if (stmts.isEmpty()) return expr;
return syn.createStmtExpr(pos, stmts, expr);
}
/**
* @param pos
* @param stmts
* @return
*/
private Stmt toStmt(Position pos, List<Stmt> stmts) {
if (1 == stmts.size() && stmts.get(0) == pos ) return stmts.get(0);
return syn.createBlock(pos, stmts);
}
/**
* Prefix a list of labels to a statement.
*
* @param stmt the statement to be labeled
* @param labeled a flag indicating the parent is Labeled
* @return either the stmt (there are no labels), or a Labeled statement
*/
private Stmt label(Stmt stmt, boolean labeled) {
if (!labeled) return stmt;
if (!labelInfo.get(labelInfo.size()-1).oldLabelUsed) return stmt;
return(syn.createLabeledStmt(stmt.position(),
labelInfo.get(labelInfo.size()-1).oldLabel,
stmt));
}
}