/*
* 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 2010.
*/
package x10.visit;
import java.util.ArrayList;
import java.util.List;
import polyglot.ast.Allocation;
import polyglot.ast.ConstructorCall;
import polyglot.ast.Ext;
import polyglot.ast.FieldDecl;
import polyglot.ast.Local;
import polyglot.ast.LocalDecl;
import polyglot.ast.New;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Special;
import polyglot.ast.Stmt;
import polyglot.frontend.Job;
import polyglot.types.Flags;
import polyglot.types.Name;
import polyglot.types.ObjectType;
import polyglot.types.QName;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.TypeSystem_c;
import polyglot.util.Position;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import x10.ast.AnnotationNode;
import x10.extension.X10Ext;
import x10.types.Annotated;
import x10.types.X10TypeObjectMixin;
import x10.util.AltSynthesizer;
/**
* Split X10 constructor nodes (X10New_c) into
* 1) a primative X10Allocation that the backends will cause memory allocation
* 2) a "method" (X10New_c) that has the X10Allocation as its target.
*
* see http://xj.watson.ibm.com/twiki/bin/view/Main/TheXtenProject/XtenConstructorSplitting
*
* @author Bowen Alpern
*
*/
public class ConstructorSplitterVisitor extends ContextVisitor {
private static final boolean DEBUG = false;
private void debug (Job job, String msg, Position pos) {
if (!DEBUG) return;
System.out.println("DEBUG: " +pos+ " " +msg);
}
private AltSynthesizer syn;
/**
* @param job
* @param ts
* @param nf
*/
public ConstructorSplitterVisitor(Job job, TypeSystem ts, NodeFactory nf) {
super(job, ts, nf);
assert x10.optimizations.Optimizer.CONSTRUCTOR_SPLITTING(job.extensionInfo());
syn = new AltSynthesizer(ts, nf);
}
/* (non-Javadoc)
* @see polyglot.visit.NodeVisitor#override(polyglot.ast.Node)
*
* Note: C++ backend apparently cannot handle StmtExpr's "outside functions"
*/
@Override
public Node override(Node n) {
if (n instanceof FieldDecl) return n;
return super.override(n);
}
/**
* Split X10 constructors (New AST nodes) into allocation and initialization parts.
* Memory allocation is represented by an Allocation AST node. The type of this node
* tells the back-ends how much memory to allocate. Initialization is handled by
* ConstructorCall AST nodes (effectively, a call to this() with arguments). These nodes
* now take a target which resolves to the allocated storage. In the case of Special
* calls (explicit calls to this() or super()), this target is a synthesized "this" AST
* node.
*
* @see polyglot.visit.ErrorHandlingVisitor#leaveCall(polyglot.ast.Node, polyglot.ast.Node, polyglot.ast.Node, polyglot.visit.NodeVisitor)
* @return an AST Node with all New nodes replaced by an Allocation and a ConstructorCall,
* and all ConstructorCall nodes having a target.
*/
@Override
protected Node leaveCall(Node parent, Node old, Node node, NodeVisitor v) throws SemanticException {
Position pos = node.position();
if (node instanceof New && !(parent instanceof LocalDecl)){
New n = (New) node;
if (isUnsplittable(n.constructorInstance().container().toClass()))
return n;
Type type = n.type();
Allocation a = (Allocation)syn.createAllocation(pos, n.objectType(), n.typeArguments()).type(type);
a = (Allocation) copyAnnotations(n, a);
LocalDecl ld = syn.createLocalDecl(pos, Flags.FINAL, Name.makeFresh("alloc"), a);
ConstructorCall cc = syn.createConstructorCall(syn.createLocal(pos, ld), n);
cc = (ConstructorCall) copyAnnotations(n, cc);
List<Stmt> stmts = new ArrayList<Stmt>();
stmts.add(ld);
stmts.add(cc);
Node result = syn.createStmtExpr(pos, stmts, syn.createLocal(pos, ld));
if (DEBUG) debug(job, "ConstructorSplitterVisitor splitting \n\t" +node+ " ~>\n\t" +result, pos);
return result;
}
if (node instanceof LocalDecl && ((LocalDecl) node).init() instanceof New) {
LocalDecl ld = (LocalDecl) node;
New n = (New) ld.init();
if (isUnsplittable(n.constructorInstance().container().toClass()))
return ld;
Type type = n.type();
Allocation a = (Allocation)syn.createAllocation(pos, n.objectType(), n.typeArguments()).type(type);
a = (Allocation) copyAnnotations(n, a);
// We're in a statement context, so we can avoid a stmt expr.
List<Stmt> stmts = new ArrayList<Stmt>();
if (type.typeSystem().typeDeepBaseEquals(ld.declType(), n.type(), context)) {
ld = ld.init(a);
ConstructorCall cc = syn.createConstructorCall(syn.createLocal(pos, ld), n);
cc = (ConstructorCall) copyAnnotations(n, cc);
stmts.add(ld);
stmts.add(cc);
} else {
// Type of the local != type of the new.
// This happens when the type of the local decl is a supertype of the type of the new
// Introduce additional localdecl so that the constructor call can be made
// on a variable of the correct type.
LocalDecl ld2 = syn.createLocalDecl(pos, Flags.FINAL, Name.makeFresh("a"), a);
ConstructorCall cc = syn.createConstructorCall(syn.createLocal(pos, ld2), n);
cc = (ConstructorCall) copyAnnotations(n, cc);
ld = ld.init(syn.createLocal(pos, ld2));
stmts.add(ld2);
stmts.add(cc);
stmts.add(ld);
}
Node result = syn.createStmtSeq(pos, stmts);
if (DEBUG) debug(job, "ConstructorSplitterVisitor splitting \n\t" +node+ " ~>\n\t" +result, pos);
return result;
}
if (node instanceof ConstructorCall) {
ConstructorCall cc = (ConstructorCall) node;
/*
if (cc.kind() == ConstructorCall.SUPER && ts.typeEquals(cc.constructorInstance().returnType(), ts.Object(), context())) {
return nf.Empty(cc.position());
}
*/
if (null == cc.target()) {
Special target = syn.createThis(node.position(), cc.constructorInstance().returnType());
// TODO if "this" is generic make sure it's typeArgs get included in those of cc
return cc.target(target);
}
}
return super.leaveCall(parent, old, node, v);
}
/**
* @param type
* @return
*/
public static boolean isUnsplittable(Type type) {
assert null != type;
TypeSystem ts = type.typeSystem();
assert null != ts;
if (hasNativeAnnotation(type))
return true;
if (type instanceof ObjectType) {
return inheritsUnsplittability(((ObjectType) type).superClass(), ts);
}
return false;
}
/**
* @param type
* @param ts
* @return
*/
public static boolean inheritsUnsplittability(Type type, TypeSystem ts) {
if (null == type)
return false; // some non-Native ObjectClass's (x10.array.RectLayout for one) don't have a superClass ????
if (hasNativeAnnotation(type))
return true; // inheriting from any other native class is not
return inheritsUnsplittability(((ObjectType) type).superClass(), ts);
}
/**
* @param type
* @return
*/
private static boolean hasNativeAnnotation(Type type) {
List<Type> annotations = type.annotations();
if (null == annotations || annotations.isEmpty())
return false;
TypeSystem ts = type.typeSystem();
if (!X10TypeObjectMixin.annotationsMatching(annotations, ts.NativeClass()).isEmpty())
return true;
if (!X10TypeObjectMixin.annotationsMatching(annotations, ts.NativeRep()).isEmpty())
return true;
return false;
}
public static List<AnnotationNode> getAnnotations(Node n) {
Ext ext = n.ext();
if (null == ext || !(ext instanceof X10Ext))
return null;
X10Ext x10ext = (X10Ext) ext;
return x10ext.annotations();
}
public static Node addAnnotations(Node n, List<AnnotationNode> annotations) {
Ext ext = n.ext();
if (null == annotations || annotations.isEmpty() || null == ext || !(ext instanceof X10Ext))
return n;
X10Ext x10ext = (X10Ext) ext;
Node node = x10ext.annotations(annotations);
return node;
}
public static Node copyAnnotations(Node src, Node dst) {
return addAnnotations(dst, getAnnotations(src));
}
}