/*
* 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 x10cpp.visit;
import polyglot.ast.Allocation_c;
import polyglot.ast.Block_c;
import polyglot.ast.BooleanLit;
import polyglot.ast.Branch;
import polyglot.ast.Conditional;
import polyglot.ast.Empty_c;
import polyglot.ast.Eval_c;
import polyglot.ast.FlagsNode_c;
import polyglot.ast.FloatLit;
import polyglot.ast.Id_c;
import polyglot.ast.Labeled_c;
import polyglot.ast.LocalAssign_c;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.NullLit;
import polyglot.ast.NumLit;
import polyglot.ast.Return;
import polyglot.frontend.Job;
import polyglot.types.SemanticException;
import polyglot.types.ContainerType;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import x10.ast.AnnotationNode_c;
import x10.ast.AssignPropertyCall;
import x10.ast.AssignPropertyCall_c;
import x10.ast.ParExpr_c;
import x10.ast.StmtExpr_c;
import x10.ast.X10Binary_c;
import x10.ast.X10Call_c;
import x10.ast.X10CanonicalTypeNode;
import x10.ast.X10CanonicalTypeNode_c;
import x10.ast.X10ConstructorCall_c;
import x10.ast.X10ConstructorDecl_c;
import x10.ast.X10FieldAssign_c;
import x10.ast.X10Field_c;
import x10.ast.X10Formal_c;
import x10.ast.X10If_c;
import x10.ast.X10LocalDecl_c;
import x10.ast.X10Local_c;
import x10.ast.X10MethodDecl_c;
import x10.ast.X10New_c;
import x10.ast.X10Special_c;
import x10.types.X10ClassType;
import x10.types.MethodInstance;
import polyglot.types.TypeSystem;
import x10.util.Synthesizer;
public class StructMethodAnalyzer extends ContextVisitor {
private final TypeSystem xts;
private final X10ClassType myContainer;
// GACK: This is ridiculous. canGoInHeaderStream should be a simple boolean field,
// but polyglot insists on doing copying/cloning under the covers
// and the update to a simple instance field within leaveCall get lost.
boolean[] canGoInHeaderStream = new boolean[] { true };
public StructMethodAnalyzer(Job job, TypeSystem ts, NodeFactory nf, X10ClassType container) {
super(job, ts, nf);
myContainer = container;
xts = (TypeSystem) ts;
}
public boolean canGoInHeaderStream() { return canGoInHeaderStream[0]; }
private boolean isBuiltInNumeric(Type t) {
return xts.typeBaseEquals(xts.Boolean(), t, context) ||
xts.typeBaseEquals(xts.Byte(), t, context) ||
xts.typeBaseEquals(xts.UByte(), t, context) ||
xts.typeBaseEquals(xts.Char(), t, context) ||
xts.typeBaseEquals(xts.Short(), t, context) ||
xts.typeBaseEquals(xts.UShort(), t, context) ||
xts.typeBaseEquals(xts.Int(), t, context) ||
xts.typeBaseEquals(xts.UInt(), t, context) ||
xts.typeBaseEquals(xts.Float(), t, context) ||
xts.typeBaseEquals(xts.Long(), t, context) ||
xts.typeBaseEquals(xts.ULong(), t, context) ||
xts.typeBaseEquals(xts.Double(), t, context);
}
public Node leaveCall(Node old, Node n, NodeVisitor v) throws SemanticException {
// Boilerplate to get to the body of the constructor/method
if (n instanceof X10ConstructorDecl_c || n instanceof X10MethodDecl_c ||
n instanceof Block_c || n instanceof Eval_c) {
return n;
}
// Wrapper expressions/statements that by themselves are not a problem
if (n instanceof Conditional || n instanceof Branch || n instanceof Return ||
n instanceof X10If_c || n instanceof X10Binary_c || n instanceof ParExpr_c ||
n instanceof StmtExpr_c || n instanceof Labeled_c ) {
return n;
}
// Trivial nodes that will never by themselves prevent us from putting the body in the struct method class.
if (n instanceof FlagsNode_c || n instanceof Id_c ||
n instanceof X10Local_c || n instanceof LocalAssign_c || n instanceof AnnotationNode_c ||
n instanceof X10CanonicalTypeNode_c || n instanceof X10Special_c || n instanceof Empty_c) {
return n;
}
if (n instanceof X10Formal_c) {
Type decType = ((X10Formal_c)n).type().type();
if (!(xts.typeBaseEquals(decType, myContainer, context) || isBuiltInNumeric(decType))) {
canGoInHeaderStream[0] = false;
}
return n;
}
// Literals of built-in struct types and of null are fine.
if (n instanceof BooleanLit || n instanceof FloatLit || n instanceof NullLit || n instanceof NumLit) {
return n;
}
// Constructor call.
// We can allow calls to the same class as we are analyzing.
// Any other constructor call will require us to set canBeInlined to false.
if (n instanceof X10ConstructorCall_c) {
ContainerType container = ((X10ConstructorCall_c)n).constructorInstance().container();
if (!(xts.typeBaseEquals(container, myContainer, context))) {
canGoInHeaderStream[0] = false;
}
return n;
}
// Can allow X10New_c of our container type, but nothing else
if (n instanceof X10New_c) {
X10New_c ne = (X10New_c)n;
if (!xts.typeBaseEquals(ne.objectType().type(), myContainer, context)) {
canGoInHeaderStream[0] = false;
}
return n;
}
// Can allow Allocation_c of our container type, but nothing else
if (n instanceof Allocation_c) {
Allocation_c ne = (Allocation_c)n;
if (!xts.typeBaseEquals(ne.type(), myContainer, context)) {
canGoInHeaderStream[0] = false;
}
return n;
}
// Only allow field assignments to fields of the current container; since those decls must be available.
if (n instanceof X10FieldAssign_c) {
X10FieldAssign_c fa = (X10FieldAssign_c)n;
if (!xts.typeBaseEquals(fa.fieldInstance().container(), myContainer, context)) {
canGoInHeaderStream[0] = false;
}
return n;
}
// Another way to get a field assign to a field of the current container.
if (n instanceof AssignPropertyCall) {
return n;
}
// Only allow LocalDecls of the type of the current container or a built-in primitive
if (n instanceof X10LocalDecl_c) {
Type decType = ((X10LocalDecl_c)n).type().type();
if (!(xts.typeBaseEquals(decType, myContainer, context) || isBuiltInNumeric(decType))) {
canGoInHeaderStream[0] = false;
}
return n;
}
// Only allow field reads of fields of the current container; those decls must be available.
if (n instanceof X10Field_c) {
X10Field_c fr = (X10Field_c)n;
if (!xts.typeBaseEquals(fr.fieldInstance().container(), myContainer, context)) {
canGoInHeaderStream[0] = false;
}
return n;
}
// Only allow calls to methods of the current container and to methods of built-in numeric types.
if (n instanceof X10Call_c) {
ContainerType methodType = ((X10Call_c)n).methodInstance().container();
if (!(xts.typeBaseEquals(methodType, myContainer, context) || isBuiltInNumeric(methodType))) {
canGoInHeaderStream[0] = false;
}
return n;
}
// Discovering any other AST in the body means that we don't want to put the method in the header file.
canGoInHeaderStream[0] = false;
return n;
}
}