/* ____ ____ ____ ____ ______ *\ ** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** ** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002-2005, LAMP/EPFL ** ** /_____/\____/\___/\____/____/ ** \* */ // $Id$ // TODO find a good way to change symbol flags (PRIVATE, DEFERRED, // INTERFACE). In particular find how to make sure that the // modifications are not made too early, for example before the symbol // is cloned. package scalac.transformer; import java.util.Map; import java.util.HashMap; import scalac.Global; import scalac.ast.GenTransformer; import scalac.ast.Tree; import scalac.ast.Tree.Template; import scalac.ast.TreeGen; import scalac.ast.TreeList; import scalac.symtab.Type; import scalac.symtab.Scope; import scalac.symtab.Symbol; import scalac.symtab.SymbolSubstTypeMap; import scalac.util.Debug; /** * Add, for each class, an interface with the same name, to be used * later by mixin expansion. More specifically: * * - at the end of the name of every class, the string "$class" is * added, * * - an interface with the original name of the class is created, and * contains all directly bound members of the class (as abstract * members), * * - the interface is added to the mixin base classes of the class. * * @author Michel Schinz * @version 1.0 */ public class AddInterfaces extends GenTransformer { //######################################################################### // Private Fields /** The AddInterface phase */ private final AddInterfacesPhase phase; /** The current class (null is none) */ private Symbol clasz; /** The current member (null is none) */ private Symbol member; /** The current class substitution (null is none) */ private Type.Map classSubst; /** The current parameter substitution (null is none) */ private SymbolSubstTypeMap paramSubst; //######################################################################### // Public Constructors /** Initializes this instance. */ public AddInterfaces(Global global, AddInterfacesPhase phase) { super(global); this.phase = phase; } //######################################################################### // Public Methods /** Transforms the given symbol. * * @param tree * @return */ public Symbol getSymbolFor(Tree tree) { switch (tree) { case Create(_, _): return phase.getClassSymbol(tree.symbol()); case Return(_): return member; case This(_): case Super(_, _): return clasz; case Select(Super(_, _), _): Symbol symbol = tree.symbol(); if (symbol.isInitializer()) return getClassMember(symbol); return getClassMember(symbol, true); case Select(_, _): Symbol symbol = tree.symbol(); if (symbol.isInitializer()) return getClassMember(symbol); return symbol; case Ident(_): Symbol symbol = tree.symbol(); if (symbol.isInitializer()) return getClassMember(symbol); if (symbol.isParameter() && !symbol.owner().isStatic()) return getClassVParam(symbol); return symbol; default: return tree.symbol(); } } /** Transforms the given type. */ public Type transform(Type type) { if (classSubst != null) type = classSubst.apply(type); if (paramSubst != null) type = paramSubst.apply(type); return type; } /** Transforms the given trees. */ public Tree[] transform(Tree[] trees) { if (member != null) return super.transform(trees); TreeList list = new TreeList(); for (int i = 0; i < trees.length; i++) template(list, trees[i]); return list.toArray(); } /** Transforms the given tree. */ public Tree transform(Tree tree) { switch (tree) { case ValDef(_, _, _, _): case LabelDef(_, _, _): Symbol symbol = tree.symbol(); if (symbol.owner() != member) { symbol.updateInfo(transform(symbol.info())); symbol.setOwner(member); } return super.transform(tree); case Select(Tree qualifier, _): Type prefix = qualifier.type(); qualifier = transform(qualifier); Symbol symbol = getSymbolFor(tree); if (symbol.isJava() && !symbol.owner().isInterface()) { if (qualifier.type().widen().symbol().isInterface()) { Type baseType = prefix.baseType(symbol.owner()); assert baseType != Type.NoType: tree; qualifier = gen.mkAsInstanceOf(qualifier, baseType); } } return gen.Select(tree.pos, qualifier, symbol); default: return super.transform(tree); } } //######################################################################### // Private Methods /** Transforms the given template and adds it to given list. * * @param trees * @param tree */ private void template(TreeList trees, Tree tree) { switch (tree) { case Empty: return; case PackageDef(_, _): trees.append(super.transform(tree)); return; case ClassDef(_, _, _, _, _, Template(_, Tree[] body)): TreeList list = new TreeList(transform(body)); this.clasz = tree.symbol(); Map methods = new HashMap(); if (phase.needInterface(clasz)) { Symbol clone = phase.getClassSymbol(clasz); trees.append(getClassTree(clasz, list, methods)); list = new TreeList(); this.classSubst = new Type.SubstThisMap(clasz, clone); this.paramSubst = phase.getClassSubst(clone); this.clasz = clone; } for (int i = 0; i < body.length; i++) member(methods, body[i]); trees.append(getClassTree(clasz, list, methods)); assert methods.isEmpty(): Debug.show(methods.keySet().toArray()); this.paramSubst = null; this.classSubst = null; this.clasz = null; return; case DefDef(_, _, _, _, _, _): case ValDef(_, _, _, _): return; default: throw Debug.abort("illegal tree", tree); } } /** * Transforms the given class member. Methods with a non-empty * body are added to the given method map. All other members are * dropped. * * @param methods * @param tree */ private void member(Map methods, Tree tree) { switch (tree) { case ClassDef(_, _, _, _, _, _): return; case DefDef(_, _, _, _, _, Tree rhs): if (rhs == Tree.Empty) return; Symbol symbol = tree.symbol(); this.member = getClassMember(symbol); if (member != symbol) { paramSubst.insertSymbol( symbol.typeParams(), member.nextTypeParams()); paramSubst.insertSymbol( symbol.valueParams(), member.nextValueParams()); } methods.put(member, gen.DefDef(member, transform(rhs))); if (member != symbol) { paramSubst.removeSymbol(symbol.valueParams()); paramSubst.removeSymbol(symbol.typeParams()); } this.member = null; return; case ValDef(_, _, _, Tree rhs): assert rhs == Tree.Empty: tree; return; default: throw Debug.abort("illegal tree", tree); } } /** * Returns the tree of the given class. Its body is built by * adding its members to the provided body. Non-abstract methods * are removed from the provided method map. All other members are * generated from their symbol. * * @param clasz * @param body * @param methods * @return */ private Tree getClassTree(Symbol clasz, TreeList body, Map methods) { Scope members = clasz.nextInfo().members(); for (Scope.SymbolIterator i = members.iterator(); i.hasNext(); ) { Symbol member = i.next(); if (!member.isTerm()) continue; body.append(getMemberTree(clasz, member, methods)); } Tree[] array = body.toArray(); if (!clasz.isInterface()) phase.classToBody.put(clasz, array); return gen.ClassDef(clasz, array); } /** * Returns the tree of the given member. Non-abstract methods are * removed from the given method map. All other members are * generated from their symbol. * * @param clasz * @param member * @param methods * @return */ private Tree getMemberTree(Symbol clasz, Symbol member, Map methods) { if (!member.isMethod()) return gen.ValDef(member, Tree.Empty); if (member.isDeferred()) return gen.DefDef(member, Tree.Empty); Tree method = (Tree)methods.remove(member); assert method != null: Debug.show(clasz + "." + member + ":" + member.info() + member.locationString()); return method; } /** Returns the symbol of given parameter in current class. * * @param vparam * @return */ private Symbol getClassVParam(Symbol vparam) { if (paramSubst == null) return vparam; Symbol clone = (Symbol)paramSubst.lookupSymbol(vparam); assert clone != null: Debug.show(vparam, clasz, member); return clone; } /** Returns the symbol of given member in current class. */ private Symbol getClassMember(Symbol member) { return getClassMember(member, false); } // !!! Try to remove version with lazy argument. It is currently // needed for super calls to abstract method (possible in mixins). private Symbol getClassMember(Symbol member, boolean lazy) { Symbol owner = member.owner(); assert owner.isClass(): Debug.show(member); if (!phase.needInterface(owner)) return member; Symbol clasz = phase.getClassSymbol(owner); Symbol clone = (Symbol)phase.getClassMemberMap(clasz).get(member); assert clone != null || lazy: Debug.show(member, " not in ", clasz); return clone != null ? clone : member; } //######################################################################### }