/* ____ ____ ____ ____ ______ *\ ** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** ** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** ** /_____/\____/\___/\____/____/ ** \* */ // $Id$ package scalac.transformer; import java.util.Map; import java.util.HashMap; import java.util.Iterator; import scalac.Global; import scalac.Phase; import scalac.PhaseDescriptor; import scalac.Unit; import scalac.ast.GenTransformer; import scalac.ast.Tree; import scalac.ast.Tree.Ident; import scalac.ast.Tree.Template; import scalac.symtab.Modifiers; import scalac.symtab.Symbol; import scalac.symtab.TermSymbol; import scalac.symtab.Type; import scalac.util.Debug; import scalac.util.Name; import scalac.util.Names; /** * This phase does the following: * * - In every nested class, adds to each of its constructor a new * value parameter that contains a link to the outer class. * * - In every nested type, adds to each of its constructor a new type * parameter for every type parameter appearing in outer types. * * - In every class, adds a forwarding "super" method for every method * that is accessed via "super" in a nested class. * * - Replaces all prefixes of TypeRefs by localThisTypes. * * - Adds all missing qualifiers. */ public class ExplicitOuterClassesPhase extends Phase { //######################################################################## // Public Constructors /** Initializes this instance. */ public ExplicitOuterClassesPhase(Global global,PhaseDescriptor descriptor){ super(global, descriptor); } //######################################################################## // Public Methods /** Applies this phase to the given type for the given symbol. */ public Type transformInfo(Symbol symbol, Type type) { if (symbol.isPackage()) return type; // if (!symbol.isJava() && symbol.isConstructor()) // !!! //System.out.println("!!! " + Debug.show(symbol) + ": " + type + " -> " + typeTransformer.apply(type)); type = typeTransformer.apply(type); if (symbol.isJava()) return type; if (symbol.isConstructor()) { Symbol[] tparams = type.typeParams(); Symbol[] vparams = type.valueParams(); Type result = type.resultType(); // Add outer value link if (hasOuterValueLink(symbol)) { Name name = Names.OUTER(symbol); Symbol vlink = new TermSymbol(symbol.pos, name, symbol, 0); vlink.setInfo(getOuterClass(symbol).typeOfThis()); // !!! vparams = Symbol.cloneArray(1, vparams); vparams[0] = vlink; } // Add outer type links if (hasOuterTypeLinks(symbol)) { Symbol[] oldtparams = getOuterClass(symbol).nextTypeParams(); Symbol[] tlinks = Symbol.cloneArray(oldtparams); for (int i = 0; i < tlinks.length; i++) { tlinks[i] = oldtparams[i].cloneSymbol(symbol); tlinks[i].name = Names.OUTER(symbol, oldtparams[i]); } tparams = Symbol.concat(tlinks, tparams); result = Type.getSubst(oldtparams, tlinks, true).apply(result); } type = Type.MethodType(vparams, result); if (tparams.length != 0) type = Type.PolyType(tparams, type); } else { Symbol owner = symbol.owner().isConstructor() ? symbol.owner() : symbol.enclClass(); //System.out.println("!!! debug2 = " + Debug.show(symbol) + " - " + Debug.show(owner) + " --- " + (owner == Symbol.NONE) + " -- #" + owner.name + "#"); //if (onwer.isJava() && owner != Symbol.NONE && owner.name.length() > 0) // !!! if (owner.isType() || owner.isConstructor()) type = getOuterTypeSubst(owner, true).apply(type); } /* String s1 = Debug.show(symbol); String s2 = symbol.info().toString(); symbol.updateInfo(type); global.nextPhase(); String s3 = type.toString(); global.prevPhase(); System.out.println("!!! symbol = " + s1); System.out.println("!!! type = " + s2 + " -- " + System.identityHashCode(s2)); System.out.println("!!! new = " + s3 + " -- " + System.identityHashCode(s3)); System.out.println("!!!"); */ return type; } /** Applies this phase to the given compilation units. */ public void apply(Unit[] units) { treeTransformer.apply(units); } //######################################################################## // Private Methods - Outer class /** Returns the outer class of the given type or constructor. */ private Symbol getOuterClass(Symbol symbol) { assert symbol.isType() || symbol.isConstructor(): Debug.show(symbol); return symbol.owner(); } /** Has the given class or constructor an outer value link? */ private boolean hasOuterValueLink(Symbol symbol) { assert symbol.isClass() || symbol.isConstructor(): Debug.show(symbol); return !symbol.isJava() && getOuterClass(symbol).isClass(); } /** Returns the outer link of the given class or constructor. */ private Symbol getOuterValueLink(Symbol symbol) { if (!hasOuterValueLink(symbol)) return Symbol.NONE; if (symbol.isClass()) symbol = symbol.primaryConstructor(); return symbol.nextValueParams()[0]; } /** Has the given type or constructor outer type links? */ private boolean hasOuterTypeLinks(Symbol symbol) { assert symbol.isType() || symbol.isConstructor(): Debug.show(symbol); if (symbol.isJava()) return false; Symbol outer = getOuterClass(symbol); return outer.isType() && outer.nextTypeParams().length != 0; } /** Returns the type substitution for the given class or constructor. */ private Type.Map getOuterTypeSubst(Symbol symbol, boolean update) { if (!hasOuterTypeLinks(symbol)) return Type.IdMap; Symbol[] oldtparams = getOuterTypeParams(symbol); Symbol[] newtparams = symbol.nextTypeParams(); Symbol[] tlinks = new Symbol[oldtparams.length]; for (int i = 0; i < tlinks.length; i++) tlinks[i] = newtparams[i]; return Type.getSubst(oldtparams, tlinks, update); } /** Returns the outer type parameters of the given class or constructor. */ private Symbol[] getOuterTypeParams(Symbol symbol) { Symbol outer = getOuterClass(symbol); Symbol[] tparams = Symbol.cloneArray(outer.nextTypeParams()); for (int i = tparams.length; i != 0; outer = getOuterClass(outer)) { Symbol[] symbols = outer.typeParams(); for (int j = symbols.length; j != 0; ) tparams[--i] = symbols[--j]; } return tparams; } /** * Extracts from given prefix the outer type arguments for the * given class or constructor. */ private Type[] getOuterTypeArgs(Type prefix, Symbol symbol) { if (!hasOuterTypeLinks(symbol)) return Type.EMPTY_ARRAY; Symbol outer = getOuterClass(symbol); global.nextPhase(); Type[] targs = prefix.baseType(outer).widen().typeArgs(); global.prevPhase(); assert targs.length == outer.nextTypeParams().length: "\nsymbol = " + Debug.show(symbol) + "\nprefix = " + prefix; return targs; } //######################################################################## // Private Class - Type transformer /** The type transformer */ private final Type.Map typeTransformer = new Type.MapOnlyTypes() { public Type apply(Type type) { switch (type) { case TypeRef(Type prefix, Symbol symbol, Type[] targs): if (!symbol.owner().isType()) break; prefix = apply(prefix); targs = map(targs); targs = Type.concat(getOuterTypeArgs(prefix, symbol), targs); if (symbol.isClassType()) { // !!! add assertions ? prefix = Type.localThisType; } else { // !!! replace outer ThisTypes by SingleTypes to outer link } return Type.TypeRef(prefix, symbol, targs); } return map(type); } }; //######################################################################## // Private Class - Tree transformer /** The tree transformer */ private final GenTransformer treeTransformer = new GenTransformer(global) { /** The current context */ private Context context; /** Transforms the given tree. */ public Tree transform(Tree tree) { switch (tree) { case ClassDef(_, _, _, _, _, Template impl): Symbol clasz = tree.symbol(); context = new Context(context, clasz, new HashMap()); Tree[] parents = transform(impl.parents); Tree[] body = transform(impl.body); body = Tree.concat(body, genSuperMethods()); context = context.outer; if (context != null) clasz.flags |= Modifiers.STATIC; return gen.ClassDef(clasz, parents, impl.symbol(), body); case DefDef(_, _, _, _, _, Tree rhs): Symbol method = tree.symbol(); Context backup = context; if (method.isConstructor()) context = context.getConstructorContext(method); else context.inMethod = true; rhs = transform(rhs); context.inMethod = false; context = backup; return gen.DefDef(method, rhs); case AbsTypeDef(_, _, _, _): case AliasTypeDef(_, _, _, _): // eliminate // !!! return Tree.Empty; case Typed(Tree expr, Tree tpe): // eliminate // !!! return transform(expr); case Apply(Tree vfun, Tree[] vargs): switch (vfun) { case TypeApply(Tree tfun, Tree[] targs): if (!tfun.symbol().isConstructor()) break; return transform(tree, vargs, vfun, targs, tfun); default: if (!vfun.symbol().isConstructor()) break; return transform(tree, vargs, vfun, Tree.EMPTY_ARRAY,vfun); } return super.transform(tree); case This(_): return genOuterRef(tree.pos, tree.symbol()); case Select(Tree qualifier, _): Symbol symbol = tree.symbol(); switch (qualifier) { case Super(_, _): Symbol clasz = qualifier.symbol(); if (clasz == context.clasz) { qualifier = gen.Super(tree.pos, qualifier.symbol()); } else { qualifier = genOuterRef(qualifier.pos, clasz); symbol = getSuperMethod(clasz, symbol); } break; default: qualifier = transform(qualifier); break; } return gen.Select(tree.pos, qualifier, symbol); case Ident(_): Symbol symbol = tree.symbol(); Symbol owner = symbol.owner(); if (owner.isClass()) { // !!! A this node is missing here. This should // never happen if all trees were correct. Tree qualifier = genOuterRef(tree.pos, owner); return gen.Select(qualifier, symbol); } if (owner.isPrimaryConstructor()) { Symbol clasz = owner.constructorClass(); if (clasz != context.clasz || context.inMethod) { Tree qualifier = genOuterRef(tree.pos, clasz); return gen.Select(qualifier, symbol); } } return gen.Ident(tree.pos, symbol); case TypeTerm(): Type type = typeTransformer.apply(tree.getType()); if (context != null) type = context.subst.apply(type); return gen.TypeTerm(tree.pos, type); default: return super.transform(tree); } } /* Add outer type and value arguments to constructor calls. */ private Tree transform(Tree vapply, Tree[] vargs, Tree tapply, Tree[] targs, Tree tree) { Symbol symbol = tree.symbol(); targs = transform(targs); vargs = transform(vargs); switch (transform(tree)) { case Select(Tree qualifier, _): if (hasOuterValueLink(symbol)) { vargs = Tree.cloneArray(1, vargs); vargs[0] = qualifier; } Type[] types = getOuterTypeArgs(qualifier.getType(), symbol); if (types.length != 0) { targs = Tree.cloneArray(types.length, targs); for (int i = 0; i < types.length; i++) targs[i] = gen.mkType(tapply.pos, types[i]); } } tree = gen.Ident(tree.pos, symbol); if (targs.length != 0) tree = gen.TypeApply(tapply.pos,tree,targs); return gen.Apply(vapply.pos, tree, vargs); } /** * Returns the forwarding "super" method of the given class * that invokes the given method. If the symbol does not yet * exist, it is created and added to the class members. */ private Symbol getSuperMethod(Symbol clasz, Symbol method) { Context context = this.context; for (; context.clasz != clasz; context = context.outer) assert context.outer != null : Debug.show(clasz); Symbol forward = (Symbol)context.supers.get(method); if (forward == null) { Name name = Names.SUPER(method); int flags = Modifiers.PRIVATE | Modifiers.FINAL; forward = new TermSymbol(method.pos, name, clasz, flags); forward.setInfo(method.nextType().cloneType(method, forward)); context.supers.put(method, forward); clasz.nextInfo().members().enter(forward); assert Debug.log("created forwarding method: ", forward); } return forward; } /** Generates the trees of the forwarding "super" methods. */ private Tree[] genSuperMethods() { if (context.supers.size() == 0) return Tree.EMPTY_ARRAY; Tree[] trees = new Tree[context.supers.size()]; Iterator entries = context.supers.entrySet().iterator(); for (int i = 0; i < trees.length; i++) { Map.Entry entry = (Map.Entry)entries.next(); Symbol method = (Symbol)entry.getKey(); Symbol forward = (Symbol)entry.getValue(); int pos = forward.pos; Tree[] targs = gen.mkTypeRefs(pos, forward.nextTypeParams()); Tree[] vargs = gen.mkRefs(pos, forward.nextValueParams()); Tree fun = gen.Select(gen.Super(pos, context.clasz), method); trees[i] = gen.DefDef(forward, gen.mkApplyTV(fun,targs,vargs)); } return trees; } /** Returns a tree referencing the given outer class. */ private Tree genOuterRef(int pos, Symbol clasz) { if (context.clasz == clasz) return gen.This(pos, clasz); Tree tree = context.inMethod ? gen.Select(gen.This(pos, context.clasz), context.link) : gen.Ident(pos, context.link); for (Context context = this.context.outer;;context =context.outer){ assert context != null: Debug.show(clasz); if (context.clasz == clasz) return tree; tree = gen.Select(tree, context.link); } } }; //######################################################################## // Private Class - Tree transformer context /** This class represents the tree transformation context. */ private class Context { /** The outer context */ public final Context outer; /** The current class symbol */ public final Symbol clasz; /** The super methods (maps invoked to forwarding methods) */ public final Map/**/ supers; /** The outer type parameter substitution */ public final Type.Map subst; /** The link to the outer class */ public final Symbol link; /** True if in a method of current class */ public boolean inMethod; /** Initializes this instance. */ public Context(Context outer, Symbol symbol, Map supers) { this.outer = outer; this.clasz = symbol.constructorClass(); this.supers = supers; this.subst = getOuterTypeSubst(symbol, false); this.link = getOuterValueLink(symbol); this.inMethod = false; } /** Returns a context for the given constructor. */ public Context getConstructorContext(Symbol constructor) { assert constructor.constructorClass() == clasz; return new Context(outer, constructor, supers); } } //######################################################################## }