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



                                                                          


                                                                          


                           
                     
                         
                          
 



                              
                                 
                       
                             














                                                                 

                                                                     



                                                                      

                                                         

                                 
                                                      


                                                                              
 


                                                                               

     




                                                                              
                                            
                                                                 
                                                                                                               
                                           
                                         
                                     












                                                                           
                                                                             










                                                                               
                                                         



                                                                                                                                                                   
                                                        






                                                              

                                
                                    
                           









                                                                                        

                                                             
                                     
     
 


                                                                              
                                                                    
                                                 
                                                                             
                              
     
 



                                                                              

     



                                                                    
                                           

     
                                                              
                                                      
                                                                             

                                             
                                                                    




                                                                            
                                                         
                                                      


                                                                          

     


                                                                               
                                                                     






                                                                               
       

                                                                  
       





                                                                 
                                                             





                                                                              






                                                                      
                                                    
                                       
                                   
                                                                             






                                                                               

                             
         
      
 

                                                                              
 
                               
                                                                               







                                          









                                                                         




                                                                    

                                            
                                     
                                         







                                               

                                            

                                       








                                                                               
                                             






























                                                                            
                                                   
                                                            
                                                                     





                                                                      
                            
                                                                  



                                                                      
                                             
             














                                                                      
                                                                             






























                                                                               
         
 









                                                                     

                                                                             
                                                                             
                                                                               

                         
         



                                                                    


                                                                        


                                                                               
                                                      












                                                                              
                                       






                                                                     

                                                    







                                                                  
                                  







                                                                  
     

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

// $Id$

package scalac.transformer;

import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;

import scalac.Global;
import scalac.Phase;
import scalac.PhaseDescriptor;
import scalac.Unit;
import scalac.ast.GenTransformer;
import scalac.ast.Tree;
import scalac.ast.Tree.Ident;
import scalac.ast.Tree.Template;
import scalac.symtab.Modifiers;
import scalac.symtab.Symbol;
import scalac.symtab.TermSymbol;
import scalac.symtab.Type;
import scalac.util.Debug;
import scalac.util.Name;
import scalac.util.Names;

/**
 * This phase does the following:
 *
 * - In every nested class, adds to each of its constructor a new
 *   value parameter that contains a link to the outer class.
 *
 * - In every nested type, adds to each of its constructor a new type
 *   parameter for every type parameter appearing in outer types.
 *
 * - In every class, adds a forwarding "super" method for every method
 *   that is accessed via "super" in a nested class.
 *
 * - Replaces all prefixes of TypeRefs by localThisTypes.
 *
 * - Adds all missing qualifiers.
 */
public class ExplicitOuterClassesPhase extends Phase {

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

    /** Initializes this instance. */
    public ExplicitOuterClassesPhase(Global global,PhaseDescriptor descriptor){
        super(global, descriptor);
    }

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

    /** Applies this phase to the given type for the given symbol. */
    public Type transformInfo(Symbol symbol, Type type) {
        if (symbol.isPackage()) return type;
        // if (!symbol.isJava() && symbol.isConstructor()) // !!!
        //System.out.println("!!! " + Debug.show(symbol) + ": " + type + " -> " + typeTransformer.apply(type));
        type = typeTransformer.apply(type);
        if (symbol.isJava()) return type;
        if (symbol.isConstructor()) {
            Symbol[] tparams = type.typeParams();
            Symbol[] vparams = type.valueParams();
            Type result = type.resultType();
            // Add outer value link
            if (hasOuterValueLink(symbol)) {
                Name name = Names.OUTER(symbol);
                Symbol vlink = new TermSymbol(symbol.pos, name, symbol, 0);
                vlink.setInfo(getOuterClass(symbol).typeOfThis()); // !!!
                vparams = Symbol.cloneArray(1, vparams);
                vparams[0] = vlink;
            }
            // Add outer type links
            if (hasOuterTypeLinks(symbol)) {
                Symbol[] oldtparams = getOuterClass(symbol).nextTypeParams();
                Symbol[] tlinks = Symbol.cloneArray(oldtparams);
                for (int i = 0; i < tlinks.length; i++) {
                    tlinks[i] = oldtparams[i].cloneSymbol(symbol);
                    tlinks[i].name = Names.OUTER(symbol, oldtparams[i]);
                }
                tparams = Symbol.concat(tlinks, tparams);
                result = Type.getSubst(oldtparams, tlinks, true).apply(result);
            }
            type = Type.MethodType(vparams, result);
            if (tparams.length != 0) type = Type.PolyType(tparams, type);
        } else {
            Symbol owner = symbol.owner().isConstructor()
                ? symbol.owner()
                : symbol.enclClass();
            //System.out.println("!!! debug2 = " + Debug.show(symbol) + " - " + Debug.show(owner) + " --- " + (owner == Symbol.NONE) + " -- #" + owner.name + "#");
            //if (onwer.isJava() && owner != Symbol.NONE && owner.name.length() > 0) // !!!
            if (owner.isType() || owner.isConstructor())
            type = getOuterTypeSubst(owner, true).apply(type);
        }

        /*

        String s1 = Debug.show(symbol);
        String s2 = symbol.info().toString();
        symbol.updateInfo(type);
        global.nextPhase();
        String s3 = type.toString();
        global.prevPhase();
        System.out.println("!!! symbol = " + s1);
        System.out.println("!!! type   = " + s2 + " -- " + System.identityHashCode(s2));
        System.out.println("!!! new    = " + s3 + " -- " + System.identityHashCode(s3));
        System.out.println("!!!");

        */

        return type;
    }

    /** Applies this phase to the given compilation units. */
    public void apply(Unit[] units) {
        treeTransformer.apply(units);
    }

    //########################################################################
    // Private Methods - Outer class

    /** Returns the outer class of the given type or constructor. */
    private Symbol getOuterClass(Symbol symbol) {
        assert symbol.isType() || symbol.isConstructor(): Debug.show(symbol);
        return symbol.owner();
    }

    /** Has the given class or constructor an outer value link? */
    private boolean hasOuterValueLink(Symbol symbol) {
        assert symbol.isClass() || symbol.isConstructor(): Debug.show(symbol);
        return !symbol.isJava() && getOuterClass(symbol).isClass();
    }

    /** Returns the outer link of the given class or constructor. */
    private Symbol getOuterValueLink(Symbol symbol) {
        if (!hasOuterValueLink(symbol)) return Symbol.NONE;
        if (symbol.isClass()) symbol = symbol.primaryConstructor();
        return symbol.nextValueParams()[0];
    }

    /** Has the given type or constructor outer type links? */
    private boolean hasOuterTypeLinks(Symbol symbol) {
        assert symbol.isType() || symbol.isConstructor(): Debug.show(symbol);
        if (symbol.isJava()) return false;
        Symbol outer = getOuterClass(symbol);
        return outer.isType() && outer.nextTypeParams().length != 0;
    }

    /** Returns the type substitution for the given class or constructor. */
    private Type.Map getOuterTypeSubst(Symbol symbol, boolean update) {
        if (!hasOuterTypeLinks(symbol)) return Type.IdMap;
        Symbol[] oldtparams = getOuterTypeParams(symbol);
        Symbol[] newtparams = symbol.nextTypeParams();
        Symbol[] tlinks = new Symbol[oldtparams.length];
        for (int i = 0; i < tlinks.length; i++) tlinks[i] = newtparams[i];
        return Type.getSubst(oldtparams, tlinks, update);
    }

    /** Returns the outer type parameters of the given class or constructor. */
    private Symbol[] getOuterTypeParams(Symbol symbol) {
        Symbol outer = getOuterClass(symbol);
        Symbol[] tparams = Symbol.cloneArray(outer.nextTypeParams());
        for (int i = tparams.length; i != 0; outer = getOuterClass(outer)) {
            Symbol[] symbols = outer.typeParams();
            for (int j = symbols.length; j != 0; ) tparams[--i] = symbols[--j];
        }
        return tparams;
    }

    /**
     * Extracts from given prefix the outer type arguments for the
     * given class or constructor.
     */
    private Type[] getOuterTypeArgs(Type prefix, Symbol symbol) {
        if (!hasOuterTypeLinks(symbol)) return Type.EMPTY_ARRAY;
        Symbol outer = getOuterClass(symbol);
        global.nextPhase();
        Type[] targs = prefix.baseType(outer).widen().typeArgs();
        global.prevPhase();
        assert targs.length == outer.nextTypeParams().length:
            "\nsymbol = " + Debug.show(symbol) +
            "\nprefix = " + prefix;
        return targs;
    }

    //########################################################################
    // Private Class - Type transformer

    /** The type transformer */
    private final Type.Map typeTransformer = new Type.MapOnlyTypes() {
        public Type apply(Type type) {
            switch (type) {
            case TypeRef(Type prefix, Symbol symbol, Type[] targs):
                if (!symbol.owner().isType()) break;
                prefix = apply(prefix);
                targs = map(targs);
                targs = Type.concat(getOuterTypeArgs(prefix, symbol), targs);
                if (symbol.isClassType()) {
                    // !!! add assertions ?
                    prefix = Type.localThisType;
                } else {
                    // !!! replace outer ThisTypes by SingleTypes to outer link
                }
                return Type.TypeRef(prefix, symbol, targs);
            }
            return map(type);
        }
    };

    //########################################################################
    // Private Class - Tree transformer

    /** The tree transformer */
    private final GenTransformer treeTransformer = new GenTransformer(global) {

        /** The current context */
        private Context context;

        /** Transforms the given tree. */
        public Tree transform(Tree tree) {
            switch (tree) {

            case ClassDef(_, _, _, _, _, Template impl):
                Symbol clasz = tree.symbol();
                context = new Context(context, clasz, new HashMap());
                Tree[] parents = transform(impl.parents);
                Tree[] body = transform(impl.body);
                body = Tree.concat(body, genSuperMethods());
                context = context.outer;
                if (context != null) clasz.flags |= Modifiers.STATIC;
                return gen.ClassDef(clasz, parents, impl.symbol(), body);

            case DefDef(_, _, _, _, _, Tree rhs):
                Symbol method = tree.symbol();
                Context backup = context;
                if (method.isConstructor())
                    context = context.getConstructorContext(method);
                else
                    context.inMethod = true;
                rhs = transform(rhs);
                context.inMethod = false;
                context = backup;
                return gen.DefDef(method, rhs);

            case AbsTypeDef(_, _, _, _):
            case AliasTypeDef(_, _, _, _):
                // eliminate // !!!
                return Tree.Empty;

            case Typed(Tree expr, Tree tpe):
                // eliminate // !!!
                return transform(expr);

            case Apply(Tree vfun, Tree[] vargs):
                switch (vfun) {
                case TypeApply(Tree tfun, Tree[] targs):
                    if (!tfun.symbol().isConstructor()) break;
                    return transform(tree, vargs, vfun, targs, tfun);
                default:
                    if (!vfun.symbol().isConstructor()) break;
                    return transform(tree, vargs, vfun, Tree.EMPTY_ARRAY,vfun);
                }
                return super.transform(tree);

            case This(_):
                return genOuterRef(tree.pos, tree.symbol());

            case Select(Tree qualifier, _):
                Symbol symbol = tree.symbol();
                switch (qualifier) {
                case Super(_, _):
                    Symbol clasz = qualifier.symbol();
                    if (clasz == context.clasz) {
                        qualifier = gen.Super(tree.pos, qualifier.symbol());
                    } else {
                        qualifier = genOuterRef(qualifier.pos, clasz);
                        symbol = getSuperMethod(clasz, symbol);
                    }
                    break;
                default:
                    qualifier = transform(qualifier);
                    break;
                }
                return gen.Select(tree.pos, qualifier, symbol);

            case Ident(_):
                Symbol symbol = tree.symbol();
                Symbol owner = symbol.owner();
                if (owner.isClass()) {
                    // !!! A this node is missing here. This should
                    // never happen if all trees were correct.
                    Tree qualifier = genOuterRef(tree.pos, owner);
                    return gen.Select(qualifier, symbol);
                }
                if (owner.isPrimaryConstructor()) {
                    Symbol clasz = owner.constructorClass();
                    if (clasz != context.clasz || context.inMethod) {
                        Tree qualifier = genOuterRef(tree.pos, clasz);
                        return gen.Select(qualifier, symbol);
                    }
                }
                return gen.Ident(tree.pos, symbol);

            case TypeTerm():
                Type type = typeTransformer.apply(tree.getType());
                if (context != null) type = context.subst.apply(type);
                return gen.TypeTerm(tree.pos, type);

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

        /* Add outer type and value arguments to constructor calls. */
        private Tree transform(Tree vapply, Tree[] vargs, Tree tapply,
            Tree[] targs, Tree tree)
        {
            Symbol symbol = tree.symbol();
            targs = transform(targs);
            vargs = transform(vargs);
            switch (transform(tree)) {
            case Select(Tree qualifier, _):
                if (hasOuterValueLink(symbol)) {
                    vargs = Tree.cloneArray(1, vargs);
                    vargs[0] = qualifier;
                }
                Type[] types = getOuterTypeArgs(qualifier.getType(), symbol);
                if (types.length != 0) {
                    targs = Tree.cloneArray(types.length, targs);
                    for (int i = 0; i < types.length; i++)
                        targs[i] = gen.mkType(tapply.pos, types[i]);
                }
            }
            tree = gen.Ident(tree.pos, symbol);
            if (targs.length != 0) tree = gen.TypeApply(tapply.pos,tree,targs);
            return gen.Apply(vapply.pos, tree, vargs);
        }

        /**
         * Returns the forwarding "super" method of the given class
         * that invokes the given method. If the symbol does not yet
         * exist, it is created and added to the class members.
         */
        private Symbol getSuperMethod(Symbol clasz, Symbol method) {
            Context context = this.context;
            for (; context.clasz != clasz; context = context.outer)
                assert context.outer != null : Debug.show(clasz);
            Symbol forward = (Symbol)context.supers.get(method);
            if (forward == null) {
                Name name = Names.SUPER(method);
                int flags = Modifiers.PRIVATE | Modifiers.FINAL;
                forward = new TermSymbol(method.pos, name, clasz, flags);
                forward.setInfo(method.nextType().cloneType(method, forward));
                context.supers.put(method, forward);
                clasz.nextInfo().members().enter(forward);
                assert Debug.log("created forwarding method: ", forward);
            }
            return forward;
        }

        /** Generates the trees of the forwarding "super" methods. */
        private Tree[] genSuperMethods() {
            if (context.supers.size() == 0) return Tree.EMPTY_ARRAY;
            Tree[] trees = new Tree[context.supers.size()];
            Iterator entries = context.supers.entrySet().iterator();
            for (int i = 0; i < trees.length; i++) {
                Map.Entry entry = (Map.Entry)entries.next();
                Symbol method = (Symbol)entry.getKey();
                Symbol forward = (Symbol)entry.getValue();
                int pos = forward.pos;
                Tree[] targs = gen.mkTypeRefs(pos, forward.nextTypeParams());
                Tree[] vargs = gen.mkRefs(pos, forward.nextValueParams());
                Tree fun = gen.Select(gen.Super(pos, context.clasz), method);
                trees[i] = gen.DefDef(forward, gen.mkApplyTV(fun,targs,vargs));
            }
            return trees;
        }

        /** Returns a tree referencing the given outer class. */
        private Tree genOuterRef(int pos, Symbol clasz) {
            if (context.clasz == clasz) return gen.This(pos, clasz);
            Tree tree = context.inMethod
                ? gen.Select(gen.This(pos, context.clasz), context.link)
                : gen.Ident(pos, context.link);
            for (Context context = this.context.outer;;context =context.outer){
                assert context != null: Debug.show(clasz);
                if (context.clasz == clasz) return tree;
                tree = gen.Select(tree, context.link);
            }
        }

    };

    //########################################################################
    // Private Class - Tree transformer context

    /** This class represents the tree transformation context. */
    private class Context {

        /** The outer context */
        public final Context outer;
        /** The current class symbol */
        public final Symbol clasz;
        /** The super methods (maps invoked to forwarding methods) */
        public final Map/*<Symbol,Symbol>*/ supers;
        /** The outer type parameter substitution */
        public final Type.Map subst;
        /** The link to the outer class */
        public final Symbol link;
        /**  True if in a method of current class */
        public boolean inMethod;

        /** Initializes this instance. */
        public Context(Context outer, Symbol symbol, Map supers) {
            this.outer = outer;
            this.clasz = symbol.constructorClass();
            this.supers = supers;
            this.subst = getOuterTypeSubst(symbol, false);
            this.link = getOuterValueLink(symbol);
            this.inMethod = false;
        }

        /** Returns a context for the given constructor. */
        public Context getConstructorContext(Symbol constructor) {
            assert constructor.constructorClass() == clasz;
            return new Context(outer, constructor, supers);
        }

    }

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