/* * 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.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import polyglot.ast.*; import polyglot.frontend.Job; import polyglot.main.Reporter; import polyglot.types.*; import polyglot.util.InternalCompilerError; import polyglot.util.Pair; import polyglot.util.Position; import polyglot.util.UniqueID; import polyglot.util.CollectionUtil; 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 InnerClassRemover extends ContextVisitor { // Name of field used to carry a pointer to the enclosing class. public static final Name OUTER_FIELD_NAME = Name.make("out$"); public InnerClassRemover(Job job, TypeSystem ts, NodeFactory nf) { super(job, ts, nf); } protected Map<ClassDef, FieldDef> outerFieldInstance = CollectionFactory.newHashMap(); /** Get a reference to the enclosing instance of the current class that is of type containerClass */ Expr getContainer(Position pos, Expr this_, ClassType currentClass, ClassType containerClass) { if (containerClass.def() == currentClass.def()) return this_; pos = pos.markCompilerGenerated(); ClassType currentContainer = currentClass.container().toClass(); FieldDef fi = boxThis(currentClass, currentContainer); Field f = nf.Field(pos, this_, nf.Id(pos, OUTER_FIELD_NAME)); f = f.fieldInstance(fi.asInstance()); f = (Field) f.type(fi.asInstance().type()); f = f.targetImplicit(false); return getContainer(pos, f, currentContainer, containerClass); } protected abstract ContextVisitor localClassRemover(); public Node override(Node parent, Node n) { if (n instanceof SourceFile) { ContextVisitor lcv = localClassRemover(); lcv = (ContextVisitor) lcv.begin(); lcv = (ContextVisitor) lcv.context(context); if (reporter.should_report(Reporter.innerremover, 1)) { System.out.println(">>> output ----------------------"); n.prettyPrint(System.out); System.out.println("<<< output ----------------------"); } n = n.visit(lcv); if (reporter.should_report(Reporter.innerremover, 1)) { System.out.println(">>> locals removed ----------------------"); n.prettyPrint(System.out); System.out.println("<<< locals removed ----------------------"); } n = this.visitEdgeNoOverride(parent, n); if (reporter.should_report(Reporter.innerremover, 1)) { System.out.println(">>> inners removed ----------------------"); n.prettyPrint(System.out); System.out.println("<<< inners removed ----------------------"); } return n; } else { return null; } } protected Node leaveCall(Node old, Node n, NodeVisitor v) { if (n instanceof Special) { return fixSpecial((Special) n); } else if (n instanceof Field) { return fixField((Field) n); } else if (n instanceof FieldAssign) { return fixFieldAssign((FieldAssign) n); } else if (n instanceof Call) { return fixCall((Call) n); } else if (n instanceof New) { return fixNew((New) n); } else if (n instanceof ConstructorCall) { return fixConstructorCall((ConstructorCall) n); } else if (n instanceof ClassDecl) { return fixClassDecl((ClassDecl) n); } else if (n instanceof TypeNode) { return fixTypeNode((TypeNode) n); } return n; } protected Expr fixSpecial(Special s) { if (s.qualifier() == null) return s; assert s.qualifier().type().toClass() != null; Context context = this.context(); if (s.qualifier().type().toClass().def() == context.currentClassDef()) return s; Position pos = s.position().markCompilerGenerated(); return getContainer(pos, nf.This(pos).type(context.currentClass()), context.currentClass(), s.qualifier().type().toClass()); } protected Node fixField(Field f) { if (f.isTargetImplicit() && !(f.target() instanceof Special)) return f.targetImplicit(false); return f; } protected Node fixFieldAssign(FieldAssign f) { if (f.targetImplicit() && !(f.target() instanceof Special)) return f.targetImplicit(false); return f; } protected Node fixCall(Call c) { if (c.isTargetImplicit() && !(c.target() instanceof Special)) return c.targetImplicit(false); return c; } public TypeNode fixTypeNode(TypeNode tn) { return tn; } public New fixNew(New neu) { Expr q = neu.qualifier(); if (q == null) return neu; // Add the qualifier as an argument to constructor invocations. neu = neu.qualifier(null); ConstructorInstance ci = neu.constructorInstance(); // Fix the ci. List<Type> argTypes = new ArrayList<Type>(); argTypes.add(q.type()); argTypes.addAll(ci.formalTypes()); ci = ci.formalTypes(argTypes); neu = neu.constructorInstance(ci); List<Expr> args = new ArrayList<Expr>(); args.add(q); args.addAll(neu.arguments()); neu = (New) neu.arguments(args); return neu; } protected Node fixConstructorCall(ConstructorCall cc) { // Add the qualifier as an argument to constructor invocations. if (cc.kind() != ConstructorCall.SUPER) { // FIXME: [IP] Why? What about calls to this()? return cc; } ConstructorInstance ci = cc.constructorInstance(); ClassType ct = ci.container().toClass(); // NOTE: we require that a constructor call to a non-static member have a qualifier. // We can't check for this now, though, since the type information may already have been // rewritten. // // Add a qualifier to non-static member class super() calls if not present. // if (ct.isMember() && ! ct.flags().isStatic()) { // if (cc.qualifier() == null) { // cc = cc.qualifier(nf.This(pos).type(context.currentClass())); // } // } if (cc.qualifier() == null) { return cc; } Expr q = cc.qualifier(); cc = cc.qualifier(null); boolean fixCI = cc.arguments().size()+1 != ci.formalTypes().size(); // if (q == null) { // if (ct.isMember() && ! ct.flags().isStatic()) { // q = getContainer(pos, nf.Special(pos, Special.THIS).type(context.currentClass()), context.currentClass(), ct); // } // else if (ct.isMember()) { // // might have already been rewritten to static. If so, the CI should have been rewritten also. // if (((ConstructorInstance) ci.declaration()).formalTypes().size() >= cc.arguments().size()) { // q = nf.Special(pos, Special.THIS).type(context.currentClass()); // } // } // } // Fix the ci if a copy; otherwise, let the ci be modified at the declaration node. if (fixCI) { List<Type> args = new ArrayList<Type>(); args.add(q.type()); args.addAll(ci.formalTypes()); ci = ci.formalTypes(args); cc = cc.constructorInstance(ci); } List<Expr> args = new ArrayList<Expr>(); args.add(q); args.addAll(cc.arguments()); cc = (ConstructorCall) cc.arguments(args); return cc; } public static boolean isInner(ClassDef def) { return def.isMember() && (!def.flags().isStatic() || def.wasInner()); } protected ClassDecl fixClassDecl(ClassDecl cd) { if (cd.classDef().isMember() && !cd.flags().flags().isStatic()) { cd.classDef().setWasInner(true); cd.classDef().flags(cd.classDef().flags().Static()); Flags f = cd.classDef().flags(); cd = cd.flags(cd.flags().flags(f)); Context context = this.context(); assert (Types.get(cd.classDef().container()).toClass().def() == context.currentClass().def()); // Add a field for the enclosing class. ClassType ct = context.currentClass(); FieldDef fi = boxThis(cd.classDef().asType(), ct); cd = addFieldsToClass(cd, Collections.singletonList(fi), ts, nf, true); cd = fixQualifiers(cd); } return cd; } public ClassDecl fixQualifiers(ClassDecl cd) { return (ClassDecl) cd.visitChildren(new NodeVisitor() { LocalDef li; public Node override(Node parent, Node n) { if (n instanceof ClassBody) { return null; } if (n instanceof ConstructorDecl) { return null; } if (parent instanceof ConstructorDecl && n instanceof Formal) { Formal f = (Formal) n; LocalDef li = f.localDef(); if (li.name().equals(OUTER_FIELD_NAME)) { this.li = li; } return n; } if (parent instanceof ConstructorDecl && n instanceof Block) { return null; } if (parent instanceof Block && n instanceof ConstructorCall) { return null; } if (parent instanceof ConstructorCall) { return null; } return n; // // if (n instanceof ClassMember) { // this.li = null; // return n; // } // // return null; } public Node leave(Node parent, Node old, Node n, NodeVisitor v) { if (parent instanceof ConstructorCall && li != null && n instanceof Expr) { return fixQualifier((Expr) n, li); } return n; } }); } public Expr fixQualifier(Expr e, final LocalDef li) { return (Expr) e.visit(new NodeVisitor() { public Node leave(Node old, Node n, NodeVisitor v) { if (n instanceof Field) { Field f = (Field) n; if (f.target() instanceof Special) { Special s = (Special) f.target(); if (s.kind() == Special.THIS && f.name().id().equals(OUTER_FIELD_NAME)) { Local l = nf.Local(n.position().markCompilerGenerated(), f.name()); l = l.localInstance(li.asInstance()); l = (Local) l.type(li.asInstance().type()); return l; } } } if (n instanceof FieldAssign) { FieldAssign f = (FieldAssign) n; if (f.target() instanceof Special) { Special s = (Special) f.target(); if (s.kind() == Special.THIS && f.name().id().equals(OUTER_FIELD_NAME)) { Local l = nf.Local(n.position().markCompilerGenerated(), f.name()); l = l.localInstance(li.asInstance()); l = (Local) l.type(li.asInstance().type()); return l; } } } return n; } }); } public ClassDecl addFieldsToClass(ClassDecl cd, List<FieldDef> newFields, TypeSystem ts, NodeFactory nf, boolean rewriteMembers) { if (newFields.isEmpty()) { return cd; } ClassBody b = cd.body(); // Add the new fields to the class. List<ClassMember> newMembers = new ArrayList<ClassMember>(); for (Iterator<FieldDef> i = newFields.iterator(); i.hasNext(); ) { FieldDef fi = i.next(); Position pos = fi.position().markCompilerGenerated(); FieldDecl fd = nf.FieldDecl(pos, nf.FlagsNode(pos, fi.flags()), nf.CanonicalTypeNode(pos, fi.type()), nf.Id(pos, fi.name())); fd = fd.fieldDef(fi); newMembers.add(fd); } for (Iterator<ClassMember> i = b.members().iterator(); i.hasNext(); ) { ClassMember m = i.next(); if (m instanceof ConstructorDecl) { ConstructorDecl td = (ConstructorDecl) m; // Create a list of formals to add to the constructor. List<Formal> formals = new ArrayList<Formal>(); List<LocalDef> locals = new ArrayList<LocalDef>(); for (FieldDef fi : newFields) { Position pos = fi.position().markCompilerGenerated(); LocalDef li = ts.localDef(pos, Flags.FINAL, fi.type(), fi.name()); li.setNotConstant(); Formal formal = nf.Formal(pos, nf.FlagsNode(pos, li.flags()), nf.CanonicalTypeNode(pos, li.type()), nf.Id(pos, li.name())); formal = formal.localDef(li); formals.add(formal); locals.add(li); } List<Formal> newFormals = new ArrayList<Formal>(); newFormals.addAll(formals); newFormals.addAll(td.formals()); td = td.formals(newFormals); // Create a list of field assignments. List<Stmt> statements = new ArrayList<Stmt>(); for (int j = 0; j < newFields.size(); j++) { FieldDef fi = newFields.get(j); LocalDef li = formals.get(j).localDef(); Position pos = fi.position().markCompilerGenerated(); Local l = nf.Local(pos, nf.Id(pos, li.name())); l = (Local) l.type(li.asInstance().type()); l = l.localInstance(li.asInstance()); FieldAssign a = nf.FieldAssign(pos, nf.This(pos).type(fi.asInstance().container()), nf.Id(pos, fi.name()), Assign.ASSIGN, l); a = (FieldAssign) a.type(fi.asInstance().type()); a = (FieldAssign) a.fieldInstance(fi.asInstance()); a = a.targetImplicit(false); Eval e = nf.Eval(pos, a); statements.add(e); } // Add the assignments to the constructor body after the super call. // Or, add pass the locals to another constructor if a this call. Block block = td.body(); if (block.statements().size() > 0) { Stmt s0 = (Stmt) block.statements().get(0); if (s0 instanceof ConstructorCall) { ConstructorCall cc = (ConstructorCall) s0; ConstructorInstance ci = cc.constructorInstance(); if (cc.kind() == ConstructorCall.THIS) { // Not a super call. Pass the locals as arguments. List<Expr> arguments = new ArrayList<Expr>(); for (Iterator<Stmt> j = statements.iterator(); j.hasNext(); ) { Stmt si = j.next(); Eval e = (Eval) si; Assign a = (Assign) e.expr(); arguments.add(a.right()); } // Modify the CI if it is a copy of the declaration CI. // If not a copy, it will get modified at the declaration. { List<Type> newFormalTypes = new ArrayList<Type>(); for (int j = 0; j < newFields.size(); j++) { FieldDef fi = newFields.get(j); newFormalTypes.add(fi.asInstance().type()); } newFormalTypes.addAll(ci.formalTypes()); ci = ci.formalTypes(newFormalTypes); cc = cc.constructorInstance(ci); } arguments.addAll(cc.arguments()); cc = (ConstructorCall) cc.arguments(arguments); } else { // A super call. Don't rewrite it here; the visitor will handle it elsewhere. } // prepend the super call statements.add(0, cc); } else { // [DC] The absence of this case in previous versions was a bug // but I believe it would only have caused a problem with the root (Object) // which was not an inner class (and was @NativeRep anyway) statements.add(s0); } statements.addAll(block.statements().subList(1, block.statements().size())); } else { statements.addAll(block.statements()); } block = block.statements(statements); td = (ConstructorDecl) td.body(block); newMembers.add(td); adjustConstructorFormals(td.constructorDef(), newFormals); } else { newMembers.add(m); } } b = b.members(newMembers); return cd.body(b); } protected void adjustConstructorFormals(ConstructorDef ci, List<Formal> newFormals) { List<Ref<? extends Type>> newFormalTypes = new ArrayList<Ref<? extends Type>>(); for (Formal f : newFormals) { newFormalTypes.add(f.type().typeRef()); } ci.setFormalTypes(newFormalTypes); } // Add local variables to the argument list until it matches the declaration. List<Expr> addArgs(ProcedureCall n, ConstructorInstance nci, Expr q) { if (nci == null || q == null) return n.arguments(); List<Expr> args = new ArrayList<Expr>(); args.add(q); args.addAll(n.arguments()); assert args.size() == nci.formalTypes().size(); return args; } // Create a field instance for a qualified this. protected FieldDef boxThis(ClassType currClass, ClassType outerClass) { FieldDef fi = outerFieldInstance.get(currClass.def()); if (fi != null) return fi; Position pos = outerClass.position(); fi = ts.fieldDef(pos, Types.ref(currClass), Flags.FINAL.Private(), Types.ref(outerClass), OUTER_FIELD_NAME); fi.setNotConstant(); currClass.def().addField(fi); // FIXME: should boxThis() be idempotent? outerFieldInstance.put(currClass.def(), fi); return fi; } public static <K,V> V hashGet(Map<K,V> map, K k, V v) { return LocalClassRemover.<K,V>hashGet(map, k, v); } }