/*
* 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.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import polyglot.ast.Assign;
import polyglot.ast.Call;
import polyglot.ast.CanonicalTypeNode;
import polyglot.ast.ClassBody;
import polyglot.ast.ClassMember;
import polyglot.ast.Expr;
import polyglot.ast.FieldDecl;
import polyglot.ast.Formal;
import polyglot.ast.Id;
import polyglot.ast.New;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Receiver;
import polyglot.ast.Stmt;
import polyglot.ast.TypeNode;
import polyglot.frontend.Job;
import polyglot.main.Options;
import polyglot.main.Reporter;
import polyglot.types.ClassDef;
import polyglot.types.ClassType;
import polyglot.types.ConstructorDef;
import polyglot.types.ConstructorInstance;
import polyglot.types.Flags;
import polyglot.types.LocalDef;
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.util.ErrorInfo;
import polyglot.util.Position;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import x10.ast.AnnotationNode;
import x10.ast.TypeParamNode;
import x10.ast.X10ClassDecl;
import x10.ast.X10ConstructorDecl;
import x10.ast.X10MethodDecl;
import x10.extension.X10Ext;
import x10.types.MethodInstance;
import x10.types.ParameterType;
import x10.types.X10ClassDef;
import x10.types.X10ClassType;
import x10.types.X10ConstructorDef;
import x10.types.X10Def;
import x10.types.X10FieldDef;
import x10.types.X10MethodDef;
import x10.util.FileUtils;
import x10.types.constants.StringValue;
/**
* Visitor that expands @NativeClass and @NativeDef annotations.
*/
public class NativeClassVisitor extends ContextVisitor {
public static final Name NATIVE_FIELD_NAME = Name.make("__NATIVE_FIELD__");
final String theLanguage;
final TypeSystem xts;
final NodeFactory xnf;
public NativeClassVisitor(Job job, TypeSystem ts, NodeFactory nf, String theLanguage) {
super(job, ts, nf);
xts = (TypeSystem) ts;
xnf = (NodeFactory) nf;
this.theLanguage = theLanguage;
}
protected boolean isNativeDef(X10Def def) throws SemanticException {
Type t = xts.systemResolver().findOne(QName.make("x10.compiler.NativeDef"));
List<Type> as = def.annotationsMatching(t);
for (Type at : as) {
String lang = getPropertyInit(at, 0);
if (theLanguage.equals(lang)) {
return true;
}
}
return false;
}
protected String getNativeClassName(X10ClassDef def) throws SemanticException {
Type t = xts.NativeClass();
List<Type> as = def.annotationsMatching(t);
for (Type at : as) {
String lang = getPropertyInit(at, 0);
if (theLanguage.equals(lang)) {
return getPropertyInit(at, 2);
}
}
return null;
}
protected String getNativeClassPackage(X10ClassDef def) throws SemanticException {
Type t = xts.NativeClass();
List<Type> as = def.annotationsMatching(t);
for (Type at : as) {
String lang = getPropertyInit(at, 0);
if (theLanguage.equals(lang)) {
return getPropertyInit(at, 1);
}
}
return null;
}
protected 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;
}
protected static Flags clearNative(Flags flags) {
return flags.clear(Flags.NATIVE);
}
protected Node leaveCall(Node parent, Node old, Node n, NodeVisitor v) throws SemanticException {
// look for @NativeClass class declarations
if (!(n instanceof X10ClassDecl))
return n;
X10ClassDecl cdecl = (X10ClassDecl) n;
X10ClassDef cdef = (X10ClassDef) cdecl.classDef();
String cname = getNativeClassName(cdef);
if (cname == null)
return n;
if (reporter.should_report(Reporter.nativeclass, 1))
reporter.report(1, "Processing @NativeClass " + cdecl);
ClassBody cbody = cdecl.body();
List<ClassMember> cmembers = new ArrayList<ClassMember>();
Position p = Position.compilerGenerated(cbody.position());
// create fake def for native class
X10ClassDef fake = (X10ClassDef) xts.createClassDef();
fake.name(Name.make(cname));
fake.kind(ClassDef.TOP_LEVEL);
fake.setFromEncodedClassFile();
if (cdef.isStruct()) {
fake.setFlags(Flags.STRUCT);
} else {
fake.setFlags(Flags.NONE);
}
String cpackage = getNativeClassPackage(cdef);
fake.setPackage(Types.ref(ts.packageForName(QName.make(cpackage))));
java.util.Iterator<ParameterType> ps = cdef.typeParameters().iterator();
java.util.Iterator<ParameterType.Variance> vs = cdef.variances().iterator();
while (ps.hasNext()) {
ParameterType pp = ps.next();
ParameterType.Variance vv = vs.next();
fake.addTypeParameter(pp, vv);
}
X10ClassType embed = (X10ClassType) xts.Embed();
List<AnnotationNode> anodes;
if (fake.isStruct()) {
anodes = Collections.<AnnotationNode>emptyList();
} else {
anodes = Collections.<AnnotationNode>singletonList(xnf.AnnotationNode(p, xnf.CanonicalTypeNode(p, embed)));
}
// add field with native type
Name fname = NATIVE_FIELD_NAME;
Id fid = xnf.Id(p, fname);
ClassType ftype = fake.asType();
CanonicalTypeNode ftnode = xnf.CanonicalTypeNode(p, ftype);
Flags fflags = Flags.PRIVATE.Final();
X10FieldDef fdef = xts.fieldDef(p, Types.ref(cdef.asType()), fflags, Types.ref(ftype), fname);
fdef.setDefAnnotations(Collections.<Ref<? extends Type>>singletonList(Types.ref(embed)));
FieldDecl fdecl = xnf.FieldDecl(p, xnf.FlagsNode(p, fflags), ftnode, fid).fieldDef(fdef);
fdecl = (FieldDecl) ((X10Ext) fdecl.ext()).annotations(anodes);
cmembers.add(fdecl);
cdef.addField(fdef);
// field selector
Receiver special = xnf.This(p).type(cdef.asType());
Receiver field = xnf.Field(p, special, fid).fieldInstance(fdef.asInstance()).type(ftype);
// add copy constructor
ConstructorInstance xinst;
{
Name id0 = Name.make("id0");
LocalDef ldef = xts.localDef(p, Flags.FINAL, Types.ref(ftype), id0);
Expr init = xnf.Local(p, xnf.Id(p, id0)).localInstance(ldef.asInstance()).type(ftype);
Expr assign = xnf.FieldAssign(p, special, fid, Assign.ASSIGN, init).fieldInstance(fdef.asInstance()).type(ftype);
Formal f = xnf.Formal(p, xnf.FlagsNode(p, Flags.FINAL), ftnode, xnf.Id(p, id0)).localDef(ldef);
ArrayList<Stmt> ctorBlock = new ArrayList<Stmt>();
// super constructor def (noarg)
final TypeNode superClass = cdecl.superClass();
if (superClass!=null) {
ConstructorDef sdef = xts.findConstructor(superClass.type(),
xts.ConstructorMatcher(superClass.type(), Collections.<Type>emptyList(), context)).def();
ctorBlock.add(xnf.SuperCall(p, Collections.<Expr>emptyList()).constructorInstance(sdef.asInstance()));
}
ctorBlock.add(xnf.Eval(p, assign));
X10ConstructorDecl xd = (X10ConstructorDecl) xnf.ConstructorDecl(p,
xnf.FlagsNode(p, Flags.PRIVATE),
cdecl.name(),
Collections.<Formal>singletonList(f),
xnf.Block(p,ctorBlock));
xd = xd.typeParameters(Collections.<TypeParamNode>emptyList());
xd = xd.returnType(ftnode);
ConstructorDef xdef = xts.constructorDef(p, p,
Types.ref(cdef.asType()),
Flags.PRIVATE,
Collections.<Ref<? extends Type>>singletonList(Types.ref(ftype)),
Collections.<Ref<? extends Type>>emptyList()
);
cmembers.add(xd.constructorDef(xdef));
xinst = xdef.asInstance(); // to be used later
}
Boolean hasNativeConstructor = false;
// look for native methods and constructors
for (ClassMember m : cbody.members()) {
if (m instanceof X10MethodDecl) {
X10MethodDecl mdecl = (X10MethodDecl) m;
X10MethodDef mdef = (X10MethodDef) mdecl.methodDef();
if (!isNativeDef(mdef) && !mdef.flags().isNative()) {
cmembers.add(m);
continue;
}
if (reporter.should_report("nativeclass", 2))
reporter.report(1, "Processing @NativeDef " + mdecl);
// clear native flag
mdecl = (X10MethodDecl) mdecl.flags(xnf.FlagsNode(p, clearNative(mdecl.flags().flags())));
mdef.setFlags(clearNative(mdef.flags()));
// turn formals into arguments of delegate call
List<Expr> args = new ArrayList<Expr>();
for (Formal f : mdecl.formals())
args.add(xnf.Local(p, f.name()).localInstance(f.localDef().asInstance()).type(f.type().type()));
// reuse x10 method instance for delegate method
MethodInstance minst = mdef.asInstance();
minst = (MethodInstance) minst.container(ftype);
// call delegate
Receiver target = mdef.flags().isStatic() ? ftnode : field;
Call call = xnf.Call(p, target, mdecl.name(), args); // no type yet
// void vs factory vs non-void methods
Stmt body;
if (mdecl.returnType().type().isVoid()) {
body = xnf.Eval(p, call.methodInstance(minst).type(ts.Void()));
} else if (mdecl.returnType().type().isSubtype(cdef.asType(), context)) {
// delegate method return native object
minst = minst.returnType(ftype);
// call copy constructor
New copy = xnf.New(p,
xnf.CanonicalTypeNode(p, Types.ref(cdef.asType())),
Collections.<Expr>singletonList(call.methodInstance(minst).type(ftype)));
body = xnf.Return(p, copy.constructorInstance(xinst).type(cdef.asType()));
} else{
body = xnf.Return(p, call.methodInstance(minst).type(mdecl.returnType().type()));
}
cmembers.add((X10MethodDecl) mdecl.body(xnf.Block(p, body)));
continue;
}
if (m instanceof X10ConstructorDecl) {
X10ConstructorDecl xdecl = (X10ConstructorDecl) m;
X10ConstructorDef xdef = (X10ConstructorDef) xdecl.constructorDef();
if (!isNativeDef(xdef) && !xdef.flags().isNative()) {
// TODO: check that non-native constructors invoke native constructors
cmembers.add(m);
continue;
}
hasNativeConstructor = true; // good!
if (reporter.should_report("nativeclass", 2))
reporter.report(1, "Processing @NativeDef " + xdecl);
// clear native flag
xdecl = (X10ConstructorDecl) xdecl.flags(xnf.FlagsNode(p, clearNative(xdecl.flags().flags())));
xdef.setFlags(clearNative(xdef.flags()));
// turn formals into arguments of delegate call
List<Expr> args = new ArrayList<Expr>();
for (Formal f : xdecl.formals())
args.add(xnf.Local(p, f.name()).localInstance(f.localDef().asInstance()).type(f.type().type()));
// call delegate constructor
Expr init = xnf.New(p, ftnode, args).constructorInstance(xdef.asInstance()).type(ftype);
init = (Expr) ((X10Ext) init.ext()).annotations(anodes);
// invoke copy constructor
Expr assign = xnf.FieldAssign(p, special, fid, Assign.ASSIGN, init).fieldInstance(fdef.asInstance()).type(ftype);
ArrayList<Stmt> ctorBlock = new ArrayList<Stmt>();
// super constructor def (noarg)
final TypeNode superClass = cdecl.superClass();
if (superClass!=null) {
ConstructorDef sdef = xts.findConstructor(superClass.type(),
xts.ConstructorMatcher(superClass.type(), Collections.<Type>emptyList(), context)).def();
ctorBlock.add(xnf.SuperCall(p, Collections.<Expr>emptyList()).constructorInstance(sdef.asInstance()));
}
ctorBlock.add(xnf.Eval(p, assign));
// Stmt body = xnf.ThisCall(p, Collections.<Expr>singletonList(init)).constructorInstance(xinst);
cmembers.add((X10ConstructorDecl) xdecl.body(xnf.Block(p, ctorBlock)));
continue;
}
cmembers.add(m);
}
if (!hasNativeConstructor) {
throw new SemanticException("@NativeClass " + cdecl.name() + " must declare a native constructor.");
}
return cdecl.body(cbody.members(cmembers));
}
}