/*
* 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.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import polyglot.ast.ClassMember;
import polyglot.ast.Expr;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.frontend.Globals;
import polyglot.frontend.Job;
import polyglot.types.QName;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import x10.ast.X10ClassDecl;
import x10.ast.X10ConstructorDecl;
import x10.ast.X10FieldDecl;
import x10.ast.X10MethodDecl;
import x10.types.X10ClassDef;
import x10.types.X10ClassType;
import x10.types.X10Def;
import x10.types.X10MethodDef;
import x10.types.constants.StringValue;
import polyglot.types.TypeSystem;
import polyglot.util.CollectionUtil; import x10.util.CollectionFactory;
/**
* Visitor that checks @Native and @NativeRep annotations.
*/
public class CheckNativeAnnotationsVisitor extends ContextVisitor {
String theLanguage;
public CheckNativeAnnotationsVisitor(Job job, TypeSystem ts, NodeFactory nf, String theLanguage) {
super(job, ts, nf);
this.theLanguage = theLanguage;
}
public Map<String, String> getNativeRepParam(X10ClassDef def, int i) {
Map<String,String> map = CollectionFactory.newHashMap();
try {
TypeSystem xts = (TypeSystem) this.typeSystem();
Type rep = xts.NativeRep();
List<Type> as = def.annotationsMatching(rep);
for (Type at : as) {
assertNumberOfInitializers(at, 4);
String lang = getPropertyInit(at, 0);
if (lang != null) {
String lit = getPropertyInit(at, i);
map.put(lang, lit);
}
}
}
catch (SemanticException e) {}
return map;
}
public Map<String, String> getNativeRep(X10ClassDef def) {
return getNativeRepParam(def, 1);
}
public Map<String, String> getNativeBoxedRep(X10ClassDef def) {
return getNativeRepParam(def, 2);
}
public Map<String, String> getNativeRTTRep(X10ClassDef def) {
return getNativeRepParam(def, 3);
}
String getPropertyInit(Type at, int index) throws SemanticException {
at = Types.baseType(at);
if (at instanceof X10ClassType) {
X10ClassType act = (X10ClassType) at;
if (index < act.propertyInitializers().size()) {
Expr e = act.propertyInitializer(index);
if (e.isConstant() && e.constantValue() instanceof StringValue) {
return ((StringValue)e.constantValue()).value();
}
else {
throw new SemanticException("Property initializer for @" + at + " must be a string literal.");
}
}
}
return null;
}
void assertNumberOfInitializers(Type at, int len) {
at = Types.baseType(at);
if (at instanceof X10ClassType) {
X10ClassType act = (X10ClassType) at;
assert len == act.propertyInitializers().size();
}
}
Map<String,String> getNativeImplForDef(X10Def o) {
Map<String,String> map = CollectionFactory.newHashMap();
TypeSystem xts = (TypeSystem) o.typeSystem();
try {
Type java = xts.NativeType();
List<Type> as = o.annotationsMatching(java);
for (Type at : as) {
assertNumberOfInitializers(at, 2);
String lang = getPropertyInit(at, 0);
if (lang != null) {
String lit = getPropertyInit(at, 1);
map.put(lang, lit);
}
}
}
catch (SemanticException e) {}
return map;
}
public Node leaveCall(Node old, Node n, NodeVisitor v) throws SemanticException {
boolean isNative = false;
boolean classHasNativeRep = false;
boolean defHasNativeImp = false;
if (! (n instanceof ClassMember))
return n;
X10ClassDef cd = (X10ClassDef) context.currentClassDef();
if (cd == null)
return n;
Map<String,String> nativeReps = getNativeRep(cd);
Map<String,String> nativeImps = Collections.<String,String>emptyMap();
if (! nativeReps.isEmpty())
classHasNativeRep = true;
if (n instanceof X10MethodDecl) {
X10MethodDecl md = (X10MethodDecl) n;
X10Def def = (X10Def) md.methodDef();
if (md.flags().flags().isNative())
isNative = true;
nativeImps = getNativeImplForDef(def);
}
if (n instanceof X10ConstructorDecl) {
X10ConstructorDecl xd = (X10ConstructorDecl) n;
X10Def def = (X10Def) xd.constructorDef();
if (xd.flags().flags().isNative())
isNative = true;
nativeImps = getNativeImplForDef(def);
}
if (n instanceof X10FieldDecl) {
X10FieldDecl fd = (X10FieldDecl) n;
X10Def def = (X10Def) fd.fieldDef();
nativeImps = getNativeImplForDef(def);
}
if (n instanceof X10ClassDecl) {
if (nativeReps.containsKey(theLanguage)) {
{
Type t = cd.asType().superClass();
if (t != null) {
X10ClassType ct = (X10ClassType) Types.baseType(t);
X10ClassDef sd = ct.x10Def();
Map<String, String> map = getNativeRep(sd);
if (!map.containsKey(theLanguage)) {
throw new SemanticException("Class with NativeRep annotation may only extend classes with NativeRep.", n.position());
}
}
}
}
}
if (isNative && nativeImps.get(theLanguage) == null && n instanceof X10MethodDecl)
throw new SemanticException("Native methods must have a @Native annotation for backend \"" + theLanguage + "\".", n.position());
// if (! nativeReps.isEmpty() && ! isNative && n instanceof X10MethodDecl) {
// throw new SemanticException("Class with NativeRep annotation may contain only native methods.", n.position());
// }
// if (! nativeReps.isEmpty() && ! isNative && n instanceof X10ConstructorDecl) {
// throw new SemanticException("Class with NativeRep annotation may contain only native constructors.", n.position());
// }
// if (! nativeImps.isEmpty() && ! isNative && n instanceof X10MethodDecl) {
// throw new SemanticException("Method with Native annotation must be declared native.");
// }
if (nativeReps.containsKey(theLanguage)) {
if (! nativeImps.containsKey(theLanguage) && n instanceof X10MethodDecl) {
X10MethodDecl md = (X10MethodDecl) n;
X10MethodDef def = (X10MethodDef) md.methodDef();
// HACK: ignore unary property methods -- there could be a native annotation on the property
if (def.flags().isProperty() && def.formalTypes().size() == 0)
;
else if (md.name().toString().equals("typeName")) // special case this synthetic method
;
else
throw new SemanticException("Method " + md + "\n of class " + def.container()+ " with NativeRep annotation must be annotated Native.", n.position());
}
if (! nativeImps.containsKey(theLanguage) && n instanceof X10FieldDecl) {
throw new SemanticException("Fields of a class with NativeRep annotation must be annotated Native.", n.position());
}
}
if (! nativeImps.isEmpty() && n instanceof X10ConstructorDecl) {
throw new SemanticException("Constructors may not have Native annotations.");
}
return n;
}
}