summaryrefslogblamecommitdiff
path: root/sources/scalac/transformer/ExpandMixinsPhase.java
blob: e56e1840f3688b247ff25e6a46e2ca05772a1c6e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11



                                                                          






                                                                          

                          

                         


                            
 
                     

                              
                              

                                


                           

                              

                                 
                           


                            
 






                                          






                                                                    
                          
                                              



                                                                              
                                                
                                                    
 








                                                                 


                                                      
 
                                                              
                                          

                                                                              






                                                                           
                                          
                                        
                                                                      
                                     
                                                 

     

                                                                              
 


                                                            
     
 
                                                                     
                                                         

                                             
                                                  
                                                                 
                                   
                                                           
             


                                           
         
     
 


                                                                              
                                                                              
                                    
 





                                                                    

                          
                                                 
                                 
                                                          
                                             
                    
                                  
             
         




                                                                      

                                                                   
                                             










































                                                                               
                    





                                                


                                                               

                                             
                            










































                                                                              
                                                          

                                                            
                                                                          








                                                                              
                                                          

                                                            
                                                                          
                             
             
                                                         
         
































                                                                              
                                                     



































                                                                                    
                                    


                                                  
                                              
                               
                                           
                                             





































                                                                                                                   
                                                                       


                                                                               









                                                                        






                                                                                 
                                                                     








                                                                  







                                                                           
                                 
                                                        
                                                                 

                                                                               
                             











                                                                                        
                                                                                                                                               



















                                                                               


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

// $OldId: ExpandMixinsPhase.java,v 1.8 2002/05/02 10:59:35 schinz Exp $
// $Id$

package scalac.transformer;

import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.LinkedList;

import scalac.Global;
import scalac.Phase;
import scalac.PhaseDescriptor;
import scalac.CompilationUnit;
import scalac.ast.Tree;
import scalac.ast.Tree.Template;
import scalac.ast.TreeGen;
import scalac.ast.TreeInfo;
import scalac.ast.TreeList;
import scalac.ast.Traverser;
import scalac.ast.Transformer;
import scalac.ast.GenTransformer;
import scalac.ast.GenTreeCloner;
import scalac.symtab.Scope;
import scalac.symtab.Symbol;
import scalac.symtab.Type;
import scalac.util.Debug;

import scalac.util.Name;
import scalac.util.Names;
import scalac.symtab.Modifiers;
import scalac.symtab.Scope.SymbolIterator;
import scalac.symtab.SymbolCloner;
import scalac.symtab.SymbolSubstTypeMap;

// TODO do not copy hidden members which are not accessible via
// "super".

/**
 * A phase to expand mixins using code copying. We assume that links
 * to outer classes have been made explicit by a previous phase.
 */
// !!! needs to be cleaned
public class ExpandMixinsPhase extends Phase {

    //########################################################################
    // Private Fields

    /** A map from classes to their interface */
    private final Map/*<Symbol,Symbol>*/ interfaces;

    /** A map from classes to their type transformer */
    private final Map/*<Symbol,TypeTransformer>*/ transformers;

    /** A map from classes to their expanded body */
    private final Map/*<Symbol,Tree[]>*/ expansions;

    /** A map from classes to their original (unexpanded) body */
    private final Map/*<Symbol,Tree[]>*/ bodies;

    /** A map from members to their original member */
    private final Map/*<Symbol,Symbol>*/ origins;


    /** A transformer that expands classes that have mixins */
    private final GenTransformer expander;

    //########################################################################
    // Public Constructors

    /** Initializes this instance. */
    public ExpandMixinsPhase(Global global, PhaseDescriptor descriptor) {
        super(global, descriptor);
        Phase addinterfaces = global.PHASE.ADDINTERFACES.phase();
        this.interfaces = ((AddInterfacesPhase)addinterfaces).classToIFace;
        this.transformers = new HashMap();
        this.expansions = new HashMap();
        this.bodies = ((AddInterfacesPhase)addinterfaces).classToBody;
        this.origins = new HashMap();
        this.expander = new TreeExpander(global);
    }

    //########################################################################
    // Public Methods

    /** Applies this phase to the given compilation unit. */
    public void apply(CompilationUnit unit) {
        expander.apply(unit);
    }

    /** Applies this phase to the given type for the given symbol. */
    public Type transformInfo(Symbol symbol, Type type) {
        while (true) {
            if (symbol.isJava()) return type;
            if (symbol.isInterface()) return type;
            if (symbol.isCompoundSym()) return type; // !!! check
            if (symbol.isClass()) {
                return getTypeExpander(symbol).apply(type);
            }
            symbol = symbol.isConstructor()
                ? symbol.constructorClass()
                : symbol.owner();
        }
    }

    //########################################################################
    // Private Methods

    //########################################################################
    // Private Class - Tree expander

    /**
     * A tree transformer that expands class definitions and removes
     * compiled units.
     */
    private class TreeExpander extends GenTransformer {
        public TreeExpander(Global global) {
            super(global);
        }
        public void apply(CompilationUnit unit) {
            if (unit.mixinOnly) {
                assert Debug.log("removing unit " + unit);
                unit.body = Tree.EMPTY_ARRAY;
            } else {
                super.apply(unit);
            }
        }
        public Tree transform(Tree tree) {
            switch (tree) {
            case ClassDef(_, _, _, _, _, _):
                Symbol clasz = tree.symbol();
                if (clasz.isInterface()) return super.transform(tree);
                return gen.ClassDef(clasz, getExpandedBody(clasz));
            default:
                return super.transform(tree);
            }
        }
    }

    //########################################################################
    // Private Class - Tree inliner

    /**
     * A Tree cloner that clones mixin bodies. It assumes that these
     * bodies have already been expanded. References to mixin value
     * parameters are replaced by references to newly-created inlined
     * fields. The symbol of Super and This nodes is replaced by the
     * symbol of the generated class. The symbol of super members is
     * rebound if required. Note that as the input tree has already
     * been expanded, it contains only super member to the first
     * supertype of the mixin, all other super members have been
     * removed during the expansion of the input tree.
     */
    private static class TreeInliner extends GenTreeCloner {
        private final Symbol clasz;
        private final Type supertype;
        private boolean initializer;
        public TreeInliner(Global global, TypeTransformer transformer) {
            super(global, transformer, transformer.cloner);
            // !!! global.nextPhase();
            this.clasz = transformer.clasz;
            // !!! global.prevPhase();
            this.supertype = clasz.nextInfo().parents()[0];
        }
        public Symbol getSymbolFor(Tree tree) {
            switch (tree) {
            case Select(Super(_, _), _):
                if (tree.symbol().isInitializer()) return tree.symbol(); // !!!
                assert supertype.symbol().isSubClass(tree.symbol().owner()):
                    tree + " -- " + Debug.show(clasz);
                global.nextPhase();
                Symbol symbol = tree.symbol().overridingSymbol(supertype);
                global.prevPhase();
                assert !symbol.isNone(): tree + " -- " + Debug.show(clasz);
                return symbol;
            case Super(_, _):
            case This(_):
                return clasz;
            default:
                return super.getSymbolFor(tree);
            }
        }
        public Tree transform(Tree tree) {
            switch (tree) {
            case DefDef(_, _, _, _, _, _):
                Symbol symbol = getSymbolFor(tree);
                if (symbol.owner() != clasz) return Tree.Empty;
                if (symbol.isInitializer()) initializer = true;
                tree = super.transform(tree);
                initializer = false;
                return tree;
            case Apply(Select(Super(_, _), _), _):
                if (TreeInfo.methSymbol(tree).isInitializer() && !initializer)
                    return Tree.Empty;
                break;
            }
            if (tree.hasSymbol() && tree.symbol().isParameter()) {
                Symbol symbol = getSymbolFor(tree);
                if (!symbol.isParameter()) {
                    assert tree instanceof Tree.Ident: tree;
                    return gen.Select(gen.This(tree.pos, clasz),  symbol);
                }
            }
            return super.transform(tree);
        }
    }

    //########################################################################
    // Private Class - Class tree expander

    /**
     * A tree transformer that expands a class definitions. Super
     * members are rebound and/or rewritten into normal members.
     */
    private class ClassTreeExpander extends TreeExpander {
        private final Symbol clasz;
        private final Type[] parents;
        private final Map clones;
        public ClassTreeExpander(Global global, TypeTransformer transformer) {
            super(global);
            this.clasz = transformer.clasz;
            this.parents = clasz.parents();
            this.clones = transformer.cloner.clones;
        }
        private Symbol getSuperMember(Symbol member) {
            for (int i = 0; i < parents.length; i++) {
                global.nextPhase();
                if (!parents[i].symbol().isSubClass(member.owner())) {
                    global.prevPhase(); // !!!
                    continue;
                }
                Symbol override = member.overridingSymbol(parents[i]);
                global.prevPhase();
                assert !override.isNone():
                    Debug.show(member, clasz, parents[i]);
                if (i == 0) return override;
                Symbol clone = (Symbol)clones.get(override);
                assert clone != null: Debug.show(member, override, clasz);
                return clone;
            }
            // !!! double loop
            for (int i = 0; i < parents.length; i++) {
                if (!parents[i].symbol().isSubClass(member.owner())) continue;
                //global.nextPhase();
                Symbol override = member.overridingSymbol(parents[i]);
                //global.prevPhase();
                assert !override.isNone():
                    Debug.show(member, clasz, parents[i]);
                if (i == 0) return override;
                Symbol clone = (Symbol)clones.get(override);
                assert clone != null: Debug.show(member, override, clasz);
                return clone;
            }
            throw Debug.abort(Debug.show(member, clasz));
        }
        public Tree transform(Tree tree) {
            switch (tree) {
            case Select(Super(_, _), _):
                Symbol symbol = getSuperMember(tree.symbol());
                Tree qualifier = symbol.owner() == clasz
                    ? gen.This(tree.pos, clasz)
                    : gen.Super(tree.pos, clasz);
                return gen.Select(tree.pos, qualifier, symbol);
            default:
                return super.transform(tree);
            }
        }
    }















    /** Returns the type transformer to use for the given class. */
    private TypeTransformer getTypeExpander(Symbol clasz) { // !!!
        assert clasz.isClass() && !clasz.isInterface(): Debug.show(clasz);
        TypeTransformer transformer =(TypeTransformer)transformers.get(clasz);
        if (transformer == null) {
            transformer = new TypeTransformer(clasz);
            transformers.put(clasz, transformer);
        }
        return transformer;
    }

    /** Returns the expanded body of the given clasz. */
    private Tree[] getExpandedBody(Symbol clasz) {
        Tree[] body = (Tree[])expansions.get(clasz);
        if (body == null) {
            body = (Tree[])bodies.remove(clasz);
            assert body != null: Debug.show(clasz);
            TypeTransformer transformer = getTypeExpander(clasz);
            TreeList list = new TreeList();
            assert Debug.log("expanding tree ", clasz);
            Type[] parents = clasz.parents();
            TreeInliner inliner = new TreeInliner(global, transformer);
            for (int i = 1; i < parents.length; i++) {
                Symbol mixin = parents[i].symbol();
                if (mixin.isInterface()) continue;
                assert Debug.log("expanding tree ", clasz, ": inlining ", mixin);
                list.append(inliner.transform(getExpandedBody(mixin)));
            }
            ClassTreeExpander expander = new ClassTreeExpander(global, transformer);
            list.append(expander.transform(body));
            body = list.toArray();
            expansions.put(clasz, body);
        }
        return body;
    }

    //########################################################################
    // Private Class - Collector

    private class TypeTransformer extends Type.MapOnlyTypes {

        public final Symbol clasz;
        public final Type[] parents;
        public final SymbolCloner cloner;
        public final Map/*<Symbol,Type>*/ inlines;

        public TypeTransformer(Symbol clasz) {
            this.clasz = clasz;
            this.parents = clasz.parents();
            this.cloner = new SymbolCloner();
            this.inlines = new HashMap();
            initialize();
        }

        private void initialize() {
            Type[] parents = clasz.parents();
            // !!! parents.length > 0
            if (parents.length > 0) parents[0].symbol().nextInfo(); // force
            assert Debug.log("expanding type ", clasz);
            for (int i = parents.length - 1; 0 < i; i--) {
                switch (parents[i]) {
                case TypeRef(Type prefix, Symbol mixin, Type[] args):
                    if (mixin.isInterface()) continue;
                    mixin.nextInfo(); // force
                    assert Debug.log("expanding type ", clasz, ": inlining ", mixin);
                    cloner.owners.put(mixin, clasz);
                    cloner.owners.put(mixin.primaryConstructor(), clasz);
                    // map mixin type parameters to mixin type arguments
                    Symbol[] tparams = mixin.typeParams();
                    for (int j = 0; j < tparams.length; j++)
                        inlines.put(tparams[j], args[j]);

                    createMixedInMemberSymbols(mixin.nextInfo().members());
                }
            }
            Set clones = new HashSet(cloner.clones.keySet());
//             cloner.clones.putAll(getTypeTransformerFor(parents[0].symbol()).cloner.clones); // !!! build closure

            // !!! remove ?
            for (Iterator i = cloner.clones.values().iterator(); i.hasNext();){
                // !!! for (Iterator i = clones.iterator(); i.hasNext();){
                Symbol clone = (Symbol)i.next();
                clone.setInfo(apply(clone.info()));
            }
        }

        private void createMixedInMemberSymbols(Scope symbols) {
            Scope scope = clasz.members();
            for (SymbolIterator i = symbols.iterator(); i.hasNext();) {
                Symbol member = i.next();
                boolean shadowed = member.isPrivate() || member.isInitializer()
                    || member.overridingSymbol(clasz.thisType()) != member;
                Symbol origin = (Symbol)origins.get(member);
                if (!shadowed) {
                    if (member.overriddenSymbol(parents[0]) == origin) {
                        cloner.clones.put(member, origin);
                        continue;
                    }
                }
                // !!! when the symbol is shadowed, inlining could be
                // avoided if the original definition is accessible
                // through super
                assert Debug.log("expanding type ", clasz, ": cloning ", member);
                Symbol clone = cloner.cloneSymbol(member);
                if (shadowed) clone.name = Names.MIXIN(member);
                if (shadowed) clone.flags &= ~Modifiers.OVERRIDE;
                if (shadowed) clone.flags &= ~Modifiers.ACCESSFLAGS;
                if (shadowed) clone.flags |=  Modifiers.PRIVATE;
                scope.enterOrOverload(clone);
                origins.put(clone, origin != null ? origin : member);
            }
        }


        public Type apply(Type type) {
            switch (type) {
            case TypeRef(Type prefix, Symbol symbol, Type[] args):
                Type inline = (Type)inlines.get(symbol);
                if (inline != null) return inline;
                if (symbol.isParameter()) {
                    Symbol clone = (Symbol)cloner.clones.get(symbol);
                    if (clone != null) {
                        assert prefix == Type.NoPrefix && args.length == 0:
                            type;
                        return Type.typeRef(prefix, clone, args);
                    }
                }
                return map(type);
            case SingleType(Type prefix, Symbol symbol):
                Symbol clone = (Symbol)cloner.clones.get(symbol);
                prefix = apply(prefix);
                return Type.singleType(prefix, clone != null ? clone : symbol);
            case ThisType(_):
                return clasz.thisType();
            case CompoundType(Type[] parents, Scope members):
                if (type.symbol() != clasz) return map(type);
                if (parents.length <= 1) return type;
                Symbol iface = (Symbol)interfaces.get(clasz);
                if (iface == null) {
                    int i = 1;
                    while (i < parents.length && parents[i].symbol().isInterface()) i++;
                    if (i == parents.length) return type;
                    parents = Type.cloneArray(parents);
                    while (i < parents.length) {
                        if (!parents[i].symbol().isInterface())
                            parents[i] = Type.typeRef(parents[i].prefix(), (Symbol)interfaces.get(parents[i].symbol()), parents[i].typeArgs());
                        i++;
                    }
                    return Type.compoundType(parents, members, clasz);
                }
                assert parents[parents.length - 1].symbol() == iface: type;
                if (parents.length == 2) return type;
                for (int i = 1; i < parents.length - 1; i++) {
                    Symbol mixin = parents[i].symbol();
                    Symbol imix = mixin.isInterface()
                        ? mixin
                        : (Symbol)interfaces.get(mixin);
                    assert iface.isSubClass(imix): type+" - "+i;
                }
                parents = new Type[] {parents[0], parents[parents.length - 1]};
                return Type.compoundType(parents, members, clasz);
            default:
                return map(type);
            }
        }

    }

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