summaryrefslogblamecommitdiff
path: root/sources/scalac/transformer/ExplicitOuterClasses.java
blob: 47937f515adab9373fd26dc7f7f9c578ed3f5d20 (plain) (tree)




























                                                                            

                                                    
                                                                     
                                                        
 
                                                                                 
                      
                           
                                       




                                                                   



                                                                   



                                                         
                                




                             


                                                           
                                        
                                  
 












                                                                
         

     























                                                                                  




                                                  

                                                
 

                                                   

                                                                            

                                              

                                                                      
                                                                
                                                           


                                                                 


                                                      
                                                                         

                                                                    
                                                           



                                                                                       



















                                                                           
                                                                           











                                                                     


                                     







                                                                   

         

                                                  

                                             
                                                                   

                                                               
                                            
                                                                     




                                             
                       

                                                                  
                                                  





                                             
                                                  

                                                                    
                                                                      






                                                                       















                                                                                 
                                                 

                                                                                    

                                                                            



                                           
                                

                                                                     
                                         
                                                 

















                                                                             


                                                                              












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

// $Id$
// $OldId: ExplicitOuterClasses.java,v 1.22 2002/10/17 12:31:56 schinz Exp $

package scalac.transformer;

import java.util.*;

import scalac.*;
import scalac.ast.*;
import scalac.util.*;
import scalac.parser.*;
import scalac.symtab.*;
import scalac.typechecker.*;
import Tree.*;

/**
 * Make links from nested classes to their enclosing class explicit.
 *
 * @author Michel Schinz
 * @version 1.0
 */

public class ExplicitOuterClasses extends Transformer {
    protected final ExplicitOuterClassesPhase phase;

    // Mapping from class constructor symbols to owner field symbols.
    protected final HashMap/*<Symbol,Symbol>*/ outerMap;

    public ExplicitOuterClasses(Global global, ExplicitOuterClassesPhase phase) {
        super(global);
        this.phase = phase;
        this.outerMap = phase.outerMap;
    }

    protected LinkedList/*<Symbol>*/ classStack = new LinkedList();
    protected LinkedList/*<Symbol>*/ outerLinks = new LinkedList();

    /**
     * Return the number of outer links to follow to find the given
     * symbol.
     */
    protected int outerLevel(Symbol sym) {
        Iterator classIt = classStack.iterator();
        for (int level = 0; classIt.hasNext(); ++level) {
            Symbol classSym = (Symbol)classIt.next();
            if (classSym == sym)
                return level;
        }
        return -1;
    }

    /**
     * Return a tree referencing the "level"th outer class.
     */
    protected Tree outerRef(int level) {
        assert level >= 0 : level;

        if (level == 0) {
            Symbol thisSym = (Symbol)classStack.getFirst();
            return gen.This(thisSym.pos, thisSym);
        } else {
            Iterator outerIt = outerLinks.iterator();
            Tree root = gen.Ident((Symbol)outerIt.next());

            for (int l = 1; l < level; ++l) {
                Symbol outerSym = (Symbol)outerIt.next();
                root = gen.mkStable(gen.Select(root, outerSym));
            }

            return root;
        }
    }

    protected LinkedList/*<HashMap<Symbol,Symbol>>*/ superSymsStack =
        new LinkedList();

    protected Symbol outerSuperSym(int level, Symbol funSym) {
        assert level > 0 : level;

        HashMap symMap = (HashMap)superSymsStack.get(level);
        Symbol outerSuperSym = (Symbol)symMap.get(funSym);
        if (outerSuperSym == null) {
            Name outerSuperName =
                Name.fromString("super$" + funSym.name.toString());
            outerSuperSym = new TermSymbol(funSym.pos,
                                           outerSuperName,
                                           (Symbol)classStack.get(level),
                                           Modifiers.PRIVATE);

            global.log("created forwarding symbol: " + Debug.show(outerSuperSym));

            outerSuperSym.setInfo(funSym.info().cloneType(funSym, outerSuperSym));
            symMap.put(funSym, outerSuperSym);
        }
        return outerSuperSym;
    }

    public Tree transform(Tree tree) {
        switch (tree) {
        case ClassDef(int mods, _, _, _, _, _) : {
            // Add outer link
            ClassDef classDef = (ClassDef) tree;
            Symbol classSym = classDef.symbol();
            ValDef[][] newVParams;

            classStack.addFirst(classSym);
            superSymsStack.addFirst(new HashMap());
            if (classStack.size() == 1 || Modifiers.Helper.isStatic(mods)) {
                outerLinks.addFirst(null);
                newVParams = classDef.vparams;
            } else {
                // Add the outer parameter to the tree (it is added to
                // the type by transformInfo).
                Symbol constSym = classSym.primaryConstructor();
                Symbol outerSym = phase.outerSym(constSym);
                assert (outerSym.owner() == constSym) : outerSym;
                outerLinks.addFirst(outerSym);

                ValDef[][] vparams = classDef.vparams;
                ValDef[] newVParamsI;
                if (vparams.length == 0)
                    newVParamsI = new ValDef[] { gen.mkParam(outerSym) };
                else {
                    newVParamsI = new ValDef[vparams[0].length + 1];
                    newVParamsI[0] = gen.mkParam(outerSym);
                    System.arraycopy(vparams[0], 0, newVParamsI, 1, vparams[0].length);
                }
                newVParams = new ValDef[][] { newVParamsI };

                classSym.flags |= Modifiers.STATIC;
            }

            // Add forwarding "super" methods and add them to the
            // class members.
            Scope newMembers = new Scope(classSym.members());
            TreeList newBody = new TreeList(transform(classDef.impl.body));
            HashMap/*<Symbol,Symbol>*/ symMap =
                (HashMap)superSymsStack.removeFirst();
            Iterator symIt = symMap.entrySet().iterator();
            while (symIt.hasNext()) {
                Map.Entry symPair = (Map.Entry)symIt.next();
                Symbol funSym = (Symbol)symPair.getKey();
                Symbol fwdSym = (Symbol)symPair.getValue();

                Symbol[] argsSym = fwdSym.valueParams();
                Tree[] args = new Tree[argsSym.length];
                for (int i = 0; i < argsSym.length; ++i)
                    args[i] = gen.mkRef(argsSym[i].pos, argsSym[i]);
                Tree fwdBody =
                    gen.Apply(gen.Select(gen.Super(classSym.pos, classSym),
                                         funSym),
                              args);

                newBody.append(gen.DefDef(fwdSym, fwdBody));
                newMembers.enter(fwdSym);
            }
            classSym.updateInfo(Type.compoundType(classSym.parents(),
                                                  newMembers,
                                                  classSym));

            Tree[] newParents = transform(classDef.impl.parents);

            outerLinks.removeFirst();
            classStack.removeFirst();

            return copy.ClassDef(classDef,
                                 classSym,
                                 transform(classDef.tparams),
                                 transform(newVParams),
                                 transform(classDef.tpe),
                                 copy.Template(classDef.impl,
                                               newParents,
                                               newBody.toArray()));
        }

        case Ident(_): {
            if (! tree.symbol().name.isTermName())
                return super.transform(tree);

            // Follow "outer" links to fetch data in outer classes.
            int level = outerLevel(tree.symbol().classOwner());
            if (level > 0) {
                Tree root = outerRef(level);
                return gen.mkStable(gen.Select(root, tree.symbol()));
            } else {
                return super.transform(tree);
            }
        }

        case This(_): {
            // If "this" refers to some outer class, replace it by
            // explicit reference to it.
            int level = outerLevel(tree.symbol());
            if (level > 0)
                return outerRef(level);
            else
                return super.transform(tree);
        }

        case Select(Super(_, _), Name selector): {
            // If "super" refers to an outer class, access the value
            // (a method) through outer link(s).
            int level = outerLevel(((Select)tree).qualifier.symbol());
            if (level > 0)
                return gen.Select(outerRef(level),
                                  outerSuperSym(level, tree.symbol()));
            else
                return super.transform(tree);
        }

        case Apply(Tree fun, Tree[] args): {
            // Add outer parameter to constructor calls.
            Tree realFun;
            Tree[] typeArgs;

            switch (fun) {
            case TypeApply(Tree fun2, Tree[] tArgs):
                realFun = fun2; typeArgs = tArgs; break;
            default:
                realFun = fun; typeArgs = null; break;
            }

            Tree newFun = null, newArg = null;

            if (realFun.hasSymbol() && realFun.symbol().isPrimaryConstructor()) {
                switch (transform(realFun)) {
                case Select(Tree qualifier, _): {
                    if (! (qualifier.hasSymbol()
                           && Modifiers.Helper.isNoVal(qualifier.symbol().flags))) {
                        newFun = make.Ident(qualifier.pos, realFun.symbol())
                            .setType(realFun.type());
                        newArg = qualifier;
                    }
                } break;

                case Ident(_): {
                    int level = outerLevel(realFun.symbol().owner());
                    if (level >= 0) {
                        newFun = realFun;
                        newArg = outerRef(level);
                    }
                } break;

                default:
                    throw global.fail("unexpected node in constructor call");
                }

                if (newFun != null && newArg != null) {
                    Tree[] newArgs = new Tree[args.length + 1];
                    newArgs[0] = newArg;
                    System.arraycopy(args, 0, newArgs, 1, args.length);

                    Tree finalFun;
                    if (typeArgs != null)
                        finalFun = copy.TypeApply(fun, newFun, typeArgs);
                    else
                        finalFun = newFun;

                    finalFun.type =
                        phase.addValueParam(finalFun.type,
                                            phase.outerSym(realFun.symbol()));
                    return copy.Apply(tree, finalFun, transform(newArgs));
                } else
                    return super.transform(tree);

            } else
                return super.transform(tree);
        }

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