/*
* 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.util;
import java.util.*;
import polyglot.ast.*;
import polyglot.types.Flags;
import polyglot.types.LazyRef;
import polyglot.types.LocalDef;
import polyglot.types.Name;
import polyglot.types.QName;
import polyglot.types.Ref;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.util.Position;
import polyglot.util.CollectionUtil; import x10.util.CollectionFactory;
import polyglot.visit.TypeBuilder;
import x10.ast.*;
import x10.constraint.XVar;
import x10.types.constraints.ConstraintManager;
import x10.types.*;
import x10.extension.X10Ext;
import x10cpp.visit.SharedVarsMethods;
public class Struct {
private final static java.util.Set<String> ignoreTypes = CollectionFactory.newHashSet();
// these are the structs that are @NativeRep to java
static {
ignoreTypes.add("Boolean");
ignoreTypes.add("Byte");
ignoreTypes.add("UByte");
ignoreTypes.add("Char");
ignoreTypes.add("Short");
ignoreTypes.add("UShort");
ignoreTypes.add("Int");
ignoreTypes.add("UInt");
ignoreTypes.add("Long");
ignoreTypes.add("ULong");
ignoreTypes.add("Float");
ignoreTypes.add("Double");
}
public static X10ClassDecl_c addStructMethods(TypeBuilder tb, X10ClassDecl_c n) {
final TypeSystem xts = tb.typeSystem();
final X10ClassDef cd = (X10ClassDef) n.classDef();
X10ParsedClassType ct = (X10ParsedClassType) cd.asType();
QName fullName = cd.fullName();
String strName = fullName.name().toString();
final QName qualifier = fullName.qualifier();
final ArrayList<Ref<? extends Type>> interfacesList = new ArrayList<Ref<? extends Type>>(cd.interfaces());
interfacesList.add(xts.lazyAny());
cd.setInterfaces(interfacesList);
final Position pos = Position.compilerGenerated(n.body().position());
final LazyRef<X10ParsedClassType> PLACE = Types.lazyRef(null);
PLACE.setResolver(new Runnable() {
public void run() {
PLACE.update((X10ParsedClassType) xts.Place());
}
});
final LazyRef<X10ParsedClassType> STRING = Types.lazyRef(null);
STRING.setResolver(new Runnable() {
public void run() {
STRING.update((X10ParsedClassType) xts.String());
}
});
final LazyRef<X10ParsedClassType> BOOLEAN = Types.lazyRef(null);
BOOLEAN.setResolver(new Runnable() {
public void run() {
BOOLEAN.update((X10ParsedClassType) xts.Boolean());
}
});
// Now I add the auto-generated methods (equals(Any), equals(SomeStruct), hashCode(), toString())
// if the programmer didn't already defined them
// primitive classes do not define hashCode, and we should not auto-create hashCode for them,
// or the java backend will translate:
// var x:Int; x.hashCode
// to
// x.hashCode()
// and it should translate it to:
// ((Object)x).hashCode()
boolean isPrim = (qualifier!=null && qualifier.toString().equals("x10.lang") && ignoreTypes.contains(strName));
boolean seenToString = isPrim;
boolean seenTypeName = false; // some unsigned types are still boxed, so do a honest check
boolean seenHashCode = isPrim;
boolean seenEqualsAny = isPrim;
boolean seenEqualsSelf = isPrim;
for (ClassMember member : n.body().members())
if (member instanceof MethodDecl_c) {
MethodDecl_c mdecl = (MethodDecl_c) member;
final Flags methodFlags = mdecl.flags().flags();
if (methodFlags.isStatic() || methodFlags.isAbstract()) continue;
// The compiler provides implementations of equals, hashCode, and toString if
// there are no user-defined implementations. So, we need to search the struct's members
// and determine which methods to auto-generate and which ones are user-provided.
if (mdecl.name().id().toString().equals("toString") &&
mdecl.formals().isEmpty()) {
seenToString = true;
}
if (mdecl.name().id().toString().equals("typeName") &&
mdecl.formals().isEmpty()) {
seenTypeName = true;
}
if (mdecl.name().id().toString().equals("hashCode") &&
mdecl.formals().isEmpty()) {
seenHashCode = true;
}
// [DC] this code heavily broken... unsure how to fix it at this point
if (mdecl.name().id().toString().equals("equals") && mdecl.formals().size() == 1) {
seenEqualsAny = true; // XTENLANG-2441: Needs to be a test to see if the type of formal is Any.
seenEqualsSelf = true; // XTENLANG-2441: Needs to be a test to see if the type of the formal is the Struct ct
}
}
// I use the AST instead of the type, because the type hasn't been built yet (so ct.fields() is empty!)
// I modify the AST before type-checking, because I want to connect calls to "equals"
// with the correct "equals(SomeStruct)" or "equals(Any)"
// This is important for C++ efficiency (to prevent auto-boxing of structs in equality checking)
ArrayList<FieldDecl> fields = new ArrayList<FieldDecl>();
fields.addAll(n.properties());
for (ClassMember member : n.body().members())
if (member instanceof FieldDecl_c) {
FieldDecl_c field = (FieldDecl_c) member;
if (field.flags().flags().isStatic()) continue;
fields.add(field);
}
final Flags flags = Flags.PUBLIC.Final();
final NodeFactory nf = tb.nodeFactory();
final TypeSystem ts = tb.typeSystem();
final TypeNode intTypeNode = nf.CanonicalTypeNode(pos, ts.Int());
final TypeNode boolTypeNode = nf.CanonicalTypeNode(pos, ts.Boolean());
final TypeNode placeTypeNode = nf.CanonicalTypeNode(pos, ts.Place());
final TypeNode stringTypeNode = nf.CanonicalTypeNode(pos, ts.String());
final TypeNode anyTypeNode = nf.CanonicalTypeNode(pos, ts.Any());
final List<TypeParamNode> typeParamNodeList = n.typeParameters();
List<TypeNode> params = new ArrayList<TypeNode>();
for (TypeParamNode p : typeParamNodeList)
params.add(nf.CanonicalTypeNode(pos, p.type()));
final TypeNode structTypeNode = typeParamNodeList.isEmpty() ? nf.CanonicalTypeNode(pos, cd.asType()) :
nf.AmbDepTypeNode(pos, null,
nf.Id(pos, fullName.name()), params, Collections.<Expr>emptyList(), null);
ArrayList<Stmt> bodyStmts;
Expr expr;
Block block;
String methodName;
X10MethodDecl md;
/*
// final public global safe def typeName():String { return "FULL_NAME"; }
bodyStmts = new ArrayList<Stmt>();
expr = nf.StringLit(pos, fullName.toString());
bodyStmts.add(nf.Return(pos, expr));
block = nf.Block(pos).statements(bodyStmts);
methodName = "typeName";
md = nf.MethodDecl(pos,nf.FlagsNode(pos,flags),stringTypeNode,nf.Id(pos,Name.make(methodName)),Collections.EMPTY_LIST,Collections.EMPTY_LIST,block);
n = (X10ClassDecl_c) n.body(n.body().addMember(md));
*/
if (!seenTypeName) {
Flags nativeFlags = Flags.PUBLIC.Native().Final();
ArrayList<AnnotationNode> natives;
Formal formal;
// In the Java backend, some structs (like Int) are mapped to primitives (like int)
// So I must add a native annotation on this method.
//@Native("java", "x10.rtt.Types.typeName(#this)")
//@Native("c++", "x10aux::type_name(#0)")
//global safe def typeName():String;
natives = createNative(nf, pos, "x10.rtt.Types.typeName(#this)", "x10aux::type_name(#0)");
AnnotationNode nonEscaping = nf.AnnotationNode(pos, nf.AmbMacroTypeNode(pos, nf.PrefixFromQualifiedName(pos,QName.make("x10.compiler")), nf.Id(pos, "NonEscaping"), Collections.<TypeNode>emptyList(), Collections.<Expr>emptyList()));
natives.add(nonEscaping);
methodName = "typeName";
md = nf.MethodDecl(pos,nf.FlagsNode(pos,nativeFlags),stringTypeNode,nf.Id(pos,Name.make(methodName)),Collections.<Formal>emptyList(),null);
md = (X10MethodDecl) ((X10Ext) md.ext()).annotations(natives);
n = (X10ClassDecl_c) n.body(n.body().addMember(md));
}
if (!seenToString) {
// final public global safe def toString():String {
// return "struct NAME:"+" FIELD1="+FIELD1+...;
// or
// return "struct NAME"; // if there are no fields
// }
bodyStmts = new ArrayList<Stmt>();
expr = nf.StringLit(pos, "struct " + fullName + (fields.size()==0?"":":"));
for (FieldDecl fi : fields) {
String name = fi.name().toString();
expr = nf.Binary(pos,expr, Binary.ADD,nf.StringLit(pos," "+name+"="));
expr = nf.Binary(pos,expr, Binary.ADD,nf.Field(pos,nf.This(pos),nf.Id(pos,name)));
}
bodyStmts.add(nf.Return(pos, expr));
block = nf.Block(pos).statements(bodyStmts);
methodName = "toString";
md = nf.MethodDecl(pos,nf.FlagsNode(pos,flags),stringTypeNode,nf.Id(pos,Name.make(methodName)),Collections.<Formal>emptyList(),block);
n = (X10ClassDecl_c) n.body(n.body().addMember(md));
}
if (!seenHashCode) {
// final public global safe def hashCode():Int {
// var result:Int = 1;
// result = 8191*result + FIELD1.hashCode();
// ...
// return result;
// }
bodyStmts = new ArrayList<Stmt>();
bodyStmts.add(nf.LocalDecl(pos, nf.FlagsNode(pos,Flags.NONE), intTypeNode,nf.Id(pos,"result"),nf.IntLit(pos, IntLit.INT,1)));
final Local target = nf.Local(pos, nf.Id(pos, "result"));
for (FieldDecl fi : fields) {
String name = fi.name().toString();
bodyStmts.add(nf.Eval(pos,nf.Assign(pos, target, Assign.ASSIGN,
nf.Binary(pos,
nf.Binary(pos,nf.IntLit(pos,IntLit.INT,8191),Binary.MUL,target),
Binary.ADD,
nf.Call(pos,nf.Field(pos,nf.This(pos),nf.Id(pos,name)),nf.Id(pos,"hashCode"))))));
}
bodyStmts.add(nf.Return(pos, target));
block = nf.Block(pos).statements(bodyStmts);
methodName = "hashCode";
md = nf.MethodDecl(pos,nf.FlagsNode(pos,flags),intTypeNode,nf.Id(pos,Name.make(methodName)),Collections.<Formal>emptyList(),block);
n = (X10ClassDecl_c) n.body(n.body().addMember(md));
}
// _struct_equals is used for == even when the user defined equals
// (both backends need to convert == to _struct_equals)
for (boolean isStructEquals : new boolean[]{false,true}) {
methodName = isStructEquals ? SharedVarsMethods.STRUCT_EQUALS_METHOD : "equals";
if (isStructEquals || !seenEqualsAny) {
// final public global safe def equals(other:Any):Boolean {
// if (!(other instanceof NAME)) return false;
// return equals(other as NAME);
// }
bodyStmts = new ArrayList<Stmt>();
Expr other =nf.Local(pos,nf.Id(pos,"other"));
bodyStmts.add(nf.If(pos, nf.Unary(pos, Unary.NOT,
nf.Instanceof(pos,other,structTypeNode)),
nf.Return(pos,nf.BooleanLit(pos,false))));
bodyStmts.add(nf.Return(pos,nf.Call(pos,nf.Id(pos,methodName),nf.Cast(pos,structTypeNode,other))));
block = nf.Block(pos).statements(bodyStmts);
Formal formal = nf.Formal(pos,nf.FlagsNode(pos,Flags.NONE),anyTypeNode,nf.Id(pos,"other"));
md = nf.MethodDecl(pos,nf.FlagsNode(pos,flags),boolTypeNode,nf.Id(pos,Name.make(methodName)), Collections.singletonList(formal),block);
n = (X10ClassDecl_c) n.body(n.body().addMember(md));
}
if (isStructEquals || !seenEqualsSelf) {
// final public global safe def equals(other:NAME):Boolean {
// return true && FIELD1==other.FIELD1 && ...;
// }
bodyStmts = new ArrayList<Stmt>();
Expr other =nf.Local(pos,nf.Id(pos,"other"));
Expr res = fields.isEmpty() ? nf.BooleanLit(pos, true) : null;
for (FieldDecl fi : fields) {
String name = fi.name().toString();
final Id id = nf.Id(pos, name);
Expr right = nf.Binary(pos,nf.Field(pos,nf.This(pos),id),Binary.EQ,nf.Field(pos,other,id));
if (res==null)
res = right;
else
res = nf.Binary(pos,res,Binary.COND_AND,right);
}
bodyStmts.add(nf.Return(pos, res));
block = nf.Block(pos).statements(bodyStmts);
Formal formal = nf.Formal(pos,nf.FlagsNode(pos,Flags.NONE),structTypeNode,nf.Id(pos,"other"));
md = nf.MethodDecl(pos,nf.FlagsNode(pos,flags),boolTypeNode,nf.Id(pos,Name.make(methodName)),Collections.singletonList(formal),block);
n = (X10ClassDecl_c) n.body(n.body().addMember(md));
}
}
return n;
}
private static ArrayList<AnnotationNode> createNative(NodeFactory nf,Position pos, String java, String cpp) {
ArrayList<AnnotationNode> res = new ArrayList<AnnotationNode>(2);
for (int i=0; i<2; i++) {
List<Expr> list = new ArrayList<Expr>(2);
list.add(nf.StringLit(pos, i==0 ? "java" : "c++"));
list.add(nf.StringLit(pos, i==0 ? java : cpp));
res.add( nf.AnnotationNode(pos, nf.AmbMacroTypeNode(pos, nf.PrefixFromQualifiedName(pos,QName.make("x10.compiler")), nf.Id(pos, "Native"), Collections.<TypeNode>emptyList(), list)) );
}
return res;
}
}