/* ____ ____ ____ ____ ______ *\ ** / __// __ \/ __// __ \/ ____/ 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.AbsTypeSymbol; import scalac.symtab.Modifiers; import scalac.symtab.Scope; 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. */ // !!! needs to be cleaned public class ExplicitOuterClassesPhase extends Phase { //######################################################################## // Private Fields /** A map from constructor symbols to type contexts */ private final Map/**/ contexts = new HashMap(); //######################################################################## // Public Constructors /** Initializes this instance. */ public ExplicitOuterClassesPhase(Global global,PhaseDescriptor descriptor){ super(global, descriptor); } //######################################################################## // Public Methods /** Applies this phase to the given compilation units. */ public void apply(Unit[] units) { treeTransformer.apply(units); } private boolean show = false; // !!! /** Applies this phase to the given type for the given symbol. */ public Type transformInfo(Symbol symbol, Type type) { if (show && !symbol.isPackage()) System.out.println("!!! <<< transformInfo - symbol: " + Debug.show(symbol)); if (show && !symbol.isPackage()) System.out.println("!!! <<< transformInfo - type : " + Debug.show(type)); if (symbol.isPackage()) return type; // !!! TypeContext context = getTypeContextFor(symbol); if (symbol.isConstructor() && symbol.constructorClass().isClassType()) { // !!! isClassType -> isClass ? Symbol clasz = symbol.constructorClass(); Symbol[] tparams = type.typeParams(); Symbol[] vparams = type.valueParams(); Type result = type.resultType(); result = context.transformer.apply(result); if (context.vlink != null) { vparams = Symbol.cloneArray(1, vparams); vparams[0] = context.vlink; } Type prefix = clasz.owner().thisType(); Type[] args = Symbol.type(tparams); // !!! use getNewTypeArgs ? Type self = Type.TypeRef(prefix, clasz, args); Type s = self; self = context.transformer.apply(self); tparams = Type.symbol(self.typeArgs()); for (int i = 0; i < tparams.length; i++) assert tparams[i].isParameter() && tparams[i].owner() == symbol: Debug.show(symbol, " -- ", clasz, " -- ", self); type = Type.MethodType(vparams, result); if (tparams.length != 0) type = Type.PolyType(tparams, type); } else { Type t = type; type = context.transformer.apply(type); assert type != null: Debug.show(symbol) + " -- " + t; } if (show && !symbol.isPackage()) System.out.println("!!! >>> transformInfo - symbol: " + Debug.show(symbol)); if (show && !symbol.isPackage()) System.out.println("!!! >>> transformInfo - type : " + Debug.show(type)); return type; } //######################################################################## // Private Methods /** Returns the type context for the given symbol. */ private TypeContext getTypeContextFor(Symbol symbol) { while (!symbol.isClassType() && !(symbol.isConstructor() && symbol.constructorClass().isClassType())) // !!! isClassType -> isClass ? symbol = symbol.owner(); if (symbol.isClassType()) symbol = symbol.primaryConstructor(); TypeContext context = (TypeContext)contexts.get(symbol); if (context == null) { context = createTypeContext(symbol); contexts.put(symbol, context); } return context; } /** Creates the context for the given constructor. */ private TypeContext createTypeContext(Symbol constructor) { Symbol clasz = constructor.constructorClass(); // get outer contexts TypeContext[] outers; if (clasz.isRoot()) { outers = new TypeContext[0]; } else { TypeContext outer = getTypeContextFor(clasz.owner()); outers = new TypeContext[1 + outer.outers.length]; outers[0] = outer; for (int i = 1; i < outers.length; i++) outers[i] = outer.outers[i - 1]; } // create outer type links Symbol[] tlinks = new Symbol[outers.length]; int tflags = Modifiers.PARAM | Modifiers.COVARIANT | Modifiers.SYNTHETIC | Modifiers.STABLE; for (int i = 0; i < outers.length; i++) { if (outers[i].isStable) continue; Name tname = Names.OUTER(constructor, outers[i].clasz); tlinks[i] = new AbsTypeSymbol(constructor.pos, tname, constructor, tflags); tlinks[i].setInfo(outers[i].clasz.type()); } // create outer value link Symbol vlink = null; if (outers.length > 0 && (outers[0].vlink != null || !outers[0].isStable)) { int index = 0; while (outers[index].isStable) index++; int vflags = Modifiers.PARAM | Modifiers.SYNTHETIC; Name vname = Names.OUTER(constructor); vlink = new TermSymbol(constructor.pos, vname, constructor, vflags); vlink.setInfo(outers[index].clasz.thisType()); } // create new type parameters Map tparams = new HashMap(); for (int o = 0; o < outers.length; o++) { Symbol[] oldtparams = outers[o].oldtparams; for (int i = 0; i < oldtparams.length; i++) { Symbol oldtparam = oldtparams[i]; Symbol newtparam = oldtparam.cloneSymbol(constructor); newtparam.name = Names.OUTER(constructor, oldtparam); tparams.put(oldtparam, newtparam.type()); } } // !!! duplicated code Symbol[] oldtparams = constructor.typeParams(); for (int i = 0; i < oldtparams.length; i++) { Symbol oldtparam = oldtparams[i]; Symbol newtparam = oldtparam; tparams.put(oldtparam, newtparam.type()); } return new TypeContext(clasz, outers, tlinks, vlink, constructor.typeParams(), tparams); } /** !!! */ // !!! where is not used // !!! prefix is an old type // !!! args are old types // !!! returns old types private Type[] getNewArgsOf(TypeContext where, Type prefix, Symbol clasz, Type[] args) { TypeContext context = getTypeContextFor(clasz); int vlinks = 0; // !!! for (int i = 0; i < context.outers.length; i++) if (!context.outers[i].isStable) vlinks++; Type[] types = new Type[context.transformer.tparams.size() + vlinks]; int p = types.length; for (int i = args.length; 0 < i; ) types[--p] = args[--i]; for (int o = 0; o < context.outers.length - 1; o++) { if (!context.outers[o].isStable) types[--p] = prefix; else if (context.outers[o].clasz.isPackage()) break; Type base = prefix.baseType(context.outers[o].clasz); assert base.symbol() == context.outers[o].clasz: prefix + " -- " + Debug.show(clasz) + " -- " + context.outers[o].clasz + " -- " + base; prefix = base.prefix(); args = base.typeArgs(); for (int i = args.length; 0 < i; ) types[--p] = args[--i]; } // !!! assert p == 0: p; for (int i = 0; i < types.length; i++) { // !!! assert types[i] != null: "\nprefix = " + prefix + "\nclasz = " + Debug.show(clasz) + "\nargs = " + Debug.show(args) + "\ntypes = " + Debug.show(types) + "\ncontext= " + context; } return types; } //######################################################################## // Private Class - Type transformer context private class TypeContext { /** The context class */ private final Symbol clasz; /** Is this context class stable? */ private final boolean isStable; /** The outer contexts (from innermost to outermost) */ private final TypeContext[] outers; /** The outer type links (null for stable outer contexts) */ private final Symbol[] tlinks; /** The outer value link (null if all outer contexts are stable) */ private final Symbol vlink; /** The old type parameters of the context class */ private Symbol[] oldtparams; /** !!! */ private TypeTransformer transformer; // !!! type /** !!! */ public TypeContext(Symbol clasz, TypeContext[] outers, Symbol[] tlinks, Symbol vlink, Symbol[] oldtparams, Map tparams) { this.clasz = clasz; this.isStable = clasz.isPackage() || clasz.isModuleClass(); this.outers = outers; this.tlinks = tlinks; this.vlink = vlink; this.oldtparams = oldtparams; this.transformer = new TypeTransformer(this, tparams); } /** !!! */ public Type getTypeLink(int level) { if (tlinks.length <= level) return Type.localThisType; if (tlinks[level] != null) return tlinks[level].type(); // !!! if (clasz.isPackage()) return Type.localThisType; return Type.singleType(getTypeLink(level + 1), clasz.module()); } public String toString() { return "\nclasz = " + Debug.show(clasz) + "\nisStable = " + isStable + "\ntlinks = " + Debug.show(tlinks) + "\nvlink = " + Debug.show(vlink) + "\noldparams= " + Debug.show(oldtparams) + "\ntparams = " + Debug.show(transformer.tparams) + (outers.length > 0 ? ("\nouter : " + "\n" + outers[0]) : ""); } } //######################################################################## // Private Class - Type transformer /** The type transformer */ private final class TypeTransformer extends Type.MapOnlyTypes { private TypeContext context; private Map/**/ tparams; public TypeTransformer(TypeContext context, Map tparams) { this.context = context; this.tparams = tparams; } public Type apply(Type type) { switch (type) { case TypeRef(Type prefix, Symbol symbol, Type[] args): if (symbol.isParameter() && symbol.owner().isConstructor()) { assert prefix.equals(Type.localThisType): type; assert args.length == 0: type; Object value = tparams.get(symbol); return value != null ? (Type)value : type; } if (symbol.isClass()) { args = map(getNewArgsOf(context, prefix, symbol, args)); prefix = Type.localThisType; return Type.TypeRef(prefix, symbol, args); } if (symbol.isPackage()) { args = Type.EMPTY_ARRAY; prefix = Type.localThisType; return Type.TypeRef(prefix, symbol, args); } return Type.TypeRef(apply(prefix), symbol, map(args)); case SingleType(Type prefix, Symbol symbol): return Type.singleType(apply(prefix), symbol); case ThisType(Symbol clasz): if (clasz == Symbol.NONE) return type; if (clasz == context.clasz) return type; if (clasz.isPackage()) return Type.localThisType; if (clasz.isModuleClass()) { Type prefix = clasz.owner().thisType(); return Type.singleType(apply(prefix), clasz.module()); } for (int i = 0; i < context.outers.length; i++) if (clasz == context.outers[i].clasz) return context.getTypeLink(i); throw Debug.abort("illegal ThisType", type); case CompoundType(Type[] parents, Scope members): // !!! this case should not be needed return Type.compoundType(map(parents), members, type.symbol()); default: return map(type); } } } //######################################################################## // Private Class - Tree transformer /** The tree transformer */ private final GenTransformer treeTransformer = new GenTransformer(global) { /** The current context */ private Context context; /** True if currently in a method */ public boolean inMethod; /** 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 inMethod = true; rhs = transform(rhs); 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 || inMethod) { Tree qualifier = genOuterRef(tree.pos, clasz); return gen.Select(qualifier, symbol); } } return gen.Ident(tree.pos, symbol); case TypeTerm(): Type type = context.context.transformer.apply(tree.getType()); 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(); vargs = transform(vargs); switch (transform(tree)) { case Select(Tree qualifier, _): if (getTypeContextFor(symbol).vlink != null) { vargs = Tree.cloneArray(1, vargs); vargs[0] = qualifier; Type prefix; // !!! this is done to avoid types like "vlink.type" switch (tree) { case Select(Tree qualifier1, _): prefix = qualifier1.getType(); break; default: throw Debug.abort("illegal case", tree); } Type[] newtargs = getNewArgsOf(context.context, prefix, symbol, Tree.typeOf(targs)); targs = Tree.cloneArray(newtargs.length - targs.length, targs); for (int i = 0; i < newtargs.length; i++) targs[i] = gen.mkType(tapply.pos, newtargs[i]); } } targs = transform(targs); 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); TypeContext tcontext = null; for (int i = 0; i < context.context.outers.length; i++) if (context.context.outers[i].clasz == clasz) tcontext = context.context.outers[i]; assert tcontext != null: Debug.show(clasz, " -- ", context.clasz); if (tcontext.isStable) { if (!clasz.owner().isPackage()) { Tree qualifier = genOuterRef(pos,tcontext.outers[0].clasz); return gen.Select(pos, qualifier, clasz.module()); } else { assert clasz.owner().isPackage(): Debug.show(clasz); return gen.Ident(pos, clasz.module()); } } else { assert context.context.vlink != null: Debug.show(clasz, " -- ", context.clasz); Tree tree = inMethod ? gen.Select(gen.This(pos, context.clasz), context.context.vlink) : gen.Ident(pos, context.context.vlink); Context context = this.context; while (true) { context = context.outer; assert context != null: Debug.show(clasz, " -- ", this.context.clasz); while (context.context.isStable) { context = context.outer; assert context != null: Debug.show(clasz, " -- ", this.context.clasz); } if (context.clasz == clasz) return tree; tree = gen.Select(tree, context.context.vlink); } } } }; //######################################################################## // 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; public final TypeContext context; /** Initializes this instance. */ public Context(Context outer, Symbol symbol, Map supers) { this.context = getTypeContextFor(symbol); this.outer = outer; this.clasz = symbol.constructorClass(); this.supers = supers; } /** Returns a context for the given constructor. */ public Context getConstructorContext(Symbol constructor) { assert constructor.constructorClass() == clasz; return new Context(outer, constructor, supers); } } //######################################################################## }