summaryrefslogblamecommitdiff
path: root/sources/scalac/transformer/AddInterfaces.java
blob: dd58d55799fd1f57bb88ffca2904adc49cb7be0d (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 scalac.*;
import ch.epfl.lamp.util.*;
import scalac.ast.*;
import scalac.symtab.*;
import scalac.util.*;
import Tree.*;

import java.util.*;

/**
 * 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
 */

class AddInterfaces extends Transformer {
    protected AddInterfacesPhase phase;
    protected Definitions defs;

    public AddInterfaces(Global global, AddInterfacesPhase phase) {
        super(global);
        this.phase = phase;
        this.defs = global.definitions;
    }

    protected LinkedList/*<Pair<Symbol,Symbol>>*/ ownerSubstStack =
        new LinkedList();
    protected Pair/*<Symbol,Symbol>*/ ownerSubst = null;
    protected SymbolSubstTypeMap typeSubst = null;
    protected Type.SubstThisMap thisTypeSubst = null;

    protected LinkedList/*<List<Tree>>*/ bodyStack = new LinkedList();

    public Tree[] transform(Tree[] trees) {
        List newTrees = new ArrayList();

        bodyStack.addFirst(newTrees);

        for (int i = 0; i < trees.length; ++i)
            newTrees.add(transform(trees[i]));

        bodyStack.removeFirst();

        return (Tree[]) newTrees.toArray(new Tree[newTrees.size()]);
    }

    public Tree transform(Tree tree) {
        // Update tree type, to take into account the new (type)
        // symbols of enclosing classes / methods.
        Type newTp = tree.getType();
        if (typeSubst != null) newTp = typeSubst.apply(newTp);
        if (thisTypeSubst != null) newTp = thisTypeSubst.apply(newTp);
        tree.setType(newTp);

        if (tree.definesSymbol() && !(tree instanceof ClassDef)) {
            // Update symbol's owner, if needed.
            Symbol sym = tree.symbol();
            if (ownerSubst != null && ownerSubst.fst == sym.owner())
                sym.setOwner((Symbol)ownerSubst.snd);

            // Update symbol's type. Do that only for symbols which do
            // not belong to a class, since the type of these (i.e.
            // class members) has been changed during cloning
            // operation.
            if (! (sym.owner().isClass() || thisTypeSubst == null))
                sym.updateInfo(typeSubst.apply(sym.info()));
        }

        switch (tree) {
        case ClassDef(_,_,_,_,_,_): {
            Symbol sym = tree.symbol();
            if (phase.needInterface(sym)) {
                ClassDef classDef = (ClassDef)tree;
                Symbol ifaceSym = sym;
                Symbol classSym = phase.getClassSymbol(ifaceSym);

                List/*<Tree>*/ enclosingBody = (List)bodyStack.getFirst();
                enclosingBody.add(makeInterface(classDef));

                typeSubst = phase.getClassSubst(classSym);
                Tree newTree = makeClass(classDef);
                typeSubst = null;

                return newTree;
            } else
                return super.transform(tree);
        }

        case DefDef(int mods,
                    Name name,
                    AbsTypeDef[] tparams,
                    ValDef[][] vparams,
                    Tree tpe,
                    Tree rhs): {
            Symbol sym = tree.symbol();
            Tree newTree;

            Symbol owner;
            if (sym.isConstructor())
                owner = sym.constructorClass();
            else
                owner = sym.owner();

            if (owner.isClass() && phase.needInterface(owner)) {
                Symbol classOwner = phase.getClassSymbol(owner);
                Map ownerMemberMap = phase.getClassMemberMap(classOwner);
                Symbol newSym = (Symbol)ownerMemberMap.get(sym);
                assert newSym != null
                    : Debug.show(sym) + " not in " + ownerMemberMap;

                global.nextPhase();
                typeSubst.insertSymbol(sym.typeParams(), newSym.typeParams());
                if (!sym.isConstructor())
                    typeSubst.insertSymbol(sym.valueParams(), newSym.valueParams());
                global.prevPhase();

                pushOwnerSubst(sym, newSym);

                newTree = gen.DefDef(newSym, transform(rhs));

                popOwnerSubst();

                typeSubst.removeSymbol(sym.valueParams());
                typeSubst.removeSymbol(sym.typeParams());
            } else
                newTree = super.transform(tree);
            return newTree;
        }

        case This(_): {
            // Use class symbol for references to "this".
            Symbol classThisSym = phase.getClassSymbol(tree.symbol());
            return gen.This(tree.pos, classThisSym);
        }

        case Super(_, _): {
            // Use class symbol for references to "super".
            Symbol classSuperSym = phase.getClassSymbol(tree.symbol());
            return gen.Super(tree.pos, classSuperSym);
        }

        case Select(Super(_, _), _): {
            // Use class member symbols for references to "super".

            Symbol sym = tree.symbol();
            Symbol classOwner = phase.getClassSymbol(sym.owner());
            Map ownerMemberMap = phase.getClassMemberMap(classOwner);
            if (ownerMemberMap != null && ownerMemberMap.containsKey(sym)) {
                Tree qualifier = transform(((Select)tree).qualifier);
                Symbol newSym = (Symbol)ownerMemberMap.get(sym);
                return gen.Select(tree.pos, qualifier, newSym);
            } else
                return super.transform(tree);
        }

        case Select(Tree qualifier, _): {
            Symbol sym = tree.symbol();
            if (sym.isConstructor()) {
                // If the constructor now refers to the interface
                // constructor, use the class constructor instead.
                Symbol clsSym = sym.constructorClass();
                if (phase.needInterface(clsSym)) {
                    Symbol realClsSym = phase.getClassSymbol(clsSym);
                    Map memMap = phase.getClassMemberMap(realClsSym);
                    assert memMap != null
                        : Debug.show(clsSym) + " " + Debug.show(realClsSym);
                    return gen.Select(tree.pos, qualifier, (Symbol)memMap.get(sym));
                } else
                    return super.transform(tree);
            } else {
                qualifier = transform(qualifier);
                Symbol owner = sym.owner();
                if (owner.isClass()
                    && owner.isJava() && !owner.isInterface()
                    && owner != defs.ANY_CLASS
                    && owner != defs.ANYREF_CLASS
                    && owner != defs.JAVA_OBJECT_CLASS) {
                    Type qualifierType = qualifier.getType().bound();
                    if (phase.needInterface(qualifierType.symbol())) {
                        Type castType = qualifierType.baseType(owner);
                        qualifier = gen.mkAsInstanceOf(qualifier, castType);
                    }
                }
                return copy.Select(tree, sym, qualifier);
            }
        }

        case Ident(_): {
            Symbol sym = tree.symbol();
            if (sym.isConstructor()) {
                // If the constructor now refers to the interface
                // constructor, use the class constructor instead.
                Symbol clsSym = sym.constructorClass();
                if (phase.needInterface(clsSym)) {
                    Symbol realClsSym = phase.getClassSymbol(clsSym);
                    Map memMap = phase.getClassMemberMap(realClsSym);
                    assert memMap != null
                        : Debug.show(clsSym) + " " + Debug.show(realClsSym);
                    assert memMap.containsKey(sym)
                        : Debug.show(sym) + " not in " + memMap;
                    return gen.Ident(tree.pos, (Symbol)memMap.get(sym));
                } else
                    return super.transform(tree);
            } else if (typeSubst != null) {
                Symbol newSym = (Symbol)typeSubst.lookupSymbol(tree.symbol());
                if (newSym != null)
                    return gen.Ident(tree.pos, newSym);
                else
                    return super.transform(tree);
            } else {
                return super.transform(tree);
            }
        }

        case New(Template templ): {
            Tree.New newTree = (Tree.New)super.transform(tree);
            Tree.Apply parent = (Tree.Apply)newTree.templ.parents[0];
            Symbol ifaceSym = TreeInfo.methSymbol(parent).type().resultType().symbol();
            if (phase.needInterface(ifaceSym)) {
                Map clsMap = new HashMap();
                Symbol classSym = phase.getClassSymbol(ifaceSym);
                clsMap.put(ifaceSym, classSym);
                clsMap.put(ifaceSym.primaryConstructor(), classSym.primaryConstructor());

                SymbolSubstTypeMap clsSubst =
                    new SymbolSubstTypeMap(clsMap, Collections.EMPTY_MAP);

                newTree.setType(clsSubst.apply(newTree.type));
                newTree.templ.setType(clsSubst.apply(newTree.templ.type));
                parent.setType(clsSubst.apply(parent.type));
                parent.fun.setType(clsSubst.apply(parent.fun.type));
            }
            return newTree;
        }

        case Template(_,_): {
            Symbol sym = tree.symbol();
            if (sym != Symbol.NONE) {
                Symbol newDummySymbol = sym.cloneSymbol();
                pushOwnerSubst(sym, newDummySymbol);
                Tree newTemplate = super.transform(tree);
                popOwnerSubst();
                return newTemplate;
            } else
                return super.transform(tree);
        }

        default:
            return super.transform(tree);
        }
    }

    protected Tree makeInterface(ClassDef classDef) {
        Template classImpl = classDef.impl;
        Tree[] classBody = classImpl.body;
        TreeList ifaceBody = new TreeList();

        for (int i = 0; i < classBody.length; ++i) {
            Tree t = classBody[i];
            Symbol tSym = t.symbol();

            if (!t.definesSymbol() || !phase.memberGoesInInterface(tSym))
                continue;

            if (tSym.isClass())
                ifaceBody.append(transform(new Tree[] { t }));
            else if (tSym.isType())
                ifaceBody.append(t);
            else if (tSym.isMethod())
                ifaceBody.append(gen.DefDef(tSym, Tree.Empty));
            else
                throw Debug.abort("don't know what to do with this ", t);
        }

        return gen.ClassDef(classDef.symbol(), ifaceBody.toArray());
    }

    protected Tree makeClass(ClassDef classDef) {
        Symbol ifaceSym = classDef.symbol();
        Symbol classSym = phase.getClassSymbol(ifaceSym);

        TreeList newClassBody = new TreeList();
        Template classImpl = classDef.impl;
        Tree[] classBody = classImpl.body;

        Map classMemberMap = phase.getClassMemberMap(classSym);

        assert thisTypeSubst == null;
        thisTypeSubst = new Type.SubstThisMap(ifaceSym, classSym);

        for (int i = 0; i < classBody.length; ++i) {
            Tree t = classBody[i];
            Symbol tSym = t.symbol();

            if (t.definesSymbol() && !(classMemberMap.containsKey(tSym)
                                       || tSym.isConstructor()))
                continue;

            Tree newT = transform(t);

            if (t.definesSymbol() && classMemberMap.containsKey(tSym))
                newT.setSymbol((Symbol)classMemberMap.get(tSym));

            newClassBody.append(newT);
        }

        thisTypeSubst = null;

        Tree[] newParents = Tree.cloneArray(transform(classImpl.parents), 1);
        global.nextPhase();
        Type ifaceType = classSym.parents()[newParents.length - 1];
        global.prevPhase();
        newParents[newParents.length - 1] =
            gen.mkPrimaryConstr(classDef.pos, ifaceType);

        Symbol local = classDef.impl.symbol();
        local.setOwner(classSym);
        return gen.ClassDef(classSym, newParents, local, newClassBody.toArray());
    }

    protected Tree[][] extractParentArgs(Tree[] parents) {
        Tree[][] pArgs = new Tree[parents.length][];
        for (int i = 0; i < parents.length; ++i) {
            switch(parents[i]) {
            case Apply(_, Tree[] args): pArgs[i] = transform(args); break;
            default: throw Debug.abort("unexpected parent constr. ", parents[i]);
            }
        }
        return pArgs;
    }

    protected void pushOwnerSubst(Symbol from, Symbol to) {
        ownerSubstStack.addFirst(ownerSubst);
        ownerSubst = new Pair(from, to);
    }

    protected void popOwnerSubst() {
        ownerSubst = (Pair)ownerSubstStack.removeFirst();
    }

}