package scala.tools.nsc
package transform
import symtab._
import Flags.{ CASE => _, _ }
import scala.collection.mutable.ListBuffer
import matching.{ Patterns, ParallelMatching }
abstract class ExplicitOuter extends InfoTransform
with Patterns
with ParallelMatching
with TypingTransformers
with ast.TreeDSL
{
import global._
import definitions._
import CODE._
import Debug.TRACE
override def phaseNewFlags: Long = notPRIVATE | notPROTECTED | lateFINAL
val phaseName: String = "explicitouter"
override def changesBaseClasses = false
protected def newTransformer(unit: CompilationUnit): Transformer =
new ExplicitOuterTransformer(unit)
private def isInner(clazz: Symbol) =
!clazz.isPackageClass && !clazz.outerClass.isStaticOwner
private def haveSameOuter(parent: Type, clazz: Symbol) = parent match {
case TypeRef(pre, sym, _) =>
val owner = clazz.owner
sym.isClass && owner.isClass &&
owner == sym.owner &&
owner.thisType =:= pre
case _ => false
}
def hasOuterField(clazz: Symbol) = {
val parents = clazz.info.parents
isInner(clazz) && !clazz.isTrait && {
parents.isEmpty || !haveSameOuter(parents.head, clazz)
}
}
private def outerField(clazz: Symbol): Symbol = {
val result = clazz.info.member(nme.OUTER_LOCAL)
assert(result != NoSymbol, "no outer field in "+clazz+" at "+phase)
result
}
class RemoveBindingsTransformer(toRemove: Set[Symbol]) extends Transformer {
override def transform(tree: Tree) = tree match {
case Bind(_, body) if toRemove(tree.symbol) =>
TRACE("Dropping unused binding: " + tree.symbol)
super.transform(body)
case _ => super.transform(tree)
}
}
def isArraySeqTest(lhs: Type, rhs: Type) =
(ArrayClass.tpe <:< lhs.widen) && (rhs.widen matchesPattern SeqClass.tpe)
def outerAccessor(clazz: Symbol): Symbol = {
val firstTry = clazz.info.decl(nme.expandedName(nme.OUTER, clazz))
if (firstTry != NoSymbol && firstTry.outerSource == clazz) firstTry
else clazz.info.decls find (_.outerSource == clazz) getOrElse NoSymbol
}
def transformInfo(sym: Symbol, tp: Type): Type = tp match {
case MethodType(params, restpe1) =>
val restpe = transformInfo(sym, restpe1)
if (sym.owner.isTrait && ((sym hasFlag (ACCESSOR | SUPERACCESSOR)) || sym.isModule)) {
sym.makeNotPrivate(sym.owner)
}
if (sym.owner.isTrait && sym.isProtected) sym setFlag notPROTECTED
if (sym.isClassConstructor && isInner(sym.owner)) {
val p = sym.newValueParameter(sym.pos, "arg" + nme.OUTER)
.setInfo(sym.owner.outerClass.thisType)
MethodType(p :: params, restpe)
} else if (restpe ne restpe1)
MethodType(params, restpe)
else tp
case ClassInfoType(parents, decls, clazz) =>
var decls1 = decls
if (isInner(clazz) && !clazz.isInterface) {
decls1 = decls.cloneScope
val outerAcc = clazz.newMethod(clazz.pos, nme.OUTER)
outerAcc expandName clazz
val restpe = if (clazz.isTrait) clazz.outerClass.tpe else clazz.outerClass.thisType
decls1 enter (clazz.newOuterAccessor(clazz.pos) setInfo MethodType(Nil, restpe))
if (hasOuterField(clazz)) {
val access = if (clazz.isFinal) PRIVATE | LOCAL else PROTECTED
decls1 enter (
clazz.newValue(clazz.pos, nme.OUTER_LOCAL)
setFlag (SYNTHETIC | PARAMACCESSOR | access)
setInfo clazz.outerClass.thisType
)
}
}
if (!clazz.isTrait && !parents.isEmpty) {
for (mc <- clazz.mixinClasses) {
val mixinOuterAcc: Symbol = atPhase(phase.next)(outerAccessor(mc))
if (mixinOuterAcc != NoSymbol) {
if (decls1 eq decls) decls1 = decls.cloneScope
val newAcc = mixinOuterAcc.cloneSymbol(clazz)
newAcc resetFlag DEFERRED setInfo (clazz.thisType memberType mixinOuterAcc)
decls1 enter newAcc
}
}
}
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 _ =>
if (sym.owner.isTrait && sym.hasLocalFlag &&
(sym.getter(sym.owner.toInterface) == NoSymbol))
sym.makeNotPrivate(sym.owner)
tp
}
abstract class OuterPathTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
protected var outerParam: Symbol = NoSymbol
protected def outerValue: Tree =
if (outerParam != NoSymbol) ID(outerParam)
else outerSelect(THIS(currentClass))
private def outerSelect(base: Tree): Tree = {
val outerAcc = outerAccessor(base.tpe.typeSymbol.toInterface)
val currentClass = this.currentClass
val outerFld =
if (outerAcc.owner == currentClass &&
base.tpe =:= currentClass.thisType &&
outerAcc.owner.isFinal)
outerField(currentClass) suchThat (_.owner == currentClass)
else
NoSymbol
val path =
if (outerFld != NoSymbol) Select(base, outerFld)
else Apply(Select(base, outerAcc), Nil)
localTyper typed path
}
protected def outerPath(base: Tree, from: Symbol, to: Symbol): Tree = {
if (from == to || from.isImplClass && from.toInterface == to) base
else outerPath(outerSelect(base), from.outerClass, to)
}
override def transform(tree: Tree): Tree = {
val savedOuterParam = outerParam
try {
tree match {
case Template(_, _, _) =>
outerParam = NoSymbol
case DefDef(_, _, _, vparamss, _, _) =>
if (tree.symbol.isClassConstructor && isInner(tree.symbol.owner)) {
outerParam = vparamss.head.head.symbol
assert(outerParam.name startsWith nme.OUTER, outerParam.name)
}
case _ =>
}
super.transform(tree)
}
finally outerParam = savedOuterParam
}
}
class ExplicitOuterTransformer(unit: CompilationUnit) extends OuterPathTransformer(unit) {
transformer =>
def outerFieldDef: Tree = VAL(outerField(currentClass)) === EmptyTree
def outerAccessorDef: Tree = {
val outerAcc = outerAccessor(currentClass)
var rhs: Tree =
if (outerAcc.isDeferred) EmptyTree
else This(currentClass) DOT outerField(currentClass)
localTyper typed {
(DEF(outerAcc) withPos currentClass.pos withType null) === rhs
}
}
def mixinOuterAccessorDef(mixinClass: Symbol): Tree = {
val outerAcc = outerAccessor(mixinClass) overridingSymbol currentClass
assert(outerAcc != NoSymbol)
val path =
if (mixinClass.owner.isTerm) THIS(mixinClass.owner.enclClass)
else gen.mkAttributedQualifier(currentClass.thisType baseType mixinClass prefix)
localTyper typed {
(DEF(outerAcc) withPos currentClass.pos) === {
transformer.transform(path) AS_ANY outerAcc.info.resultType
}
}
}
def setNotFlags(sym: Symbol, flags: Int*) {
for (f <- flags ; notFlag <- notFlagMap get f)
if (sym hasFlag f)
sym setFlag notFlag
}
def matchTranslation(tree: Match) = {
val Match(selector, cases) = tree
var nselector = transform(selector)
def makeGuardDef(vs: List[Symbol], guard: Tree) = {
val gdname = unit.freshTermName("gd")
val method = currentOwner.newMethod(tree.pos, gdname) setFlag SYNTHETIC
val fmls = vs map (_.tpe)
val tpe = new MethodType(method newSyntheticValueParams fmls, BooleanClass.tpe)
method setInfo tpe
localTyper typed (DEF(method) === {
new ChangeOwnerTraverser(currentOwner, method) traverse guard
new TreeSymSubstituter(vs, method.paramss.head) transform (guard)
})
}
val nguard = new ListBuffer[Tree]
val ncases =
for (CaseDef(pat, guard, body) <- cases) yield {
val patternIdents = for (b @ Bind(_, _) <- pat) yield b.symbol
val references: Set[Symbol] = Set(guard, body) flatMap { t => for (id @ Ident(name) <- t) yield id.symbol }
val (used, unused) = patternIdents partition references
val strippedPat = if (unused.isEmpty) pat else new RemoveBindingsTransformer(unused.toSet) transform pat
val gdcall =
if (guard == EmptyTree) EmptyTree
else {
val guardDef = makeGuardDef(used, guard)
nguard += transform(guardDef)
localTyper typed (Ident(guardDef.symbol) APPLY (used map Ident))
}
(CASE(transform(strippedPat)) IF gdcall) ==> transform(body)
}
def isUncheckedAnnotation(tpe: Type) = tpe hasAnnotation UncheckedClass
def isSwitchAnnotation(tpe: Type) = tpe hasAnnotation SwitchClass
val (checkExhaustive, requireSwitch) = nselector match {
case Typed(nselector1, tpt) =>
val unchecked = isUncheckedAnnotation(tpt.tpe)
if (unchecked)
nselector = nselector1
(!unchecked, isSwitchAnnotation(tpt.tpe))
case _ =>
(true, false)
}
val t = atPos(tree.pos) {
val context = MatrixContext(currentRun.currentUnit, transform, localTyper, currentOwner, tree.tpe)
val t_untyped = handlePattern(nselector, ncases, checkExhaustive, context)
if (requireSwitch) t_untyped match {
case Block(_, Match(_, _)) =>
case _ =>
unit.error(tree.pos, "could not emit switch for @switch annotated match")
}
localTyper.typed(t_untyped, context.matchResultType)
}
if (nguard.isEmpty) t
else Block(nguard.toList, t) setType t.tpe
}
override def transform(tree: Tree): Tree = {
val sym = tree.symbol
if (sym != null && sym.isType)
setNotFlags(sym, PRIVATE, PROTECTED)
tree match {
case Template(parents, self, decls) =>
val newDefs = new ListBuffer[Tree]
atOwner(tree, currentOwner) {
if (!currentClass.isInterface || (currentClass hasFlag lateINTERFACE)) {
if (isInner(currentClass)) {
if (hasOuterField(currentClass))
newDefs += outerFieldDef
newDefs += outerAccessorDef
}
if (!currentClass.isTrait)
for (mc <- currentClass.mixinClasses)
if (outerAccessor(mc) != NoSymbol)
newDefs += mixinOuterAccessorDef(mc)
}
}
super.transform(
treeCopy.Template(tree, parents, self,
if (newDefs.isEmpty) decls else decls ::: newDefs.toList)
)
case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
if (sym.isClassConstructor) {
rhs match {
case Literal(_) =>
sys.error("unexpected case")
case _ =>
val clazz = sym.owner
val vparamss1 =
if (isInner(clazz)) {
val outerParam =
sym.newValueParameter(sym.pos, nme.OUTER) setInfo outerField(clazz).info
((ValDef(outerParam) setType NoType) :: vparamss.head) :: vparamss.tail
} else vparamss
super.transform(treeCopy.DefDef(tree, mods, name, tparams, vparamss1, tpt, rhs))
}
} else
super.transform(tree)
case This(qual) =>
if (sym == currentClass || sym.hasModuleFlag && sym.isStatic) tree
else atPos(tree.pos)(outerPath(outerValue, currentClass.outerClass, sym))
case Select(qual, name) =>
if (currentClass != sym.owner)
sym.makeNotPrivate(sym.owner)
val qsym = qual.tpe.widen.typeSymbol
if (sym.isProtected &&
(qsym.isTrait || !(qual.isInstanceOf[Super] || (qsym isSubClass currentClass))))
sym setFlag notPROTECTED
super.transform(tree)
case Apply(sel @ Select(qual, nme.CONSTRUCTOR), args) if isInner(sel.symbol.owner) =>
val outerVal = atPos(tree.pos)(qual match {
case _: This =>
assert(outerParam != NoSymbol)
outerValue
case _ =>
gen.mkAttributedQualifier(qual.tpe.prefix match {
case NoPrefix => sym.owner.outerClass.thisType
case x => x
})
})
super.transform(treeCopy.Apply(tree, sel, outerVal :: args))
case mch: Match =>
matchTranslation(mch)
case _ =>
if (settings.Xmigration28.value) tree match {
case TypeApply(fn @ Select(qual, _), args) if fn.symbol == Object_isInstanceOf || fn.symbol == Any_isInstanceOf =>
if (isArraySeqTest(qual.tpe, args.head.tpe))
unit.warning(tree.pos, "An Array will no longer match as Seq[_].")
case _ => ()
}
val x = super.transform(tree)
if (x.tpe eq null) x
else x setType transformInfo(currentOwner, x.tpe)
}
}
override def transformUnit(unit: CompilationUnit) {
atPhase(phase.next)(super.transformUnit(unit))
}
}
override def newPhase(prev: scala.tools.nsc.Phase): StdPhase =
new Phase(prev)
class Phase(prev: scala.tools.nsc.Phase) extends super.Phase(prev) {
override val checkable = false
override def run() {
super.run
Pattern.clear()
}
}
}
<iframe src="https://xuwei-k.github.io/scala-compiler-sxr/scala-compiler-2.9.1/scala/tools/nsc/transform/ExplicitOuter.scala.html" width="1280" height="720" frameborder="0"> </iframe>