diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala new file mode 100644 index 0000000000..2a3fc9a19f --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -0,0 +1,330 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +import symtab._; +import Flags._; +import util.ListBuffer; +import collection.mutable.HashMap; + +abstract class ExplicitOuter extends InfoTransform { + import global._; + import definitions._; + import posAssigner.atPos; + + override def phaseNewFlags: long = notPRIVATE | notPROTECTED; + + /** the following two members override abstract members in Transform */ + val phaseName: String = "explicitouter"; + override def changesBaseClasses = false; + + protected def newTransformer(unit: CompilationUnit): Transformer = + new ExplicitOuterTransformer(unit); + + private def outerClass(clazz: Symbol): Symbol = + if (clazz.owner.isClass) clazz.owner + else outerClass(if (clazz.isClassLocalToConstructor) clazz.owner.owner else clazz.owner); + + private def isStatic(clazz: Symbol) = + clazz.isPackageClass || outerClass(clazz).isStaticOwner; + + /** The type transformation method: + * 1. Add an outer paramter to the formal parameters of a constructor or mixin constructor + * in a non-static class; + * 2. Add a mixin constructor $init$ to all traits except interfaces + * Leave all other types unchanged. + */ + def transformInfo(sym: Symbol, tp: Type): Type = tp match { + case MethodType(formals, restpe) => + //todo: needed? + if (sym.owner.isTrait && (sym hasFlag PROTECTED)) sym setFlag notPROTECTED; + if (sym.isConstructor && !isStatic(sym.owner)) + MethodType(formals ::: List(outerClass(sym.owner).toInterface.thisType), restpe) + else tp; + case ClassInfoType(parents, decls, clazz) => + var decls1 = decls; + if (!(clazz hasFlag INTERFACE)) { + if (!isStatic(clazz)) { + decls1 = new Scope(decls1.toList); + val outerAcc = clazz.newMethod(clazz.pos, nme.OUTER); + if ((clazz hasFlag TRAIT) || (decls.toList exists (.isClass))) + outerAcc.expandName(clazz); + decls1 enter ( + outerAcc setFlag (PARAMACCESSOR | ACCESSOR | STABLE) + setInfo MethodType(List(), outerClass(clazz).thisType)); + decls1 enter (clazz.newValue(clazz.pos, nme.getterToLocal(outerAcc.name)) + setFlag (LOCAL | PRIVATE | PARAMACCESSOR | (outerAcc getFlag EXPANDEDNAME)) + setInfo outerClass(clazz).thisType); + } + if (clazz.isTrait) { + decls1 = new Scope(decls1.toList); + decls1 enter makeMixinConstructor(clazz); + } + } + if (decls1 eq decls) tp else ClassInfoType(parents, decls1, clazz) + case PolyType(tparams, restp) => + val restp1 = transformInfo(sym, restp); + if (restp eq restp1) tp else PolyType(tparams, restp1) + case _ => + tp + } + + private def outerMember(tp: Type): Symbol = { + var e = tp.decls.elems; + while (e != null && !(e.sym.originalName.startsWith(nme.OUTER) && (e.sym hasFlag ACCESSOR))) + e = e.next; + assert(e != null, tp); + e.sym + } + + private def makeMixinConstructor(clazz: Symbol): Symbol = + clazz.newMethod(clazz.pos, nme.MIXIN_CONSTRUCTOR) setInfo MethodType(List(), UnitClass.tpe); + + /** A base class for transformers that maintain `outerParam' values for + * outer parameters of constructors. + * The class provides methods for referencing via outer. + */ + class OuterPathTransformer extends Transformer { + + /** The directly enclosing outer parameter, if we are in a constructor */ + protected var outerParam: Symbol = NoSymbol; + + /** The first outer selection from currently transformed tree + */ + protected def outerValue: Tree = + if (outerParam != NoSymbol) gen.Ident(outerParam) + else outerSelect(gen.This(currentOwner.enclClass)); + + /** The path + * `base'.$outer ... .$outer + * which refers to the outer instance `to' of value `base + */ + protected def outerPath(base: Tree, to: Symbol): Tree = + if (base.tpe.symbol == to) base else outerPath(outerSelect(base), to); + + /** Select and apply outer accessor from `base' + */ + private def outerSelect(base: Tree): Tree = { + val otp = outerClass(base.tpe.symbol).thisType; + Apply( + Select(base, outerMember(base.tpe)) setType MethodType(List(), otp), + List()) setType otp + } + + override def transform(tree: Tree): Tree = { + try {//debug + val savedOuterParam = outerParam; + tree match { + case Template(_, _) => + outerParam = NoSymbol; + case DefDef(_, _, _, vparamss, _, _) => + if (tree.symbol.isConstructor && !(isStatic(tree.symbol.owner))) { + val lastParam = vparamss.head.last; + assert(lastParam.name.startsWith(nme.OUTER), tree); + outerParam = lastParam.symbol + } + case _ => + } + val result = super.transform(tree); + outerParam = savedOuterParam; + result + } catch {//debug + case ex: Throwable => + System.out.println("exception when transforming " + tree); + throw ex + } + } + } + + class ExplicitOuterTransformer(unit: CompilationUnit) extends Transformer { + + /** The first step performs the following transformations: + * 1. A class which is not an interface and is not static gets an outer link + * (@see outerDefs) + * 2. A mixin which is not also an interface gets a mixin constructor + * (@see mixinConstructorDef) + * 3. Constructor bodies are augmented by calls to supermixin constructors + * (@see addMixinConstructorCalls) + * 4. A constructor of a class with an outer link gets an outer parameter. + * 5. A reference C.this where C refers to an outer class is replaced by a selection + * this.$outer ... .$outer (@see outerPath) + * 7. A call to a constructor Q.<init>(args) or Q.$init$(args) where Q != this and + * the constructor belongs to a non-static class is augmented by an outer argument. + * E.g. Q.<init>(args, OUTER) where OUTER is the qualifier corresponding to the + * singleton type Q. + * 8. A call to a constructor this.<init>(args) in a secondary constructor + * is augmented to this.<init>(args, OUTER) where OUTER is the last parameter + * of the secondary constructor. + */ + private val firstTransformer = new OuterPathTransformer { + + var localTyper: analyzer.Typer = typer; + + /** The two definitions + * val outer : C.this.type _; + * def outer(): C.this.type = outer ; + * Here, C is the class enclosing the class `clazz' containing the two definitions. + */ + def outerDefs(clazz: Symbol): List[Tree] = { + val outerDef = outerMember(clazz.info); + val outerVal = outerDef.accessed; + List( + localTyper.typed { + atPos(clazz.pos) { + ValDef(outerVal) + } + }, + localTyper.typed { + atPos(clazz.pos) { + DefDef(outerDef, vparamss => Select(This(clazz), outerVal)) + } + }) + } + + /** The mixin constructor definition + * def $init$(): Unit = () + */ + def mixinConstructorDef(clazz: Symbol): Tree = + localTyper.typed { + val constr = clazz.primaryConstructor; + atPhase(currentRun.explicitOuterPhase) { + // necessary so that we do not include an outer parameter already here; + // this will be added later in transform. + DefDef(constr, vparamss => Literal(())) + } + } + + /** Add calls to supermixin constructors + * super[mix].$init$() + * to `tree'. `tree' which is assumed to be the body of a constructor of class `clazz'. + */ + def addMixinConstructorCalls(tree: Tree, clazz: Symbol): Tree = { + def mixinConstructorCall(mixin: Symbol): Tree = + atPos(tree.pos) { + Apply( + localTyper.typedOperator { + Select(Super(clazz, mixin.name), mixin.primaryConstructor) + }, + List()) setType UnitClass.tpe; // don't type this with typed(...), + // as constructor arguments might be missing + } + val mixinConstructorCalls = + for (val mixin <- clazz.info.parents.tail; !(mixin.symbol hasFlag INTERFACE)) yield + mixinConstructorCall(mixin.symbol); + tree match { + case Block(supercall :: stats, expr) => + assert(supercall match { + case Apply(Select(Super(_, _), _), _) => true + case _ => false + }); + copy.Block(tree, supercall :: mixinConstructorCalls ::: stats, expr); + case Block(_, _) => + assert(false, tree); tree + case expr => + Block(mixinConstructorCalls, expr) setType expr.tpe setPos expr.pos; + } + } + + /** The first-step transformation method */ + override def transform(tree: Tree): Tree = { + val sym = tree.symbol; + val tree1 = tree match { + case Template(parents, decls) => + val savedLocalTyper = localTyper; + localTyper = localTyper.atOwner(tree, currentOwner); + var decls1 = decls; + if (!(currentOwner hasFlag INTERFACE)) { + if (!isStatic(currentOwner)) + decls1 = decls1 ::: outerDefs(currentOwner); // (1) + if (currentOwner.isTrait) + decls1 = decls1 ::: List(mixinConstructorDef(currentOwner)) // (2) + } + localTyper = savedLocalTyper; + copy.Template(tree, parents, decls1); + case constrDef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) + if (sym.isConstructor) => + val vparamss1 = + if (isStatic(sym.owner)) vparamss + else { // (4) + val outerField = outerMember(sym.owner.info).accessed; + val outerParam = sym.newValueParameter(sym.pos, nme.OUTER) setInfo outerField.info; + List(vparamss.head ::: List(ValDef(outerParam) setType NoType)) + } + val rhs1 = + if ((sym.isPrimaryConstructor || sym.isMixinConstructor) && sym.owner != ArrayClass) + addMixinConstructorCalls(rhs, sym.owner); // (3) + else rhs; + copy.DefDef(tree, mods, name, tparams, vparamss1, tpt, rhs1); + case This(qual) => + if (sym == currentOwner.enclClass || (sym hasFlag MODULE) && sym.isStatic) tree + else atPos(tree.pos)(outerPath(outerValue, sym)); // (5) + case Apply(sel @ Select(qual, name), args) + if ((name == nme.CONSTRUCTOR || name == nme.MIXIN_CONSTRUCTOR) && !isStatic(sel.symbol.owner)) => + val outerVal = + atPos(tree.pos) { + if (qual.isInstanceOf[This]) { assert(outerParam != NoSymbol); outerValue } // (8) + else { + var pre = qual.tpe.prefix; + if (pre == NoPrefix) pre = outerClass(sym.owner).thisType; + gen.mkQualifier(pre) + } + } + copy.Apply(tree, sel, args ::: List(outerVal)) + case _ => + tree + } + super.transform(tree1) + } + } + + /** The second step performs the following transformations: + * 2. Remove private modifiers from members M of mixins T. (@see makeNotPrivate) + * 3. Remove `private' modifier from class members M that are accessed from an inner class. + * 4. Remove `protected' modifier from class members M that are accessed + * without a super qualifier accessed from an inner class. + * 5. Remove `private' and `protected' modifiers from type symbols + */ + private val secondTransformer = new Transformer { + + /** The second-step transformation method */ + override def transform(tree: Tree): Tree = { + val sym = tree.symbol; + val tree1 = super.transform(tree); + tree1 match { + case DefDef(_, _, _, _, _, _) => + if (sym.owner.isTrait && (sym hasFlag (ACCESSOR | SUPERACCESSOR))) + sym.makeNotPrivate(sym.owner); //(2) + tree1 + case Select(qual, name) => + if (currentOwner.enclClass != sym.owner) // (3) + sym.makeNotPrivate(sym.owner); + if ((sym hasFlag PROTECTED) && //(4) + !(qual.isInstanceOf[Super] || + (qual.tpe.widen.symbol isSubClass currentOwner.enclClass))) + sym setFlag notPROTECTED; + tree1 + case _ => + if (sym != null && sym.isType) {//(5) + if (sym hasFlag PRIVATE) sym setFlag notPRIVATE; + if (sym hasFlag PROTECTED) sym setFlag notPROTECTED; + } + tree1 + } + } + } + + /** The main transformation method: + * First, perform step 1 on whole tree of compilation unit. + * Then, perform step 2 on resulting tree + */ + override def transform(tree: Tree) = + atPhase(phase.next) { + secondTransformer.transform(firstTransformer.transform(tree)) + } + } +} + + |