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



                                                                          

                                                                          

       


                                                                    


                           
                       
                         
                        
                   
                         

                                                         











                                             

                                                

     



                                                         
                                                    

                                                         
                                                                      



                                                                 











                                                                  













                                                                      
                                         














                                                            









                                                               
                                            
                 



                                                                      

                                                         
                                                             

                                                                       

                                                                               



                                                                         
                 
             

                                                                  



                      



                                                  

                                    

          





                                                                     


























                                                                     













                                                                                        
                                               




                                                                          





                                                                              


                              









                                                                     
                                      





                                                             
                                                              


                                                     

                                                                  



                                                                 
                                                       
 


                                                          
                                             
                                                                    



                                                                            
 







                                                                        

                                                  










                                                                         
                                                                          

                                                
                                                   

                                                                       

                                                               




                                                                      

                                              
                                             
                                                         
                 
 
                                                                    

                                                             
                                                                                      











                                                                              
                                                                 




                                                                                        
                                                                                 





                                                                      













                                                        


                                                                              






                                                                      

                                                                                             

















                                                                      
 
/*     ____ ____  ____ ____  ______                                     *\
**    / __// __ \/ __// __ \/ ____/    SOcos COmpiles Scala             **
**  __\_ \/ /_/ / /__/ /_/ /\_ \       (c) 2002, LAMP/EPFL              **
** /_____/\____/\___/\____/____/                                        **
\*                                                                      */

// $Id$

// TODO try to use setInfo instead of updateInfo for cloned symbols,
// to avoid the need to use nextPhase/nextInfo.

package scalac.transformer;

import scalac.*;
import scalac.symtab.*;
import scalac.checkers.*;
import scalac.util.Name;
import java.util.*;
import scalac.util.Debug;

public class AddInterfacesPhase extends PhaseDescriptor {
    public String name () {
        return "addinterfaces";
    }

    public String description () {
        return "add one interface per class";
    }

    public String taskDescription() {
        return "added interfaces";
    }

    public void apply(Global global) {
        new AddInterfaces(global, this).apply();
    }

    public void apply(Unit unit) {
        new AddInterfaces(unit.global, this).apply(unit);
    }

    public Type transformInfo(Symbol sym, Type tp) {
        if (sym.isPrimaryConstructor()) {
            Symbol clazz = sym.primaryConstructorClass();
            if (!(clazz.isClass() && needInterface(clazz))) return tp;
            // The symbol is a constructor of a class which needs
            // an interface. All its value arguments have to be
            // removed.
            return removeValueParams(tp);
        } else if (sym.isClass() && !sym.isJava()) {
            Definitions definitions = Global.instance.definitions;
            Type[] oldParents = tp.parents();
            assert oldParents.length > 0 : Debug.show(sym);
            for (int i = 1; i < oldParents.length; ++i) {
                Symbol oldSym = oldParents[i].symbol();
                assert !oldSym.isJava() || oldSym.isInterface() :
                    Debug.show(sym) + " <: " + Debug.show(oldSym);
            }

            Type[] newParents;
            Scope newMembers;
            if (needInterface(sym)) {
                // Before this phase, the symbol is a class, but after
                // it will be an interface. Its type has to be changed
                // so that:
                //
                //   1. Java classes are removed from its parents,
                //
                //   2. only members which will end up in the
                //      interface are kept, and private ones are made
                //      public and renamed.
                sym.flags |= Modifiers.INTERFACE;

                Scope.SymbolIterator oldMembersIt =
                    new Scope.UnloadIterator(tp.members().iterator());
                newMembers = new Scope();
                while (oldMembersIt.hasNext()) {
                    Symbol member = oldMembersIt.next();

                    if (!memberGoesInInterface(member))
                        continue;

                    if (member.isPrivate()) {
                        member.name = uniqueName(member);
                        member.flags ^= Modifiers.PRIVATE;
                    } else if (member.isProtected())
                        member.flags ^= Modifiers.PROTECTED;

                    newMembers.enterOrOverload(member);
                }

                Symbol oldSym = oldParents[0].symbol();
                if (oldSym.isJava() && !oldSym.isInterface() &&
                    oldSym != definitions.ANY_CLASS &&
                    oldSym != definitions.ANYREF_CLASS)
                {
                    newParents = new Type[oldParents.length];
                    newParents[0] = definitions.ANYREF_TYPE;
                    for (int i = 1; i < oldParents.length; ++i)
                        newParents[i] = oldParents[i];
                } else {
                    newParents = oldParents;
                }
            } else {
                // The symbol is the one of a class which doesn't need
                // an interface. We need to fix its parents to use
                // class symbols instead of interface symbols.
                newMembers = tp.members();
                newParents = new Type[oldParents.length];
                for (int i = 0; i < oldParents.length; ++i) {
                    switch (oldParents[i]) {
                    case TypeRef(Type pre, Symbol oldSym, Type[] args):
                        newParents[i] = !needInterface(oldSym) ? oldParents[i]:
                            Type.typeRef(pre, getClassSymbol(oldSym), args);
                        break;
                    default:
                        throw Debug.abort("illegal case", oldParents[i]);
                    }
                }
            }

            return Type.compoundType(newParents, newMembers, sym);
        } else
            return tp;
    }

    public Checker[] postCheckers(Global global) {
        return new Checker[] {
            new CheckSymbols(global),
            new CheckTypes(global),
            new CheckOwners(global),
	    new CheckNames(global)
        };
    }

    protected boolean memberGoesInInterface(Symbol member) {
        return member.isType()
            || (member.isMethod() && !member.isPrimaryConstructor());
    }

    protected Type removeValueParams(Type tp) {
        switch (tp) {
        case MethodType(Symbol[] vparams, Type result):
            return new Type.MethodType(Symbol.EMPTY_ARRAY, result);
        case PolyType(Symbol[] tps, Type result):
            return new Type.PolyType(tps, removeValueParams(result));
        default:
            return tp;
        }
    }

    protected void uniqueName(Symbol sym, StringBuffer buf) {
        Symbol owner = sym.owner();

        if (owner != Symbol.NONE) {
            uniqueName(owner, buf);
            buf.append('$');
        }

        buf.append(sym.name.toString());
    }

    protected Name uniqueName(Symbol sym) {
        StringBuffer buf = new StringBuffer();
        uniqueName(sym, buf);
        Name newName = Name.fromString(buf.toString());
        if (sym.name.isTypeName()) return newName.toTypeName();
        else return newName;
    }

    // Terminology: in the following code, the symbol which was used
    // until this phase for the class, and will now be used for the
    // interface is called the "interface symbol". The symbol created
    // by this phase for the class is called the "class symbol".

    /** True iff the given class symbol needs an interface. */
    protected boolean needInterface(Symbol classSym) {
        assert classSym.isClass()
            : Debug.toString(classSym) + " is not a class (kind " + classSym.kind + ")";
        return !(classSym.isJava()
                 || classSym.isModuleClass()
                 || classSym.isAnonymousClass()
                 || hasInterfaceSymbol(classSym)
                 || classSym == Global.instance.definitions.ANY_CLASS
                 || classSym == Global.instance.definitions.ANYREF_CLASS
                 || classSym == Global.instance.definitions.ALL_CLASS
                 || classSym == Global.instance.definitions.ALLREF_CLASS);
    }

    protected final static String CLASS_SUFFIX = "$class";
    protected Name className(Name ifaceName) {
        Name className = Name.fromString(ifaceName.toString() + CLASS_SUFFIX);
        if (ifaceName.isTypeName()) return className.toTypeName();
        else return className;
    }

    protected HashMap ifaceToClass = new HashMap();
    protected HashMap classToIFace = new HashMap();

    /** Return the class symbol corresponding to the given interface
     * symbol. If the class does not need an interface, return the
     * given symbol.
     */
    protected Symbol getClassSymbol(Symbol ifaceSym) {
        if (ifaceSym.isPrimaryConstructor())
            return getClassSymbol(ifaceSym.primaryConstructorClass())
                .primaryConstructor();

        if (!needInterface(ifaceSym))
            return ifaceSym;

        Symbol classSym = (Symbol)ifaceToClass.get(ifaceSym);
        if (classSym == null) {
            classSym = ifaceSym.cloneSymbol(ifaceSym.owner());
            classSym.name = className(ifaceSym.name);
            classSym.flags &= ~Modifiers.INTERFACE;

            Symbol ifaceConstrSym = ifaceSym.primaryConstructor();
            Symbol classConstrSym = classSym.primaryConstructor();
            classConstrSym.name = className(ifaceConstrSym.name);

            Scope ifaceOwnerMembers = ifaceSym.owner().members();
            ifaceOwnerMembers.enter(classSym);
            // ifaceOwnerMembers.enter(classConstrSym);

            Type.SubstThisMap thisTypeMap =
                new Type.SubstThisMap(ifaceSym, classSym);

            // Create class substitution map.
            SymbolSubstTypeMap classSubst = newClassSubst(classSym);
            classSubst.insertSymbol(
                ifaceConstrSym.typeParams(), classConstrSym.typeParams());
            classSubst.insertSymbol(
                ifaceConstrSym.valueParams(), classConstrSym.valueParams());

            // Clone all members, entering them in the class scope.
            Map classMembersMap = newClassMemberMap(classSym);
            Scope classMembers = new Scope();
            Scope.SymbolIterator ifaceMembersIt =
                new Scope.UnloadIterator(ifaceSym.members().iterator());
            while (ifaceMembersIt.hasNext()) {
                Symbol ifaceMemberSym = ifaceMembersIt.next();

                if (ifaceMemberSym.isType()
                    || ifaceMemberSym.isDeferred()
                    || ifaceMemberSym.isPrimaryConstructor())
                    continue;

                Symbol classMemberSym;
                if (memberGoesInInterface(ifaceMemberSym)) {
                    if (ifaceMemberSym.isPrivate()) {
                        ifaceMemberSym.name = uniqueName(ifaceMemberSym);
                        ifaceMemberSym.flags ^= Modifiers.PRIVATE;
                    } else if (ifaceMemberSym.isProtected())
                        ifaceMemberSym.flags ^= Modifiers.PROTECTED;

                    classMemberSym = ifaceMemberSym.cloneSymbol(classSym);
                    classMemberSym.setInfo(
                        thisTypeMap.applyParams(
                            classSubst.applyParams(
                                classMemberSym.info().cloneType(
                                    ifaceMemberSym, classMemberSym))));

                    ifaceMemberSym.flags |= Modifiers.DEFERRED;
                } else {
                    // Member doesn't go in interface, we just make it
                    // owned by the class.
                    classMemberSym = ifaceMemberSym;
                    classMemberSym.setOwner(classSym);
                    classMemberSym.updateInfo(
                        thisTypeMap.apply(
                            classSubst.apply(
                                classMemberSym.info())));
                }

                classMembersMap.put(ifaceMemberSym, classMemberSym);
                classMembers.enterOrOverload(classMemberSym);
                if (classMemberSym.isClass())
                    classMembers.enterOrOverload(classMemberSym.primaryConstructor());
            }

            // Give correct type to the class symbol by using class
            // symbols for its parents, and by adding the interface
            // among them.
            Type[] oldClassParents = classSym.parents();
            int oldParentsCount = oldClassParents.length;
            Type[] newClassParents = new Type[oldParentsCount + 1];
            for (int i = 0; i < oldParentsCount; ++i) {
                switch (oldClassParents[i]) {
                case TypeRef(Type pre, Symbol sym, Type[] args):
                    Type newTp = Type.typeRef(pre, getClassSymbol(sym), args);
                    newClassParents[i] = classSubst.apply(newTp);
                    break;
                default:
                    throw Debug.abort("unexpected type for parent", oldClassParents[i]);
                }
            }
            newClassParents[oldParentsCount] = classSubst.apply(ifaceSym.type());
            // TODO setInfo cannot be used here because the type then
            // goes through transformInfo. Maybe setInfo should behave
            // like updateInfo.
            classSym.updateInfo(Type.compoundType(newClassParents,
                                                  classMembers,
                                                  classSym));
            ifaceToClass.put(ifaceSym, classSym);
            classToIFace.put(classSym, ifaceSym);
        }
        return classSym;
    }

    public boolean hasInterfaceSymbol(Symbol classSym) {
        return classToIFace.containsKey(classSym);
    }

    public Symbol getInterfaceSymbol(Symbol classSym) {
        return (Symbol)classToIFace.get(classSym);
    }

    HashMap/*<Symbol,SymbolSubstTypeMap>*/ classSubstitutions = new HashMap();
    protected SymbolSubstTypeMap newClassSubst(Symbol classSym) {
        SymbolSubstTypeMap subst = new SymbolSubstTypeMap();
        classSubstitutions.put(classSym, subst);
        return subst;
    }

    /** Return symbol substitution for the class (a mapping from the
     * interface's type and value parameters to the class' equivalent)
     */
    public SymbolSubstTypeMap getClassSubst(Symbol classSym) {
        SymbolSubstTypeMap classSubst = (SymbolSubstTypeMap)classSubstitutions.get(classSym);
        assert classSubst != null;
        return classSubst;
    }

    HashMap/*<Symbol, HashMap>*/ classMemberMaps = new HashMap();
    protected HashMap newClassMemberMap(Symbol classSym) {
        HashMap map = new HashMap();
        classMemberMaps.put(classSym, map);
        return map;
    }

    /** Return symbol substitution for the class (a mapping from the
     * interface's type and value parameters to the class' equivalent)
     */
    public Map getClassMemberMap(Symbol classSym) {
        return (Map)classMemberMaps.get(classSym);
    }

}