summaryrefslogblamecommitdiff
path: root/sources/scalac/transformer/AddInterfaces.java
blob: 5722cf43968b970ef09386531614d10b134c8982 (plain) (tree)
1
2
3
4
5
6
7
8



                                                                          

                                                                          

       




                                                                      

                           













                                        
















                                                                     
                                                   
 

                                                                               
 

                                           
 

                                           
 

                                            
 

                                                        
 

                                                            
 

                                                                               
 



                                                                   

     

                                                                               
 

                                           
                       

                                                       















                                                                      

                                                                   


                                 
         
     
 





                                                              
 






                                                                        
 






                                           
                                                            
                                        
             










                                                                        
             


                                                           
         
     
 

                                                                               
 


















                                                                      
             







                                                                             
                                
                   
                
                                                    


         






























                                                                    
         

     
       



                                                                      


                                                                         
                                                                          

                                           
                                                               
         


                                                                      

     




                                                                     
                                                                          


                                                                       
                                                                                                                
                      
     
 



                                                                  
                                                                
                     
     
 













                                                                            
     
 
                                                                               
 
/*     ____ ____  ____ ____  ______                                     *\
**    / __// __ \/ __// __ \/ ____/    SOcos COmpiles Scala             **
**  __\_ \/ /_/ / /__/ /_/ /\_ \       (c) 2002, 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. */
    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. */
    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.
     */
    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.
     */
    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.
     */
    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. */
    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;
    }

    //#########################################################################
}