summaryrefslogblamecommitdiff
path: root/sources/scala/tools/nsc/transform/Mixin.scala
blob: 4cf1ea4ff49885140cbc1974c68f65a7daf927b4 (plain) (tree)































                                                                                              




                                                                      
 

                                       
































                                                                                                             



                                                                                



                                                                      
                                               

                                                                           

                            
       








                                                                                                  



                                         
                      
                                                                                          
                                

           

                                               
                                        











                                                                            

           





                                                                  
                                                                                       

                                                      
                                                                                                         
                                                                        



                                                                                              


               
         








































































































                                                                                                   
/* NSC -- new scala compiler
 * Copyright 2005 LAMP/EPFL
 * @author
 */
// $Id$
package scala.tools.nsc.transform;

import symtab._;
import Flags._;
import util.{ListBuffer}
import scala.tools.util.Position;

abstract class Mixin extends InfoTransform {
  import global._;
  import definitions._;
  import posAssigner.atPos;

  /** the following two members override abstract members in Transform */
  val phaseName: String = "mixin";

  override def phaseNewFlags: long = lateMODULE;

  def isForwarded(sym: Symbol) =
    sym.owner.isImplClass && sym.isMethod && !(sym hasFlag (ACCESSOR | SUPERACCESSOR));

  def isStatic(sym: Symbol) = isForwarded(sym) && (sym.hasFlag(PRIVATE) || sym.isConstructor);

  def isSelfRef(tree: Tree) = tree match {
    case This(_) => true
    case Super(_, mix) => mix != nme.EMPTY.toTypeName
    case _ => false
  }
/*
  def toImplClass(sym: Symbol): Symbol = {
    val impl = clazz.owner.info.decl(nme.IMPL_CLASS_NAME(clazz.name));

  }

  def toInterface(sym: Symbol): Symbol;
*/
  def toInterface(tp: Type): Type =
    if (tp.symbol.isImplClass) {
      val iface = tp.parents.last;
      if (!tp.symbol.name.startsWith(iface.symbol.name)) assert(false, "bad interface: " + tp + " " + iface);
      iface
    } else tp;

  def makeUnique(decls: Scope, sym: Symbol): unit = {
    if ((sym hasFlag PRIVATE) && decls.lookup(sym.name) != NoSymbol) {
      val prefix = sym.name.toString() + "$";
      var index = 0;
      while (decls.lookup(newTermName(prefix + index)) != NoSymbol) index = index + 1;
      sym.name = newTermName(prefix + index)
    }
  }

  private def staticRef(sym: Symbol) = {
    System.out.println("static ref " + sym.owner + " . " + sym);//debug
    sym.owner.info;
    Select(gen.mkRef(sym.owner.sourceModule), sym);
  }

  private def rebindSuper(base: Symbol, member: Symbol, prevowner: Symbol): Symbol = {
    var bcs = base.info.baseClasses.dropWhile(prevowner !=).tail;
    var sym: Symbol = NoSymbol;
    while (!bcs.isEmpty && sym == NoSymbol) {
      System.out.println("rebindsuper " + bcs.head + " " + bcs.head.info.nonPrivateDecl(member.name));//debug
      sym = member.overridingSymbol(bcs.head).suchThat(sym => !sym.hasFlag(DEFERRED));
      bcs = bcs.tail
    }
    assert(sym != NoSymbol, member);
    sym
  }

  private def implClass(iface: Symbol): Symbol =
    atPhase(flattenPhase)(iface.owner.info.decl(nme.implClassName(iface.name)));

  override def transformInfo(sym: Symbol, tp: Type): Type = tp match {
    case ClassInfoType(parents, decls, clazz) =>
      var parents1 = parents;
      var decls1 = decls;
      def addMember(member: Symbol): Symbol = {
        if (decls1 eq decls) decls1 = new Scope(decls.toList);
	System.out.println("new member of " + clazz + ":" + member);//debug
        decls1 enter member;
        member
      }
      def newGetter(field: Symbol): Symbol =
        clazz.newMethod(field.pos, nme.getterName(field.name))
          setFlag (field.flags & ~(PRIVATE | LOCAL | MUTABLE) | (ACCESSOR | DEFERRED | SYNTHETIC))
          setInfo MethodType(List(), field.info);
      def newSetter(field: Symbol): Symbol =
        clazz.newMethod(field.pos, nme.getterToSetter(nme.getterName(field.name)))
          setFlag (field.flags & ~(PRIVATE | LOCAL | MUTABLE) | (ACCESSOR | DEFERRED | SYNTHETIC))
          setInfo MethodType(List(field.info), UnitClass.tpe);

      if (clazz.isPackageClass) {
        for (val sym <- decls.elements) {
          if (sym.isImplClass) {
            sym setFlag lateMODULE;
            addMember(
	      clazz.newModule(sym.pos, sym.name.toTermName, sym.asInstanceOf[ClassSymbol])
                setInfo sym.tpe)
          }
        }
      } else if (clazz hasFlag lateINTERFACE) {
        val impl = implClass(clazz);
        assert(impl != NoSymbol, clazz);
        for (val member <- decls.toList) {
          if (!member.isMethod) {
            assert(member.isTerm && !member.hasFlag(DEFERRED));
            member.makeNotPrivate(clazz);
            var getter = member.getter(clazz);
            if (getter == NoSymbol) getter = addMember(newGetter(member));
            getter setFlag FINAL;
            if (!member.tpe.isInstanceOf[ConstantType]) {
              var setter = member.setter(clazz);
              if (getter == NoSymbol) setter = addMember(newSetter(member));
              setter setFlag FINAL
            }
          }
        }
      } else if (clazz.isImplClass) {
	transformInfo(clazz.owner, clazz.owner.info);
        parents1 = List();
        decls1 = new Scope(decls.toList filter isForwarded)
      } else if (!parents.isEmpty) {
        parents1 = parents.head :: (parents.tail map toInterface);
        for (val bc <- clazz.info.baseClasses.tail.takeWhile(parents.head.symbol !=)) {
          if (bc.isImplClass) {
            for (val member <- bc.info.decls.toList) {
              if (isForwarded(member) && (clazz.info.member(member.name).alternatives contains member)) {
                val member1 = member.cloneSymbol(clazz) setFlag MIXEDIN;
		member1.asInstanceOf[TermSymbol] setAlias member;
              } else if (member hasFlag SUPERACCESSOR) {
		member.asInstanceOf[TermSymbol] setAlias rebindSuper(clazz, member.alias, bc);
                addMember(member)
              }
            }
          }
        }
      }
      if ((parents1 eq parents) && (decls1 eq decls)) tp else ClassInfoType(parents, decls1, clazz)
    case MethodType(formals, restp) =>
      if (isForwarded(sym))
        MethodType(toInterface(sym.owner.thisType) :: formals, restp)
      else tp
    case _ =>
      tp
  }

  protected def newTransformer(unit: CompilationUnit): Transformer = new MixinTransformer;

  class MixinTransformer extends Transformer {

    var self: Symbol = _;
    var localTyper: analyzer.Typer = _;

    private def addNewDefs(clazz: Symbol, stats: List[Tree]): List[Tree] = {
      val newDefs = new ListBuffer[Tree];
      def addDef(pos: int, tree: Tree): unit = {
        System.out.println("add new def to " + clazz + ": " + tree);
        newDefs += localTyper.typed(atPos(pos)(tree))
      }
      def position(sym: Symbol) =
        if (sym.pos == Position.NOPOS) clazz.pos else sym.pos;
      def addDefDef(sym: Symbol, rhs: List[Symbol] => Tree): unit =
	addDef(position(sym), DefDef(sym, vparamss => rhs(vparamss.head)));
      if (!clazz.isImplClass) {
	atPhase(phase.next) {
	  for (val sym <- clazz.info.decls.toList) {
	    if (sym hasFlag MIXEDIN) {
	      if (sym hasFlag SUPERACCESSOR) {
		addDefDef(sym, vparams =>
		  Apply(Select(Super(clazz, nme.EMPTY.toTypeName), sym.alias), vparams map Ident))
	      } else if (sym hasFlag ACCESSOR) {
		addDefDef(sym, vparams =>
		  Select(This(clazz), sym.accessed))
	      } else if (isForwarded(sym)) {
		addDefDef(sym, vparams =>
		  Apply(staticRef(sym.alias), gen.This(clazz) :: (vparams map Ident)))
	      } else {
		addDef(position(sym), ValDef(sym))
	      }
	    }
	  }
	}
      }
      if (newDefs.hasNext) stats ::: newDefs.toList else stats
    }

    override def transform(tree: Tree): Tree = {
     try { //debug
      val sym = tree.symbol;
      val tree1 = tree match {
        case Template(parents, body) =>
	  localTyper = typer.atOwner(tree, currentOwner);
	  tree
        case DefDef(mods, name, tparams, List(vparams), tpt, rhs) =>
          if (isForwarded(sym)) {
            self = sym.newValue(sym.pos, nme.SELF)
	      setFlag PARAM
	      setInfo toInterface(currentOwner.thisType);
            val selfdef = ValDef(self) setType NoType;
            copy.DefDef(tree, mods, name, tparams, List(selfdef :: vparams), tpt, rhs)
          } else if (currentOwner.isImplClass) {
            EmptyTree
          } else tree
        case ValDef(_, _, _, _) if (currentOwner.isImplClass) =>
	  EmptyTree
        case Super(_, mix) if (mix == nme.EMPTY.toTypeName && currentOwner.isImplClass) =>
	  atPos(tree.pos){
	    localTyper.typed {
	      Select(Ident(self), sym.superAccessor(self.tpe.symbol))
	    }
	  }
        case Apply(Select(qual, _), args)
	if (isSelfRef(qual) && currentOwner.enclClass.isImplClass &&
	    sym.owner.isImplClass && isStatic(sym)) =>
	  atPos(tree.pos) {
	    atPhase(phase.next) {
	      localTyper.typed {
		Apply(staticRef(sym), Ident(self) :: args)
	      }
	    }
	  }
	case This(_) if (sym.isImplClass) =>
	  atPos(tree.pos)(gen.Ident(self))
	case _ =>
	  tree
      }
      val tree2 = super.transform(tree1);
      tree2 match {
        case Template(parents, body) =>
          copy.Template(tree2, parents, addNewDefs(currentOwner, body))
        case _ =>
	  tree2
      }
     } catch {//debug
       case ex: Throwable =>
	 System.out.println("exception when traversing " + tree);
       throw ex
     }
    }
  }
}