/* * 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 * * This file was originally derived from the Polyglot extensible compiler framework. * * (C) Copyright 2000-2007 Polyglot project group, Cornell University * (C) Copyright IBM Corporation 2007-2012. */ package polyglot.visit; import java.util.*; import polyglot.ast.*; import polyglot.types.*; import polyglot.util.*; import x10.types.X10ClassDef; import x10.util.CollectionFactory; // TODO: //Convert closures to anon //Add frame classes around anon and local //now all classes access only final locals //Convert local and anon to member //Dup inner member to static //Remove inner member public abstract class LocalClassRemover extends ContextVisitor { protected abstract class ConstructorCallRewriter extends NodeVisitor { protected final List<FieldDef> newFields; protected final ClassDef theLocalClass; protected ClassDef curr; public ConstructorCallRewriter(List<FieldDef> fields, ClassDef ct) { this.newFields = fields; this.theLocalClass = ct; } public NodeVisitor enter(Node n) { if (n instanceof ClassDecl) { ConstructorCallRewriter v = (ConstructorCallRewriter) shallowCopy(); v.curr = ((ClassDecl) n).classDef(); return v; } return this; } public Node leave(Node old, Node n, NodeVisitor v) { if (n instanceof New) { New neu = (New) n; ConstructorInstance ci = neu.constructorInstance(); ConstructorDef nci = ci.def(); ClassType container = Types.get(nci.container()).toClass(); if (container.def() == theLocalClass) { neu = (New) neu.arguments(addArgs(neu, nci, newFields, curr, theLocalClass)); neu = neu.constructorInstance(ci.formalTypes(addArgTypes(ci, nci, newFields, curr, theLocalClass))); if (! theLocalClass.flags().isStatic()) { Expr q; ClassDef outer = Types.get(theLocalClass.outer()); if (outer == context.currentClassDef()) q = nf.This(neu.position()).type(outer.asType()); else q = nf.This(neu.position(), nf.CanonicalTypeNode(neu.position(), outer.asType())).type(outer.asType()); neu = neu.qualifier(q); } } return neu; } if (n instanceof ConstructorCall) { ConstructorCall neu = (ConstructorCall) n; ConstructorInstance ci = neu.constructorInstance(); ConstructorDef nci = ci.def(); ClassType container = Types.get(nci.container()).toClass(); if (container.def() == theLocalClass) { neu = (ConstructorCall) neu.arguments(addArgs(neu, nci, newFields, curr, theLocalClass)); // This is wrong: we cannot refer to this in a super() call qualifier. // For now, let this pass and assume InnerClassRemover will fix it. if (! theLocalClass.flags().isStatic()) { Expr q; ClassDef outer = Types.get(theLocalClass.outer()); if (outer == context.currentClassDef()) q = nf.This(neu.position()).type(outer.asType()); else q = nf.This(neu.position(), nf.CanonicalTypeNode(neu.position(), outer.asType())).type(outer.asType()); neu = neu.qualifier(q); } } return neu; } return n; } } protected final InnerClassRemover icrv; public LocalClassRemover(InnerClassRemover icrv) { super(icrv.job(), icrv.typeSystem(), icrv.nodeFactory()); this.icrv = icrv; } Map<Pair<LocalDef, ClassDef>, FieldDef> fieldForLocal = CollectionFactory.newHashMap(); Map<ClassDef, List<ClassMember>> orphans = CollectionFactory.newHashMap(); Map<ClassDef,List<FieldDef>> newFields = CollectionFactory.newHashMap(); public Node override(Node parent, Node n) { // Find local classes in a block and remove them. // Rewrite local classes to instance member classes. // Add a field to the local class for each captured local variable. // Add a formal to each constructor for each introduced field. // Add a field initializer to each constructor for each introduced field. // Rewrite constructor calls to pass in the locals. // TODO: handle SwitchBlock correctly if (n instanceof Block) { final Block b = (Block) n; List<Stmt> ss = new ArrayList<Stmt>(b.statements()); for (int i = 0; i < ss.size(); i++) { Stmt s = ss.get(i); if (s instanceof LocalClassDecl) { LocalClassDecl lcd = (LocalClassDecl) s; ClassDecl cd = lcd.decl(); // Box locals cd = (ClassDecl) n.visitChild(cd, localBoxer()); Flags oldFlags = cd.flags().flags(); Flags flags = context.inStaticContext() ? oldFlags.Private().Static() : oldFlags.Private(); Id name = nf.Id(cd.name().position(), cd.name().toString()+"$"+cd.position().offset()); cd = cd.name(name); cd = cd.flags(cd.flags().flags(flags)); cd.classDef().flags(flags); cd.classDef().kind(ClassDef.MEMBER); cd.classDef().name(cd.name().id()); cd = renameConstructors(cd, nf); cd = rewriteLocalClass(cd, (List<FieldDef>) hashGet(newFields, cd.classDef(), Collections.<FieldDef>emptyList())); // Nested local classes will be visited later //cd = (ClassDecl) n.visitChild(cd, this); ss.remove(i); // Remove the local class declaration hashAdd(orphans, context.currentClassDef(), cd); if (cd != lcd.decl()) { // Rewrite the constructor calls in the remaining statements for (int j = i; j < ss.size(); j++) { Stmt sj = ss.get(j); sj = (Stmt) rewriteConstructorCalls(sj, cd.classDef(), (List<FieldDef>)hashGet(newFields, cd.classDef(), Collections.<FieldDef>emptyList())); ss.set(j, sj); } } i--; } else { s = (Stmt) n.visitChild(s, this); ss.set(i, s); } } return b.statements(ss); } return null; } protected abstract NodeVisitor localBoxer(); protected abstract class LocalBoxer extends ContextVisitor { public LocalBoxer() { super(LocalClassRemover.this.job, LocalClassRemover.this.ts, LocalClassRemover.this.nf); } protected Node leaveCall(Node old, Node n, NodeVisitor v) { Context context = this.context(); Position pos = n.position(); if (n instanceof Local) { Local l = (Local) n; if (!isLocal(context, l.name().id())) { FieldDef fi = boxLocal(l.localInstance().def()); if (fi != null) { Field f = nf.Field(pos, makeMissingFieldTarget(fi.asInstance(), pos), nf.Id(pos, fi.name())); f = f.fieldInstance(fi.asInstance()); f = (Field) f.type(fi.asInstance().type()); return f; } } } return n; } // Create a field instance for a local. private FieldDef boxLocal(LocalDef li) { ClassDef curr = currLocalClass(); if (curr == null) // not defined in a local class return null; Pair<LocalDef, ClassDef> key = new Pair<LocalDef, ClassDef>(li, curr); FieldDef fi = fieldForLocal.get(key); if (fi != null) return fi; Position pos = li.position(); fi = ts.fieldDef(pos, Types.ref(computeConstructedType(curr, context().currentCode())), li.flags().Private(), li.type(), li.name()); fi.setNotConstant(); curr.addField(fi); List<FieldDef> l = hashGet(newFields, curr, new ArrayList<FieldDef>()); l.add(fi); localOfField.put(fi, li); fieldForLocal.put(key, fi); return fi; } /** Get the currently enclosing local class, or null. */ private ClassDef currLocalClass() { ClassDef curr = context.currentClassDef(); while (curr != null) { if (curr.isLocal() || curr.isAnonymous()) return curr; if (curr.isTopLevel()) break; curr = Types.get(curr.outer()); } return null; } } /** * Rename all of the constructors to match the class name. */ private ClassDecl renameConstructors(ClassDecl cd, NodeFactory nf) { ClassBody b = cd.body(); List<ClassMember> newMembers = new ArrayList<ClassMember>(); List<ClassMember> members = b.members(); for (ClassMember m : members) { if (m instanceof ConstructorDecl) { ConstructorDecl td = (ConstructorDecl) m; td = td.name(nf.Id(td.name().position(), cd.name().id())); newMembers.add(td.name(cd.name())); } else { newMembers.add(m); } } return cd.body(b.members(newMembers)); } protected abstract boolean isLocal(Context c, Name name); protected Node leaveCall(Node old, Node n, NodeVisitor v) { Position pos = n.position(); // Convert anonymous classes into member classes if (n instanceof New) { New neu = (New) n; ClassBody body = neu.body(); if (body == null) return neu; // Box locals body = (ClassBody) neu.visitChild(body, localBoxer()); // Check if extending a class or an interface. TypeNode superClass = neu.objectType(); List<TypeNode> interfaces = Collections.<TypeNode>emptyList(); ConstructorInstance ci = neu.constructorInstance(); ClassType supertype = neu.objectType().type().toClass(); if (supertype != null && supertype.flags().isInterface()) { superClass = null; interfaces = Collections.singletonList(neu.objectType()); assert (neu.arguments().isEmpty()); ci = null; } Flags oldFlags = neu.anonType().flags(); Flags flags = context.inStaticContext() ? oldFlags.Private().Static() : oldFlags.Private(); Id name = nf.Id(pos, "Anonymous"+"$"+neu.position().offset()); ClassDecl cd = nf.ClassDecl(pos, nf.FlagsNode(pos, flags), name, superClass, interfaces, body); X10ClassDef type = neu.anonType(); type.kind(ClassDef.MEMBER); type.name(cd.name().id()); type.outer(Types.ref(context.currentClassDef())); type.setPackage(Types.ref(context.package_())); type.flags(flags); cd = cd.classDef(type); ConstructorDecl td = addConstructor(cd, neu, ci); // Add the CI to the class. type.addConstructor(td.constructorDef()); { // Append the constructor to the body. ClassBody b = cd.body(); List<ClassMember> members = new ArrayList<ClassMember>(); members.addAll(b.members()); members.add(td); b = b.members(members); cd = cd.body(b); } neu = neu.constructorInstance(computeConstructorInstance(td.constructorDef())); neu = neu.anonType(null); if (! flags.isStatic()) { neu = neu.qualifier(nf.This(pos).type(context.currentClass())); } cd = rewriteLocalClass(cd, (List<FieldDef>) hashGet(newFields, cd.classDef(), Collections.<FieldDef>emptyList())); hashAdd(orphans, context.currentClassDef(), cd); neu = adjustObjectType(neu, computeConstructedType(type, context().currentCode())); neu = neu.body(null); neu = (New) rewriteConstructorCalls(neu, cd.classDef(), (List<FieldDef>) hashGet(newFields, cd.classDef(), Collections.<FieldDef>emptyList())); return neu; } // Add any orphaned declarations created below to the class body if (n instanceof ClassDecl) { ClassDecl cd = (ClassDecl) n; List<ClassMember> o = orphans.get(cd.classDef()); if (o == null) return cd; ClassBody b = cd.body(); o = b.visitList(o, this); List<ClassMember> members = new ArrayList<ClassMember>(); members.addAll(b.members()); members.addAll(o); b = b.members(members); return cd.body(b); } return n; } protected New adjustObjectType(New neu, ClassType ct) { return neu.objectType(nf.CanonicalTypeNode(neu.objectType().position(), ct)); } protected abstract ConstructorInstance computeConstructorInstance(ConstructorDef cd); protected abstract ClassType computeConstructedType(ClassDef type, CodeDef codeDef); protected abstract ClassDecl rewriteLocalClass(ClassDecl cd, List<FieldDef> newFields); protected abstract Node rewriteConstructorCalls(Node s, final ClassDef ct, final List<FieldDef> fields); // Create a new constructor for an anonymous class. protected ConstructorDecl addConstructor(ClassDecl cd, New neu, ConstructorInstance superCI) { // Build the list of formal parameters and list of arguments for the super call. List<Formal> formals = new ArrayList<Formal>(); List<Expr> args = new ArrayList<Expr>(); List<Ref<? extends Type>> argTypes = new ArrayList<Ref<? extends Type>>(); List<Ref<? extends Type>> throwTypes = new ArrayList<Ref<? extends Type>>(); int i = 1; for (Expr e : neu.arguments()) { Position pos = e.position(); Id name = nf.Id(pos, "a" + i); i++; Formal f = nf.Formal(pos, nf.FlagsNode(pos, Flags.FINAL), nf.CanonicalTypeNode(pos, e.type()), name); Local l = nf.Local(pos, name); LocalDef li = ts.localDef(pos, f.flags().flags(), f.type().typeRef(), name.id()); li.setNotConstant(); f = f.localDef(li); l = l.localInstance(li.asInstance()); l = (Local) l.type(li.asInstance().type()); formals.add(f); args.add(l); argTypes.add(li.type()); } Position pos = cd.position().markCompilerGenerated(); List<Stmt> statements = new ArrayList<Stmt>(); if (superCI != null) { for (Type t : superCI.throwTypes()) { throwTypes.add(Types.ref(t)); } // Create the super call. ConstructorCall cc = nf.SuperCall(pos, args); cc = cc.constructorInstance(superCI); cc = cc.qualifier(adjustQualifier(neu.qualifier())); statements.add(cc); } // Create the constructor declaration node and the CI. ConstructorDecl td = nf.ConstructorDecl(pos, nf.FlagsNode(pos, Flags.PRIVATE), cd.name(), formals, nf.Block(pos, statements)); td = (ConstructorDecl) td.visit(new MarkReachable()); ConstructorDef ci = ts.constructorDef(pos, pos, Types.ref(cd.classDef().asType()), Flags.PRIVATE, argTypes, throwTypes); td = td.constructorDef(ci); return td; } public static class MarkReachable extends NodeVisitor { @Override public Node leave(Node old, Node n, NodeVisitor v) { Node res = n instanceof Term ? ((Term) n).reachable(true) : n; return res; } } private Expr adjustQualifier(Expr e) { if (e instanceof Special) { Special s = (Special) e; if (s.kind() == Special.THIS && s.qualifier() == null) { return s.qualifier(nf.CanonicalTypeNode(s.position(), s.type())); } } return e; } // Add types to the argument list until it matches the declaration. protected List<Type> addArgTypes(ConstructorInstance ci, ConstructorDef nci, List<FieldDef> fields, ClassDef curr, ClassDef theLocalClass) { if (nci == null || fields == null || fields.isEmpty() || ci.formalTypes().size() == nci.formalTypes().size()) return ci.formalTypes(); List<Type> args = new ArrayList<Type>(); for (FieldDef fi : fields) { if (curr != null && theLocalClass != null && ts.isEnclosed(curr, theLocalClass)) { // If in the local class being rewritten, use the type of the boxed local (i.e., field) instead of the local. args.add(fi.asInstance().type()); } else { LocalDef li = localOfField.get(fi); assert (li != null); args.add(li.asInstance().type()); } } args.addAll(ci.formalTypes()); return args; } // Add local variables to the argument list until it matches the declaration. protected List<Expr> addArgs(ProcedureCall n, ConstructorDef nci, List<FieldDef> fields, ClassDef curr, ClassDef theLocalClass) { if (nci == null || fields == null || fields.isEmpty() || n.arguments().size() == nci.formalTypes().size()) return n.arguments(); List<Expr> args = new ArrayList<Expr>(); for (FieldDef fi : fields) { if (curr != null && theLocalClass != null && ts.isEnclosed(curr, theLocalClass)) { // If in the local class being rewritten, use the boxed local (i.e., field) instead of the local. // This could generate a bad constructor call since the field will refer to 'this' before the superclass // is initialized, but we'll patch this up later. Position pos = fi.position(); Field f = nf.Field(pos, makeMissingFieldTarget(fi.asInstance(), pos), nf.Id(pos, fi.name())); f = f.fieldInstance(fi.asInstance()); f = (Field) f.type(fi.asInstance().type()); args.add(f); } else { LocalDef li = localOfField.get(fi); if (li != null) { Local l = nf.Local(li.position(), nf.Id(li.position(), li.name())); l = l.localInstance(li.asInstance()); l = (Local) l.type(li.asInstance().type()); args.add(l); } else { throw new InternalCompilerError("field " + fi + " created with rev map to null", n.position()); } } } args.addAll(n.arguments()); assert args.size() == nci.formalTypes().size(); return args; } Map<FieldDef, LocalDef> localOfField = CollectionFactory.newHashMap(); protected Receiver makeMissingFieldTarget(FieldInstance fi, Position pos) { Receiver r; Context c = context(); if (fi.flags().isStatic()) { r = nf.CanonicalTypeNode(pos, fi.container()); } else { // The field is non-static, so we must prepend with // "this", but we need to determine if the "this" // should be qualified. Get the enclosing class which // brought the field into scope. This is different // from fi.container(). fi.container() returns a super // type of the class we want. ClassType scope = (ClassType) fi.container(); if (! ts.typeEquals(scope, c.currentClass(), context)) { r = nf.This(pos.startOf(), nf.CanonicalTypeNode(pos, scope)).type(scope); } else { r = nf.This(pos.startOf()).type(scope); } } return r; } public static <K,V> V hashGet(Map<K,V> map, K k, V v) { V x = map.get(k); if (x != null) return x; map.put(k, v); return v; } public static <K,V> void hashAdd(Map<K, List<V>> map, K k, V v) { List<V> l = map.get(k); if (l == null) { l = new ArrayList<V>(); map.put(k, l); } l.add(v); } }