/* * 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.ArrayList; import java.util.Collections; import java.util.List; import polyglot.ast.Assign; import polyglot.ast.Block; import polyglot.ast.ClassBody; import polyglot.ast.ClassDecl; import polyglot.ast.ClassMember; import polyglot.ast.ConstructorCall; import polyglot.ast.Eval; import polyglot.ast.Expr; import polyglot.ast.FieldAssign; import polyglot.ast.FieldDecl; import polyglot.ast.Formal; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.Special; import polyglot.ast.Stmt; import polyglot.ast.MethodDecl_c; import polyglot.ast.TypeNode; import polyglot.ast.MethodDecl; import polyglot.ast.Call; import polyglot.ast.Id; import polyglot.frontend.Job; import polyglot.types.ConstructorInstance; import polyglot.types.Context; import polyglot.types.FieldDef; import polyglot.types.QName; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.types.TypeSystem; import polyglot.types.Flags; import polyglot.types.Types; import polyglot.types.Name; import polyglot.types.MethodDef; import polyglot.types.Ref; import polyglot.types.FieldInstance; import polyglot.util.Position; import polyglot.visit.ContextVisitor; import polyglot.visit.NodeVisitor; import polyglot.visit.LocalClassRemover; import x10.types.X10ClassType; import x10.types.X10ConstructorDef; import x10.types.X10Def; import polyglot.types.TypeSystem; import x10.ast.X10ConstructorDecl; import x10.ast.X10MethodDecl_c; import x10.ast.AssignPropertyCall; /** * Visitor that moves field initializers to the constructor * and insert explicit super constructor invocations * unless constructor is annotated @NoSuperCall. * Yoav added: * Field initializers are placed after the AssignProperty call, because we have a 3-phase init: super, properties, ctor-code * Note that the type of a field may refer to a property, so even field assignment is prohibited until after the AssignProperty call. * */ public class FieldInitializerMover extends ContextVisitor { public FieldInitializerMover(Job job, TypeSystem ts, NodeFactory nf) { super(job, ts, nf); } protected ConstructorCall superCall(Type superType, Context context) throws SemanticException { Position CG = Position.COMPILER_GENERATED; assert (superType.isClass()); Expr qualifier = null; if (superType.toClass().def().isMember() && !superType.toClass().flags().isStatic()) qualifier = nf.This(CG, nf.CanonicalTypeNode(CG, superType.toClass().outer())); ConstructorCall cc = nf.SuperCall(CG, qualifier, Collections.<Expr>emptyList()); ConstructorInstance ci = ts.findConstructor(superType, ts.ConstructorMatcher(superType, Collections.<Type>emptyList(), context)); return cc.constructorInstance(ci); } protected static boolean mustCallSuper(X10ConstructorDecl cdecl) throws SemanticException { X10ConstructorDef def = cdecl.constructorDef(); Type t = def.typeSystem().systemResolver().findOne(QName.make("x10.compiler.NoSuperCall")); return def.annotationsMatching(t).isEmpty(); } class FindProperty extends NodeVisitor { private final Stmt evalCall; private boolean didFindProperty = false; private FindProperty(Stmt evalCall) { this.evalCall = evalCall; } @Override public Node leave(Node old, Node n, NodeVisitor v) { if (n instanceof AssignPropertyCall) { AssignPropertyCall propCall = (AssignPropertyCall) n; didFindProperty = true; return nf.Block(n.position().markCompilerGenerated(),propCall,evalCall); } return n; } } private ClassDecl changeClass(ClassDecl cdecl) throws SemanticException { final ClassBody cb = cdecl.body(); final List<ClassMember> members = new ArrayList<ClassMember>(); final NodeFactory nf = this.nodeFactory(); final TypeSystem ts = this.typeSystem(); final Context context = cdecl.enterChildScope(cb, this.context()); final Position p = cdecl.position().markCompilerGenerated(); final Special this_ = (Special) nf.This(p).type(cdecl.classDef().asType()); List<Stmt> assignments = new ArrayList<Stmt>(); for (ClassMember cm : cb.members()) { if (cm instanceof FieldDecl) { FieldDecl fd = (FieldDecl) cm; FieldDef def = fd.fieldDef(); if (fd.init() != null && !def.flags().isStatic()) { final FieldInstance fieldInstance = def.asInstance(); FieldAssign a = nf.FieldAssign(p, this_, nf.Id(p, def.name()), Assign.ASSIGN, fd.init()); a = a.fieldInstance(fieldInstance); a = (FieldAssign) a.type(fieldInstance.type()); assert this_.type() != null; assert a.type() != null; Eval eval = nf.Eval(p, a); assignments.add(eval); fd = fd.init(null); } members.add(fd); } else { members.add(cm); } } Stmt evalCall = nf.Block(p); // an empty statement if (assignments.size()>0) { // create a final method that includes all the field initializers TypeNode returnType = nf.CanonicalTypeNode(p,ts.Void()); final Name name = Name.makeFresh("__fieldInitializers"); final Id nameId = nf.Id(p, name); final Flags flags = Flags.PUBLIC.Final(); MethodDecl method = nf.MethodDecl(p,nf.FlagsNode(p, flags),returnType, nameId, Collections.<Formal>emptyList(), nf.Block(p,assignments)); method = (MethodDecl) method.visit( new LocalClassRemover.MarkReachable() ); MethodDef md = ts.methodDef(p, p, Types.ref(cdecl.classDef().asType()), flags, returnType.typeRef(), name, Collections.<Ref<? extends Type>>emptyList(), Collections.<Ref<? extends Type>>emptyList()); method = method.methodDef(md); members.add(method); // create the call to __fieldInitializers Call call = nf.Call(p,this_,nameId,Collections.<Expr>emptyList()).methodInstance(md.asInstance()); call = (Call) call.type(ts.Void()); evalCall = nf.Eval(p, call); } final List<ClassMember> members2 = new ArrayList<ClassMember>(); for (ClassMember cm : members) { if (cm instanceof X10ConstructorDecl) { X10ConstructorDecl cd = (X10ConstructorDecl) cm; Block body = cd.body(); if (body == null) { body = nf.Block(p); } // if there is a property(...) call, then we replace it with: { property(...); __fieldInitializers(); } FindProperty findProperty = new FindProperty(evalCall); body = (Block) body.visit(findProperty); boolean didFindProperty = findProperty.didFindProperty; List<Stmt> stmts = body.statements(); if (stmts.size() > 0 && stmts.get(0) instanceof ConstructorCall) { ConstructorCall cc = (ConstructorCall) stmts.get(0); if (cc.kind() == ConstructorCall.SUPER) { List<Stmt> ss = new ArrayList<Stmt>(); ss.add(cc); if (!didFindProperty) ss.add(evalCall); ss.addAll(stmts.subList(1, stmts.size())); body = body.statements(ss); } } else { // implicit super call List<Stmt> ss = new ArrayList<Stmt>(); if (cdecl.superClass() != null && mustCallSuper(cd)) ss.add(superCall(cdecl.superClass().type(), cb.enterChildScope(cd, context))); if (!didFindProperty) ss.add(evalCall); ss.addAll(stmts); body = body.statements(ss); } if (body != cd.body()) { cd = (X10ConstructorDecl) cd.body(body); } members2.add(cd); } else { members2.add(cm); } } return cdecl.body(cb.members(members2)); } @Override public Node leaveCall(Node old, Node n, NodeVisitor v) throws SemanticException { FieldInitializerMover fim = (FieldInitializerMover) v; if (n instanceof ClassDecl) { final ClassDecl cdecl = (ClassDecl) n; if (!cdecl.flags().flags().isInterface()) return fim.changeClass(cdecl); } return super.leaveCall(old, n, v); } }