/*
* 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 x10c.visit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import polyglot.ast.Assign;
import polyglot.ast.Block;
import polyglot.ast.Call;
import polyglot.ast.Eval;
import polyglot.ast.Expr;
import polyglot.ast.FloatLit;
import polyglot.ast.IntLit;
import polyglot.ast.Local;
import polyglot.ast.LocalAssign;
import polyglot.ast.LocalDecl;
import polyglot.ast.New;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Receiver;
import polyglot.ast.Stmt;
import polyglot.frontend.Job;
import polyglot.types.Context;
import polyglot.types.Flags;
import polyglot.types.LocalInstance;
import polyglot.types.MethodDef;
import polyglot.types.Name;
import polyglot.types.QName;
import polyglot.types.Ref;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.types.VarDef;
import polyglot.types.VarInstance;
import polyglot.util.InternalCompilerError;
import polyglot.util.Position;
import polyglot.util.CollectionUtil; import x10.util.CollectionFactory;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import x10.ast.AtExpr;
import x10.ast.AtStmt;
import x10.ast.ClosureCall;
import x10.ast.SettableAssign;
import x10.ast.X10Binary_c;
import x10.ast.X10CanonicalTypeNode;
import x10.ast.X10Unary_c;
import x10.types.EnvironmentCapture;
import x10.types.X10ConstructorInstance;
import x10.types.X10LocalDef;
import x10.types.MethodInstance;
import x10.types.X10ParsedClassType;
import x10.types.checker.Converter;
public class VarsBoxer extends ContextVisitor {
private static final String POSTFIX_BOXED_VAR = "$b";
private static final QName LOCAL_VAR = QName.make("x10.compiler.LocalVar");
private static final Name GET = Name.make("get");
private static final Name SET = Name.make("set");
private final TypeSystem xts;
private final NodeFactory xnf;
private final Map<X10LocalDef,X10LocalDef> defToDef = CollectionFactory.newHashMap();
private X10ParsedClassType localVarType;
public VarsBoxer(Job job, TypeSystem ts, NodeFactory nf) {
super(job, ts, nf);
xts = (TypeSystem) ts;
xnf = (NodeFactory) nf;
}
@Override
public NodeVisitor begin() {
try {
localVarType = (X10ParsedClassType) xts.forName(LOCAL_VAR);
} catch (SemanticException e) {
throw new InternalCompilerError("Something is terribly wrong", e);
}
return super.begin();
}
@Override
protected Node leaveCall(Node parent, Node old, Node n, NodeVisitor v) throws SemanticException {
if (n instanceof AtStmt) {
Context context2 = n.enterScope(context);
final List<LocalDecl> privatizations = new ArrayList<LocalDecl>();
final List<Name> outerLocals = new ArrayList<Name>();
AtStmt atSt = (AtStmt) n;
List<VarInstance<? extends VarDef>> caps = atSt.atDef().capturedEnvironment();
atSt = atSt.body((Stmt) atSt.body().visit(createAtExtraNodeVisitor(context2, privatizations, caps, atSt.atDef())));
atSt = atSt.body((Stmt) atSt.body().visit(createAtOuterVarAccessVisitor(context2, outerLocals, caps)));
if (outerLocals.size() > 0 || privatizations.size() > 0) {
List<Stmt> newBody = new ArrayList<Stmt>();
addPrivatizationDeclToBody(atSt.atDef(), context2, privatizations, outerLocals, newBody);
Stmt body = addBoxValWriteBackCallToBody(context2, atSt, outerLocals);
newBody.add(body);
atSt = atSt.body(xnf.Block(Position.COMPILER_GENERATED, newBody));
List<Stmt> stmts1 = addOuterNodes(context2, privatizations, outerLocals, atSt);
return xnf.Block(Position.COMPILER_GENERATED, stmts1);
}
return n;
}
if (n instanceof LocalDecl) {
LocalDecl ld = (LocalDecl) n;
Expr e = ld.init();
if (e instanceof AtExpr) {
AtExpr atEx = (AtExpr) e;
Context context2 = e.enterScope(context);
final List<LocalDecl> privatizations = new ArrayList<LocalDecl>();
final List<Name> outerLocals = new ArrayList<Name>();
List<VarInstance<? extends VarDef>> caps = atEx.closureDef().capturedEnvironment();
atEx = (AtExpr) atEx.body((Block) atEx.body().visit(createAtExtraNodeVisitor(context2, privatizations, caps, atEx.closureDef())));
atEx = (AtExpr) atEx.body((Block) atEx.body().visit(createAtOuterVarAccessVisitor(context2, outerLocals, caps)));
if (outerLocals.size() > 0 || privatizations.size() > 0) {
List<Stmt> newBody = new ArrayList<Stmt>();
addPrivatizationDeclToBody(atEx.closureDef(), context2, privatizations, outerLocals, newBody);
Stmt body = addBoxValWriteBackCallToBody(context2, atEx, outerLocals);
newBody.add(body);
atEx = (AtExpr) atEx.body(xnf.Block(Position.COMPILER_GENERATED, newBody));
Stmt at = ld.init(atEx);
List<Stmt> stmts1 = addOuterNodes(context2, privatizations, outerLocals, at);
return xnf.StmtSeq(Position.COMPILER_GENERATED, stmts1);
}
return n;
}
}
return n;
}
// add write back call to box val in the body
// TODO optimize write back once when returning the body
private Stmt addBoxValWriteBackCallToBody(Context context2, AtStmt atSt, List<Name> outerLocals) {
Stmt body = (Stmt) atSt.body().visit(createAddWriteBackCallVisitor(context2, outerLocals));
return body;
}
// add write back call to box val in the body
// TODO optimize write back once when returning the body
private Stmt addBoxValWriteBackCallToBody(Context context2, AtExpr atEx, List<Name> outerLocals) {
Stmt body = (Stmt) atEx.body().visit(createAddWriteBackCallVisitor(context2, outerLocals));
return body;
}
private ContextVisitor createAddWriteBackCallVisitor(Context context2, final List<Name> outerLocals) {
return new ContextVisitor(job, ts, nf) {
public Node override(Node parent, Node n) {
if (n instanceof AtStmt || n instanceof AtExpr) {
return n;
}
return null;
};
public Node leaveCall(Node parent, Node old, Node n, NodeVisitor v) throws SemanticException {
if (n instanceof LocalAssign) {
LocalAssign la = (LocalAssign) n;
Local local = la.local();
if (outerLocals.contains(local.name().id())) {
LocalInstance libox = getBoxLocalDef(Position.COMPILER_GENERATED, local.localInstance()).asInstance();
return createSetCall(Position.COMPILER_GENERATED, local.localInstance(), libox, la);
}
}
if (n instanceof X10Unary_c) {
X10Unary_c unary = (X10Unary_c) n;
Expr expr = unary.expr();
if (expr instanceof Local) {
Local local = (Local) expr;
if (unary.operator() == X10Unary_c.PRE_DEC || unary.operator() == X10Unary_c.PRE_INC) {
if (outerLocals.contains(local.name().id())) {
LocalInstance libox = getBoxLocalDef(Position.COMPILER_GENERATED, local.localInstance()).asInstance();
return createSetCall(Position.COMPILER_GENERATED, local.localInstance(), libox, unary);
}
}
else if (unary.operator() == X10Unary_c.POST_INC) {
if (outerLocals.contains(local.name().id())) {
LocalInstance libox = getBoxLocalDef(Position.COMPILER_GENERATED, local.localInstance()).asInstance();
Expr one = getLiteral(Position.COMPILER_GENERATED, local.type(), 1);
return xnf.Binary(Position.COMPILER_GENERATED, createSetCall(Position.COMPILER_GENERATED, local.localInstance(), libox, xnf.Binary(Position.COMPILER_GENERATED, unary, X10Binary_c.ADD, one).type(local.type())), X10Binary_c.SUB, one).type(local.type());
}
}
else if (unary.operator() == X10Unary_c.POST_DEC) {
if (outerLocals.contains(local.name().id())) {
LocalInstance libox = getBoxLocalDef(Position.COMPILER_GENERATED, local.localInstance()).asInstance();
Expr one = getLiteral(Position.COMPILER_GENERATED, local.type(), 1);
return xnf.Binary(Position.COMPILER_GENERATED, createSetCall(Position.COMPILER_GENERATED, local.localInstance(), libox, xnf.Binary(Position.COMPILER_GENERATED, unary, X10Binary_c.SUB, one).type(local.type())), X10Binary_c.ADD, one).type(local.type());
}
}
return n;
}
}
return n;
};
}.context(context2);
}
// copied from Desugarer
private Expr getLiteral(Position pos, Type type, long val) throws SemanticException {
type = Types.baseType(type);
Expr lit = null;
if (xts.isIntOrLess(type)) {
lit = xnf.IntLit(pos, IntLit.INT, val);
} else if (xts.isLong(type)) {
lit = xnf.IntLit(pos, IntLit.LONG, val);
} else if (xts.isUInt(type)) {
lit = xnf.IntLit(pos, IntLit.UINT, val);
} else if (xts.isULong(type)) {
lit = xnf.IntLit(pos, IntLit.ULONG, val);
} else if (xts.isFloat(type)) {
lit = xnf.FloatLit(pos, FloatLit.FLOAT, val);
} else if (xts.isDouble(type)) {
lit = xnf.FloatLit(pos, FloatLit.DOUBLE, val);
} else if (xts.isChar(type)) {
// Don't want to cast
return (Expr) xnf.IntLit(pos, IntLit.INT, val).typeCheck(this);
} else
throw new InternalCompilerError(pos, "Unknown literal type: "+type);
lit = (Expr) lit.typeCheck(this);
if (!xts.isSubtype(lit.type(), type)) {
lit = xnf.X10Cast(pos, xnf.CanonicalTypeNode(pos, type), lit,
Converter.ConversionType.PRIMITIVE).type(type);
}
return lit;
}
// add decls for privatization
private void addPrivatizationDeclToBody(EnvironmentCapture ec, Context context2, final List<LocalDecl> privatizations,
final List<Name> outerLocals, List<Stmt> stmts) throws SemanticException {
for (Name name : outerLocals) {
LocalInstance li = context2.findLocal(name);
stmts.add(createDeclForPrivatization(Position.COMPILER_GENERATED, ec, li));
}
}
private List<Stmt> addOuterNodes(Context context2, final List<LocalDecl> privatizations, final List<Name> outerLocals,
Stmt st) throws SemanticException {
List<Stmt> stmts1 = new ArrayList<Stmt>();
stmts1.add(st);
for (LocalDecl ldecl : privatizations) {
stmts1.add(0, ldecl);
LocalInstance li = context2.findLocal(Name.make(ldecl.name().toString().replace(POSTFIX_BOXED_VAR, "")));
stmts1.add(xnf.Eval(Position.COMPILER_GENERATED, createWriteBack(Position.COMPILER_GENERATED, li, ldecl)));
}
for1:for (Name name : outerLocals) {
for (LocalDecl ldecl : privatizations) {
if (ldecl.name().toString().replace(POSTFIX_BOXED_VAR, "").equals(name.toString())) {
continue for1;
}
}
LocalInstance li = context2.findLocal(name);
// create decl for box
LocalDecl ldecl = createBoxDecl(Position.COMPILER_GENERATED, name, li);
stmts1.add(0, ldecl);
// create write back node
stmts1.add(xnf.Eval(Position.COMPILER_GENERATED, createWriteBack(Position.COMPILER_GENERATED, li, ldecl)));
}
return stmts1;
}
private ContextVisitor createAtExtraNodeVisitor(Context context2, final List<LocalDecl> privatizations,
final List<VarInstance<? extends VarDef>> caps, final EnvironmentCapture ec) {
return new ContextVisitor(job, ts, nf) {
public Node override(Node parent, Node n) {
if (n instanceof AtStmt || n instanceof AtExpr) {
return n;
}
return null;
};
public Node leaveCall(Node parent, Node old, Node n, NodeVisitor v) throws SemanticException {
// check privatization node and remove
if (n instanceof LocalDecl) {
LocalDecl ldecl = (LocalDecl) n;
LocalInstance li;
try {
li = context.findLocal(Name.make(ldecl.name().toString().replace(POSTFIX_BOXED_VAR, "")));
} catch (SemanticException e) {
return n;
}
if (containsCapturedEnv(caps, li)) {
replaceCapturedEnv(Position.COMPILER_GENERATED, ec, li);
privatizations.add(ldecl);
return null;
}
}
// check write back node and remove
if (n instanceof Eval) {
Expr e = ((Eval) n).expr();
if ((e instanceof LocalAssign) && (((LocalAssign) e).right() instanceof Call) ) {
Call call = (Call) ((LocalAssign) e).right();
Receiver receiver = call.target();
if (receiver instanceof Local) {
if (((Local) receiver).name().id().toString().endsWith(POSTFIX_BOXED_VAR)) {
if (containsCapturedEnv(caps, ((LocalAssign) e).local().localInstance())) {
return null;
}
}
}
}
}
return n;
}
}.context(context2);
}
private ContextVisitor createAtOuterVarAccessVisitor(Context context2, final List<Name> outerLocals,
final List<VarInstance<? extends VarDef>> caps) {
return new ContextVisitor(job, ts, nf) {
public Node override(Node parent, Node n) {
if (n instanceof AtStmt || n instanceof AtExpr) {
return n;
}
return null;
};
public Node leaveCall(Node parent, Node old, Node n, NodeVisitor v) throws SemanticException {
if (n instanceof Local) {
Local local = (Local) n;
Name name = local.name().id();
if (parent instanceof LocalAssign) {
if (((LocalAssign) parent).right() instanceof Call) {
Call call = (Call) ((LocalAssign) parent).right();
Receiver receiver = call.target();
if (receiver instanceof Local) {
if (((Local) receiver).name().id().toString().endsWith(POSTFIX_BOXED_VAR)) {
if (containsCapturedEnv(caps, ((LocalAssign) parent).local().localInstance())) {
return n;
}
}
}
}
}
if (containsCapturedEnv(caps, local.localInstance()) && !local.flags().isFinal() && !outerLocals.contains(name) && !name.toString().endsWith(POSTFIX_BOXED_VAR)) {
outerLocals.add(name);
}
}
return n;
}
}.context(context2);
}
private static boolean containsCapturedEnv(final List<VarInstance<? extends VarDef>> caps, LocalInstance li) {
for (VarInstance<? extends VarDef> vi : caps) {
if (vi.def().equals(li.def())) return true;
}
return false;
}
private LocalDecl createBoxDecl(final Position pos, Name name, LocalInstance li) {
X10ParsedClassType lvt = createLocalVarType(li);
X10CanonicalTypeNode tn = xnf.X10CanonicalTypeNode(pos, lvt);
LocalDecl ldecl = xnf.LocalDecl(pos, xnf.FlagsNode(pos, Flags.FINAL), tn, xnf.Id(pos, name.toString() + POSTFIX_BOXED_VAR));
X10LocalDef localDef = getBoxLocalDef(pos, li);
ldecl = ldecl.localDef(localDef);
Local local = (Local) xnf.Local(pos, xnf.Id(pos, name)).localInstance(li).type(li.type());
List<Expr> args = new ArrayList<Expr>();
args.add(local);
X10ConstructorInstance ci;
try {
ci = xts.findConstructor(lvt, xts.ConstructorMatcher(lvt, lvt.typeArguments(), context));
} catch (SemanticException e) {
throw new InternalCompilerError(""); // TODO
}
ci = (X10ConstructorInstance) ci.container(lvt);
New new1 = (New) xnf.New(pos, tn, args).constructorInstance(ci).type(lvt);
ldecl = ldecl.init(new1);
return ldecl;
}
private X10LocalDef getBoxLocalDef(final Position pos, LocalInstance li) {
X10LocalDef def = (X10LocalDef) li.def();
if (defToDef.containsKey(def)) {
return defToDef.get(def);
}
else {
X10ParsedClassType lvt = createLocalVarType(li);
X10LocalDef localDef = xts.localDef(pos, li.flags(), Types.ref(lvt), Name.make(li.name().toString() + POSTFIX_BOXED_VAR));
defToDef.put(def, localDef);
return localDef;
}
}
private LocalDecl createDeclForPrivatization(final Position pos, EnvironmentCapture ec, LocalInstance li) {
LocalDecl ldecl = xnf.LocalDecl(pos, xnf.FlagsNode(pos, li.flags()), xnf.X10CanonicalTypeNode(pos, li.type()), xnf.Id(pos, li.name()));
ldecl = ldecl.localDef(li.def());
X10LocalDef boxld = getBoxLocalDef(pos, li);
LocalInstance boxli = boxld.asInstance();
replaceCapturedEnv(pos, ec, li);
Call call = createGetCall(pos, li, boxli);
ldecl = ldecl.init(call);
return ldecl;
}
private void replaceCapturedEnv(final Position pos, EnvironmentCapture ec, LocalInstance li) {
List<VarInstance<? extends VarDef>> newEnv = new ArrayList<VarInstance<? extends VarDef>>();
for (VarInstance<? extends VarDef> vi : ec.capturedEnvironment()) {
if (vi.equals(li)) {
newEnv.add(getBoxLocalDef(pos, li).asInstance());
} else {
newEnv.add(vi);
}
}
ec.setCapturedEnvironment(newEnv);
}
private Call createGetCall(final Position pos, LocalInstance lilocal, LocalInstance libox) {
Name mname = GET;
X10ParsedClassType lvt = createLocalVarType(lilocal);
MethodDef md = xts.methodDef(pos, pos, Types.ref(lvt), Flags.FINAL, Types.ref(localVarType.x10Def().typeParameters().get(0)), mname, Collections.<Ref<? extends Type>>emptyList(), Collections.<Ref<? extends Type>>emptyList());
MethodInstance mi = md.asInstance();
Local local = (Local) xnf.Local(pos, xnf.Id(pos, lilocal.name().toString() + POSTFIX_BOXED_VAR)).type(libox.type());
return (Call) xnf.Call(pos, local.localInstance(libox), xnf.Id(pos, mname)).methodInstance(mi).type(lilocal.type());
}
private Call createSetCall(final Position pos, LocalInstance lilocal, LocalInstance libox, Expr arg) {
Name mname = SET;
List<Ref<? extends Type>> argTypes = new ArrayList<Ref<? extends Type>>();
argTypes.add(Types.ref(localVarType.x10Def().typeParameters().get(0)));
X10ParsedClassType lvt = createLocalVarType(lilocal);
MethodDef md = xts.methodDef(pos, pos, Types.ref(lvt), Flags.FINAL, Types.ref(localVarType.x10Def().typeParameters().get(0)), mname, argTypes, Collections.<Ref<? extends Type>>emptyList());
MethodInstance mi = md.asInstance();
Local local = (Local) xnf.Local(pos, xnf.Id(pos, lilocal.name().toString() + POSTFIX_BOXED_VAR)).localInstance(libox).type(libox.type());
return (Call) xnf.Call(pos, local, xnf.Id(pos, mname), arg).methodInstance(mi).type(lilocal.type());
}
private Call createApplyCall(final Position pos, LocalInstance lilocal, LocalInstance libox) {
Name mname = ClosureCall.APPLY;
X10ParsedClassType lvt = createLocalVarType(lilocal);
MethodDef md = xts.methodDef(pos, pos, Types.ref(lvt), Flags.FINAL, Types.ref(localVarType.x10Def().typeParameters().get(0)), mname, Collections.<Ref<? extends Type>>emptyList(), Collections.<Ref<? extends Type>>emptyList());
MethodInstance mi = md.asInstance();
Local local = (Local) xnf.Local(pos, xnf.Id(pos, lilocal.name().toString() + POSTFIX_BOXED_VAR)).type(libox.type());
return (Call) xnf.Call(pos, local.localInstance(libox), xnf.Id(pos, mname)).methodInstance(mi).type(lilocal.type());
}
private LocalAssign createWriteBack(final Position cg, LocalInstance li, LocalDecl ldecl) {
LocalAssign la = (LocalAssign) xnf.LocalAssign(cg, (Local) xnf.Local(cg, xnf.Id(cg, li.name())).localInstance(li).type(li.type()), Assign.ASSIGN, createApplyCall(cg, li, ldecl.localDef().asInstance())).type(li.type());
return la;
}
private X10ParsedClassType createLocalVarType(LocalInstance li) {
List<Type> typeArgs = new ArrayList<Type>();
typeArgs.add(li.type());
return localVarType.typeArguments(typeArgs);
}
}