package scala.tools.nsc
package transform
import symtab._
import Flags._
import scala.collection.{ mutable, immutable }
import scala.collection.mutable.ListBuffer
abstract class Mixin extends InfoTransform with ast.TreeDSL {
import global._
import definitions._
import CODE._
val phaseName: String = "mixin"
override def phaseNewFlags: Long = lateMODULE | notABSTRACT
private val treatedClassInfos = collection.mutable.Map[Symbol, Type]()
private def isImplementedStatically(sym: Symbol) =
sym.owner.isImplClass && sym.isMethod &&
(!sym.isModule || sym.hasFlag(PRIVATE | LIFTED)) &&
(!(sym hasFlag (ACCESSOR | SUPERACCESSOR)) || sym.isLazy)
private def isStaticOnly(sym: Symbol) =
isImplementedStatically(sym) && sym.isImplOnly
private def isForwarded(sym: Symbol) =
isImplementedStatically(sym) && !sym.isImplOnly
private def toInterface(tp: Type): Type =
atPhase(currentRun.mixinPhase)(tp.typeSymbol.toInterface).tpe
private val toInterfaceMap = new TypeMap {
def apply(tp: Type): Type = mapOver( tp match {
case TypeRef(pre, sym, args) if (sym.isImplClass) =>
typeRef(pre, atPhase(currentRun.mixinPhase)(sym.toInterface), args)
case _ => tp
})
}
private def implClass(iface: Symbol): Symbol = {
val impl = iface.implClass
if (impl != NoSymbol) impl else erasure.implClass(iface)
}
private def rebindSuper(base: Symbol, member: Symbol, mixinClass: Symbol): Symbol =
atPhase(currentRun.picklerPhase.next) {
var bcs = base.info.baseClasses.dropWhile(mixinClass !=).tail
var sym: Symbol = NoSymbol
if (settings.debug.value)
log("starting rebindsuper " + base + " " + member + ":" + member.tpe +
" " + mixinClass + " " + base.info.baseClasses + "/" + bcs)
while (!bcs.isEmpty && sym == NoSymbol) {
if (settings.debug.value) {
val other = bcs.head.info.nonPrivateDecl(member.name);
log("rebindsuper " + bcs.head + " " + other + " " + other.tpe +
" " + other.isDeferred)
}
sym = member.matchingSymbol(bcs.head, base.thisType).suchThat(sym => !sym.hasFlag(DEFERRED | BRIDGE))
bcs = bcs.tail
}
assert(sym != NoSymbol, member)
sym
}
def isConcreteAccessor(member: Symbol) =
member.hasAccessorFlag && (!member.isDeferred || (member hasFlag lateDEFERRED))
def isOverriddenAccessor(member: Symbol, bcs: List[Symbol]): Boolean = atPhase(ownPhase) {
def hasOverridingAccessor(clazz: Symbol) = {
clazz.info.nonPrivateDecl(member.name).alternatives.exists(
sym =>
isConcreteAccessor(sym) &&
!sym.hasFlag(MIXEDIN) &&
matchesType(sym.tpe, member.tpe, true))
}
bcs.head != member.owner &&
(hasOverridingAccessor(bcs.head) || isOverriddenAccessor(member, bcs.tail))
}
def addMember(clazz: Symbol, member: Symbol): Symbol = {
if (settings.debug.value) log("new member of " + clazz + ":" + member.defString)
clazz.info.decls enter member
member.setFlag(MIXEDIN)
}
def needsExpandedSetterName(field: Symbol) = !field.isLazy && (
if (field.isMethod) field.hasStableFlag
else !field.isMutable
)
def addLateInterfaceMembers(clazz: Symbol) {
if ((treatedClassInfos get clazz) != Some(clazz.info)) {
treatedClassInfos(clazz) = clazz.info
assert(phase == currentRun.mixinPhase)
def newGetter(field: Symbol): Symbol = {
clazz.newMethod(field.pos, nme.getterName(field.name))
.setFlag(field.flags & ~(PRIVATE | LOCAL) | ACCESSOR | lateDEFERRED |
(if (field.isMutable) 0 else STABLE))
.setInfo(MethodType(List(), field.info))
}
def newSetter(field: Symbol): Symbol = {
val setterName = nme.getterToSetter(nme.getterName(field.name))
val setter = clazz.newMethod(field.pos, setterName)
.setFlag(field.flags & ~(PRIVATE | LOCAL) | ACCESSOR | lateDEFERRED)
setter.setInfo(MethodType(setter.newSyntheticValueParams(List(field.info)), UnitClass.tpe))
if (needsExpandedSetterName(field)) {
setter.name = nme.expandedSetterName(setter.name, clazz)
}
setter
}
clazz.info
val impl = implClass(clazz)
assert(impl != NoSymbol)
for (member <- impl.info.decls.toList) {
if (!member.isMethod && !member.isModule && !member.isModuleVar) {
assert(member.isTerm && !member.isDeferred, member)
if (member.getter(impl).isPrivate) {
member.makeNotPrivate(clazz)
}
val getter = member.getter(clazz)
if (getter == NoSymbol) addMember(clazz, newGetter(member))
if (!member.tpe.isInstanceOf[ConstantType] && !member.isLazy) {
val setter = member.setter(clazz, needsExpandedSetterName(member))
if (setter == NoSymbol) addMember(clazz, newSetter(member))
}
}
}
if (settings.debug.value) log("new defs of " + clazz + " = " + clazz.info.decls);
}
}
val initializer = new mutable.HashMap[Symbol, Symbol]
val deferredBitmaps: collection.mutable.Map[Symbol, List[Tree]] = new collection.mutable.HashMap[Symbol, List[Tree]]
def addMixedinMembers(clazz: Symbol, unit : CompilationUnit) {
def cloneBeforeErasure(iface: Symbol, clazz: Symbol, imember: Symbol): Symbol = {
val newSym = atPhase(currentRun.erasurePhase){
val res = imember.cloneSymbol(clazz)
if(iface.typeParams nonEmpty) res.setInfo(clazz.thisType.baseType(iface).memberInfo(imember))
res
}
newSym.updateInfo(imember.info.cloneInfo(newSym))
newSym
}
if (!(clazz hasFlag JAVA) && (treatedClassInfos get clazz) != Some(clazz.info)) {
treatedClassInfos(clazz) = clazz.info
assert(!clazz.isTrait, clazz)
assert(clazz.info.parents.nonEmpty, clazz)
addMixedinMembers(clazz.superClass, unit)
def mixinImplClassMembers(impl: Symbol, iface: Symbol) {
assert(
nme.isImplClassName(impl.name) || impl.isImplClass,
"%s (%s) is not a an implementation class, it cannot mix in %s".format(
impl, impl.defaultFlagString, iface)
)
for (member <- impl.info.decls.toList) {
if (isForwarded(member)) {
val imember = member.overriddenSymbol(iface)
if (imember.overridingSymbol(clazz) == NoSymbol &&
clazz.info.findMember(member.name, 0, lateDEFERRED, false).alternatives.contains(imember)) {
val member1 = addMember(
clazz,
cloneBeforeErasure(iface, clazz, imember) setPos clazz.pos resetFlag (DEFERRED | lateDEFERRED))
member1.asInstanceOf[TermSymbol] setAlias member;
}
}
}
}
def mixinTraitMembers(mixinClass: Symbol) {
for (member <- mixinClass.info.decls.toList) {
if (isConcreteAccessor(member)) {
if (isOverriddenAccessor(member, clazz.info.baseClasses)) {
if (settings.debug.value)
println("!!! is overridden val: "+member)
} else {
val member1 = addMember(
clazz,
cloneBeforeErasure(mixinClass, clazz, member)
setPos clazz.pos
resetFlag (DEFERRED | lateDEFERRED))
if (member.isLazy) {
var init = implClass(mixinClass).info.decl(member.name)
assert(init != NoSymbol, "Could not find initializer for " + member.name)
initializer(member1) = init
}
if (!member.isSetter)
member.tpe match {
case MethodType(Nil, ConstantType(_)) =>
;
case MethodType(Nil, TypeRef(_, UnitClass, _)) =>
;
case _ =>
val accessed = atPhase(currentRun.picklerPhase)(member.accessed)
val sym = atPhase(currentRun.erasurePhase){
clazz.newValue(member.pos, nme.getterToLocal(member.name)).setInfo(member.tpe.resultType)
}
sym.updateInfo(member.tpe.resultType)
addMember(clazz,
sym
setFlag (LOCAL | PRIVATE | member.getFlag(MUTABLE | LAZY))
setFlag (if (!member.hasStableFlag) MUTABLE else 0)
setAnnotations accessed.annotations)
}
}
} else if (member.isSuperAccessor) {
val member1 = addMember(clazz, member.cloneSymbol(clazz)) setPos clazz.pos
assert(member1.alias != NoSymbol, member1)
val alias1 = rebindSuper(clazz, member.alias, mixinClass)
member1.asInstanceOf[TermSymbol] setAlias alias1
} else if (member.isMethod && member.isModule && member.hasNoFlags(LIFTED | BRIDGE)) {
addMember(clazz, member.cloneSymbol(clazz))
.setPos(clazz.pos)
.resetFlag(DEFERRED | lateDEFERRED)
}
}
}
for (mc <- clazz.mixinClasses)
if (mc hasFlag lateINTERFACE) {
unit.depends += mc
addLateInterfaceMembers(mc)
mixinTraitMembers(mc)
mixinImplClassMembers(implClass(mc), mc)
}
}
}
override def transformInfo(sym: Symbol, tp: Type): Type = tp match {
case ClassInfoType(parents, decls, clazz) =>
var parents1 = parents
var decls1 = decls
if (!clazz.isPackageClass) {
atPhase(phase.next)(clazz.owner.info)
if (clazz.isImplClass) {
clazz setFlag lateMODULE
var sourceModule = clazz.owner.info.decls.lookup(sym.name.toTermName)
if (sourceModule != NoSymbol) {
sourceModule setPos sym.pos
sourceModule.flags = MODULE | FINAL
} else {
sourceModule = clazz.owner.newModule(
sym.pos, sym.name.toTermName, sym.asInstanceOf[ClassSymbol])
clazz.owner.info.decls enter sourceModule
}
sourceModule setInfo sym.tpe
assert(clazz.sourceModule != NoSymbol || clazz.isAnonymousClass,
clazz + " has no sourceModule: sym = " + sym + " sym.tpe = " + sym.tpe)
parents1 = List()
decls1 = new Scope(decls.toList filter isImplementedStatically)
} else if (!parents.isEmpty) {
parents1 = parents.head :: (parents.tail map toInterface)
}
}
if ((parents1 eq parents) && (decls1 eq decls)) tp
else ClassInfoType(parents1, decls1, clazz)
case MethodType(params, restp) =>
toInterfaceMap(
if (isImplementedStatically(sym)) {
val ownerParam = sym.newSyntheticValueParam(toInterface(sym.owner.typeOfThis))
MethodType(ownerParam :: params, restp)
} else
tp)
case _ =>
tp
}
import scala.collection._
def singleUseFields(templ: Template): collection.Map[Symbol, List[Symbol]] = {
val usedIn = new mutable.HashMap[Symbol, List[Symbol]] {
override def default(key: Symbol) = Nil
}
object SingleUseTraverser extends Traverser {
override def traverse(tree: Tree) {
tree match {
case Assign(lhs, rhs) => traverse(rhs)
case _ =>
if (tree.hasSymbol && tree.symbol != NoSymbol) {
val sym = tree.symbol
if ((sym.hasAccessorFlag || (sym.isTerm && !sym.isMethod))
&& sym.isPrivate
&& !(currentOwner.isGetter && currentOwner.accessed == sym)
&& !definitions.isValueClass(sym.tpe.resultType.typeSymbol)
&& sym.owner == templ.symbol.owner
&& !sym.isLazy
&& !tree.isDef) {
log("added use in: " + currentOwner + " -- " + tree)
usedIn(sym) ::= currentOwner
}
}
super.traverse(tree)
}
}
}
SingleUseTraverser(templ)
log("usedIn: " + usedIn)
usedIn filter {
case (_, member :: Nil) => member.isValue && member.isLazy
case _ => false
}
}
protected def newTransformer(unit: CompilationUnit): Transformer =
new MixinTransformer(unit)
class MixinTransformer(unit : CompilationUnit) extends Transformer {
private var self: Symbol = _
private val rootContext =
erasure.NoContext.make(EmptyTree, RootClass, new Scope)
private var localTyper: erasure.Typer = _
private def typedPos(pos: Position)(tree: Tree) = localTyper typed { atPos(pos)(tree) }
private var lazyValNullables: mutable.MultiMap[Symbol, Symbol] = _
import scala.collection._
private val fieldOffset: mutable.Map[Symbol, Int] = new mutable.HashMap[Symbol, Int]
private def preTransform(tree: Tree): Tree = {
val sym = tree.symbol
tree match {
case Template(parents, self, body) =>
localTyper = erasure.newTyper(rootContext.make(tree, currentOwner))
atPhase(phase.next)(currentOwner.owner.info)
if (!currentOwner.isTrait && !isValueClass(currentOwner))
addMixedinMembers(currentOwner, unit)
else if (currentOwner hasFlag lateINTERFACE)
addLateInterfaceMembers(currentOwner)
tree
case DefDef(mods, name, tparams, List(vparams), tpt, rhs) =>
if (currentOwner.isImplClass) {
if (isImplementedStatically(sym)) {
sym setFlag notOVERRIDE
self = sym.newValue(sym.pos, nme.SELF)
.setFlag(PARAM)
.setInfo(toInterface(currentOwner.typeOfThis));
val selfdef = ValDef(self) setType NoType
treeCopy.DefDef(tree, mods, name, tparams, List(selfdef :: vparams), tpt, rhs)
} else {
EmptyTree
}
} else {
if (currentOwner.isTrait && sym.isSetter && !atPhase(currentRun.picklerPhase)(sym.isDeferred)) {
sym.addAnnotation(AnnotationInfo(TraitSetterAnnotationClass.tpe, List(), List()))
}
tree
}
case Apply(tapp @ TypeApply(fn, List(arg)), List()) =>
if (arg.tpe.typeSymbol.isImplClass) {
val ifacetpe = toInterface(arg.tpe)
arg.tpe = ifacetpe
tapp.tpe = MethodType(List(), ifacetpe)
tree.tpe = ifacetpe
}
tree
case ValDef(_, _, _, _) if currentOwner.isImplClass =>
EmptyTree
case _ =>
tree
}
}
private def selfRef(pos: Position) =
gen.mkAttributedIdent(self) setPos pos
private def transformSuper(tree: Tree) = tree match {
case Super(qual, _) =>
transformThis(qual)
case _ =>
tree
}
private def transformThis(tree: Tree) = tree match {
case This(_) if tree.symbol.isImplClass =>
assert(tree.symbol == currentOwner.enclClass)
selfRef(tree.pos)
case _ =>
tree
}
private def staticRef(sym: Symbol): Tree = {
sym.owner.info
sym.owner.owner.info
if (sym.owner.sourceModule == NoSymbol)
assert(false, "" + sym + " in " + sym.owner + " in " + sym.owner.owner +
" " + sym.owner.owner.info.decls.toList)
REF(sym.owner.sourceModule) DOT sym
}
@inline private def bitmapOperation[T](field: Symbol, transientCase: => T, privateCase: => T, rest: => T): T =
if (field.accessed.hasAnnotation(TransientAttr))
transientCase
else if (field.hasFlag(PRIVATE | notPRIVATE))
privateCase
else
rest
private def addNewDefs(clazz: Symbol, stats: List[Tree]): List[Tree] = {
val newDefs = new ListBuffer[Tree]
def attributedDef(pos: Position, tree: Tree): Tree = {
if (settings.debug.value) log("add new def to " + clazz + ": " + tree)
typedPos(pos)(tree)
}
def position(sym: Symbol) =
if (sym.pos == NoPosition) clazz.pos else sym.pos
def addDef(pos: Position, tree: Tree) {
newDefs += attributedDef(pos, tree)
}
def addDefDef(sym: Symbol, rhs: List[Symbol] => Tree) {
addDef(position(sym), DefDef(sym, rhs(sym.paramss.head)))
}
def add(stats: List[Tree], newDefs: List[Tree]) = {
val newSyms = newDefs map (_.symbol)
def isNotDuplicate(tree: Tree) = tree match {
case DefDef(_, _, _, _, _, _) =>
val sym = tree.symbol
!(sym.isDeferred &&
(newSyms exists (nsym => nsym.name == sym.name && (nsym.tpe matches sym.tpe))))
case _ =>
true
}
if (newDefs.isEmpty) stats
else newDefs ::: stats.filter(isNotDuplicate)
}
def addDeferredBitmap(clazz: Symbol, tree: Tree) {
deferredBitmaps(clazz) = typedPos(clazz.pos)(tree)::deferredBitmaps.getOrElse(clazz, List())
}
def completeSuperAccessor(stat: Tree) = stat match {
case DefDef(mods, name, tparams, List(vparams), tpt, EmptyTree)
if (stat.symbol.isSuperAccessor) =>
val rhs0 = (Super(clazz, tpnme.EMPTY) DOT stat.symbol.alias)(vparams map (v => Ident(v.symbol)): _*)
val rhs1 = localTyper.typed(atPos(stat.pos)(rhs0), stat.symbol.tpe.resultType)
val rhs2 = atPhase(currentRun.mixinPhase)(transform(rhs1))
if (settings.debug.value)
log("complete super acc " + stat.symbol + stat.symbol.locationString +
" " + rhs1 + " " + stat.symbol.alias + stat.symbol.alias.locationString +
"/" + stat.symbol.alias.owner.hasFlag(lateINTERFACE))
treeCopy.DefDef(stat, mods, name, tparams, List(vparams), tpt, rhs2)
case _ =>
stat
}
import lazyVals._
def localBitmapField(field: Symbol) =
field.accessed.hasAnnotation(TransientAttr) || field.hasFlag(PRIVATE | notPRIVATE) || checkinitField(field)
def bitmapFor(clazz0: Symbol, offset: Int, field: Symbol, searchParents:Boolean = true): Symbol = {
def bitmapLazyName: Name =
bitmapOperation(field, nme.bitmapNameForTransient(offset / FLAGS_PER_WORD),
nme.bitmapNameForPrivate(offset / FLAGS_PER_WORD),
nme.bitmapName(offset / FLAGS_PER_WORD))
def bitmapCheckinitName: Name =
bitmapOperation(field, nme.bitmapNameForCheckinitTransient(offset / FLAGS_PER_WORD),
nme.bitmapNameForCheckinit(offset / FLAGS_PER_WORD),
nme.bitmapNameForCheckinit(offset / FLAGS_PER_WORD))
val checkinitField = !field.isLazy
val bitmapName = if (checkinitField) bitmapCheckinitName else bitmapLazyName
def createBitmap: Symbol = {
val sym = clazz0.newVariable(clazz0.pos, bitmapName).setInfo(IntClass.tpe)
atPhase(currentRun.typerPhase) {
sym addAnnotation AnnotationInfo(VolatileAttr.tpe, Nil, Nil)
}
bitmapOperation(field,
{sym.addAnnotation(AnnotationInfo(TransientAttr.tpe, Nil, Nil)); sym.setFlag(PRIVATE | LOCAL)},
sym.setFlag(PRIVATE | LOCAL),
sym.setFlag(if (checkinitField) (PRIVATE | LOCAL) else PROTECTED))
clazz0.info.decls.enter(sym)
if (clazz0 == clazz)
addDef(clazz.pos, VAL(sym) === ZERO)
else {
addDeferredBitmap(clazz0, VAL(sym) === ZERO)
}
sym
}
var sym = clazz0.info.member(bitmapName)
assert(!sym.hasFlag(OVERLOADED))
if (sym == NoSymbol) {
if (searchParents && !localBitmapField(field))
bitmapForParents(clazz0, offset, field) match {
case Some(bitmap) =>
sym = bitmap
case None =>
sym = createBitmap
}
else
sym = createBitmap
}
sym
}
def bitmapForParents(clazz0: Symbol, offset: Int, valSym: Symbol): Option[Symbol] = {
def requiredBitmaps(fs: Int): Int = if (fs == 0) -1 else (fs - 1) / FLAGS_PER_WORD
var res:Option[Symbol] = None
val bitmapNum = offset / FLAGS_PER_WORD
for (cl <- clazz0.info.baseClasses.tail.filter(c => !c.isTrait && !c.hasFlag(JAVA))
if res == None) {
val fields0 = usedBits(cl)
if (requiredBitmaps(fields0) < bitmapNum) {
val fields1 = cl.info.decls.filter(decl => fieldWithBitmap(decl) && !localBitmapField(decl)).size
if (requiredBitmaps(fields0 + fields1) >= bitmapNum)
res = Some(bitmapFor(cl, offset, valSym, false))
else return None
}
}
res
}
def mkSetFlag(clazz: Symbol, offset: Int, valSym: Symbol): Tree = {
val bmp = bitmapFor(clazz, offset, valSym)
val mask = LIT(1 << (offset % FLAGS_PER_WORD))
def x = This(clazz) DOT bmp
x === (x INT_| mask)
}
def mkTest(clazz: Symbol, mask: Tree, bitmapSym: Symbol, equalToZero: Boolean): Tree = {
def lhs = (This(clazz) DOT bitmapSym) INT_& mask
if (equalToZero) lhs INT_== ZERO
else lhs INT_!= ZERO
}
def mkLazyDef(clazz: Symbol, lzyVal: Symbol, init: List[Tree], retVal: Tree, offset: Int): Tree = {
def nullify(sym: Symbol): Tree = {
val sym1 = if (sym.hasAccessorFlag) sym.accessed else sym
Select(This(clazz), sym1) === LIT(null)
}
val bitmapSym = bitmapFor(clazz, offset, lzyVal)
val mask = LIT(1 << (offset % FLAGS_PER_WORD))
def cond = mkTest(clazz, mask, bitmapSym, true)
val nulls = (lazyValNullables(lzyVal).toList sortBy (_.id) map nullify)
def syncBody = init ::: List(mkSetFlag(clazz, offset, lzyVal), UNIT)
log("nulling fields inside " + lzyVal + ": " + nulls)
val result = gen.mkDoubleCheckedLocking(clazz, cond, syncBody, nulls)
typedPos(init.head.pos)(BLOCK(result, retVal))
}
def mkInnerClassAccessorDoubleChecked(attrThis: Tree, rhs: Tree): Tree =
rhs match {
case Block(List(assign), returnTree) =>
val Assign(moduleVarRef, _) = assign
val cond = Apply(Select(moduleVarRef, nme.eq),List(Literal(Constant(null))))
val doubleSynchrTree = gen.mkDoubleCheckedLocking(attrThis, cond, List(assign), Nil)
Block(List(doubleSynchrTree), returnTree)
case _ =>
assert(false, "Invalid getter " + rhs + " for module in class " + clazz)
EmptyTree
}
def mkCheckedAccessor(clazz: Symbol, retVal: Tree, offset: Int, pos: Position, fieldSym: Symbol): Tree = {
val bitmapSym = bitmapFor(clazz, offset, fieldSym.getter(fieldSym.owner))
val mask = LIT(1 << (offset % FLAGS_PER_WORD))
val msg = "Uninitialized field: " + unit.source + ": " + pos.line
val result =
IF (mkTest(clazz, mask, bitmapSym, false)) .
THEN (retVal) .
ELSE (THROW(UninitializedErrorClass, LIT(msg)))
typedPos(pos)(BLOCK(result, retVal))
}
def addCheckedGetters(clazz: Symbol, stats: List[Tree]): List[Tree] = {
val stats1 = for (stat <- stats; sym = stat.symbol) yield stat match {
case DefDef(mods, name, tp, vp, tpt, rhs)
if sym.isLazy && rhs != EmptyTree && !clazz.isImplClass =>
assert(fieldOffset.isDefinedAt(sym))
val rhs1 = if (sym.tpe.resultType.typeSymbol == UnitClass)
mkLazyDef(clazz, sym, List(rhs), UNIT, fieldOffset(sym))
else {
val Block(stats, res) = rhs
mkLazyDef(clazz, sym, stats, Select(This(clazz), res.symbol), fieldOffset(sym))
}
treeCopy.DefDef(stat, mods, name, tp, vp, tpt, rhs1)
case DefDef(mods, name, tp, vp, tpt, rhs)
if needsInitFlag(sym) && rhs != EmptyTree && !clazz.isImplClass && !clazz.isTrait =>
assert(fieldOffset.isDefinedAt(sym))
val rhs1 = (mkCheckedAccessor(clazz, _: Tree, fieldOffset(sym), stat.pos, sym))(
if (sym.tpe.resultType.typeSymbol == UnitClass) UNIT else rhs
)
treeCopy.DefDef(stat, mods, name, tp, vp, tpt, rhs1)
case DefDef(mods, name, tp, vp, tpt, rhs) if sym.isConstructor =>
treeCopy.DefDef(stat, mods, name, tp, vp, tpt, addInitBits(clazz, rhs))
case DefDef(mods, name, tp, vp, tpt, rhs)
if settings.checkInit.value && !clazz.isTrait && sym.isSetter =>
val getter = sym.getter(clazz)
if (needsInitFlag(getter) && fieldOffset.isDefinedAt(getter))
treeCopy.DefDef(stat, mods, name, tp, vp, tpt,
Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter))), UNIT))
else
stat
case DefDef(mods, name, tp, vp, tpt, rhs)
if sym.isModule && (!clazz.isTrait || clazz.isImplClass) && !sym.hasFlag(BRIDGE) =>
val attrThis =
if (clazz.isImplClass) {
gen.mkAttributedIdent(vp.head.head.symbol)
} else
gen.mkAttributedThis(clazz)
val rhs1 = mkInnerClassAccessorDoubleChecked(attrThis, rhs)
treeCopy.DefDef(stat, mods, name, tp, vp, tpt, typedPos(stat.pos)(rhs1))
case _ => stat
}
stats1
}
def needsInitFlag(sym: Symbol) = {
val res = (settings.checkInit.value
&& sym.isGetter
&& !sym.isInitializedToDefault
&& !sym.hasFlag(PARAMACCESSOR | SPECIALIZED | LAZY)
&& !sym.accessed.hasFlag(PRESUPER)
&& !sym.isOuterAccessor
&& !(sym.owner isSubClass DelayedInitClass))
res
}
def addInitBits(clazz: Symbol, rhs: Tree): Tree = {
new Transformer {
override def transformStats(stats: List[Tree], exprOwner: Symbol) = {
val stats1 = stats flatMap { stat => stat match {
case Assign(lhs @ Select(This(_), _), rhs) =>
val sym = clazz.info.decl(nme.getterName(lhs.symbol.name))
.suchThat(_.isGetter)
if (rhs == EmptyTree)
List()
else if (sym != NoSymbol && needsInitFlag(sym) && fieldOffset.isDefinedAt(sym)) {
log("adding checked getter for: " + sym + " " + Flags.flagsToString(lhs.symbol.flags))
List(stat, localTyper.typed(mkSetFlag(clazz, fieldOffset(sym), sym)))
} else {
List(stat)
}
case Apply(setter @ Select(Ident(self), _), List(EmptyTree)) if setter.symbol.isSetter =>
List()
case _ => List(stat)
}
}
super.transformStats(stats1, exprOwner)
}
}.transform(rhs)
}
def fieldWithBitmap(field: Symbol) = {
field.info
field.isLazy && field.isMethod && !field.isDeferred
}
def checkinitField(field: Symbol) =
needsInitFlag(field) && !field.isDeferred
def usedBits(clazz0: Symbol): Int = {
def needsBitmap(field: Symbol) = field.owner != clazz0 && fieldWithBitmap(field)
var bits = 0
for {
cl <- clazz0.info.baseClasses.tail
if !cl.isTrait && !cl.hasFlag(JAVA)
field <- cl.info.decls.iterator
if needsBitmap(field) && !localBitmapField(field)
} bits += 1
bits
}
def buildFieldPositions(clazz0: Symbol) {
var fields = usedBits(clazz0)
var fieldsPrivate = 0
var fieldsTransient = 0
var fieldsCheckinit = 0
var fieldsCheckinitTransient = 0
for (f <- clazz0.info.decls.iterator) {
if (settings.debug.value) log(f.fullName + " -> " + fields)
if (fieldWithBitmap(f)) {
val (idx, _) =
bitmapOperation(f, (fieldsTransient, fieldsTransient += 1),
(fieldsPrivate, fieldsPrivate += 1),
(fields, fields += 1))
fieldOffset(f) = idx
} else if (checkinitField(f)) {
val (idx, _) =
bitmapOperation(f, (fieldsCheckinitTransient, fieldsCheckinitTransient += 1),
(fieldsCheckinit, fieldsCheckinit += 1),
(fieldsCheckinit, fieldsCheckinit += 1))
fieldOffset(f) = idx
}
}
}
buildFieldPositions(clazz)
var stats1 = addCheckedGetters(clazz, stats)
deferredBitmaps.remove(clazz) match {
case Some(deferred) =>
stats1 = add(stats1, deferred)
case None =>
}
for (sym <- clazz.info.decls.toList) {
if (sym hasFlag MIXEDIN) {
if (clazz hasFlag lateINTERFACE) {
addDefDef(sym, vparamss => EmptyTree)
} else if (!clazz.isTrait) {
if ((sym hasFlag ACCESSOR) &&
(!(sym hasFlag DEFERRED) || (sym hasFlag lateDEFERRED))) {
addDefDef(sym, vparams => {
val accessedRef = sym.tpe match {
case MethodType(List(), ConstantType(c)) => Literal(c)
case _ =>
if (sym.isLazy && sym.isGetter) {
val rhs1 =
if (sym.tpe.resultType.typeSymbol == UnitClass)
mkLazyDef(clazz, sym, List(Apply(staticRef(initializer(sym)), List(gen.mkAttributedThis(clazz)))), UNIT, fieldOffset(sym))
else {
val assign = atPos(sym.pos) {
Assign(Select(This(sym.accessed.owner), sym.accessed) ,
Apply(staticRef(initializer(sym)), gen.mkAttributedThis(clazz) :: Nil))
}
mkLazyDef(clazz, sym, List(assign), Select(This(clazz), sym.accessed), fieldOffset(sym))
}
rhs1
} else if (sym.getter(sym.owner).tpe.resultType.typeSymbol == UnitClass) {
UNIT
} else {
Select(This(clazz), sym.accessed)
}
}
if (sym.isSetter) {
val isOverriddenSetter =
nme.isTraitSetterName(sym.name) && {
sym.allOverriddenSymbols match {
case other :: _ =>
isOverriddenAccessor(other.getter(other.owner), clazz.info.baseClasses)
case _ =>
false
}
}
if (isOverriddenSetter) UNIT
else accessedRef match {
case Literal(_) => accessedRef
case _ =>
val init = Assign(accessedRef, Ident(vparams.head))
val getter = sym.getter(clazz)
if (needsInitFlag(getter))
Block(List(init, mkSetFlag(clazz, fieldOffset(getter), getter)), UNIT)
else
init
}
} else if (needsInitFlag(sym)) {
mkCheckedAccessor(clazz, accessedRef, fieldOffset(sym), sym.pos, sym)
} else
gen.mkCheckInit(accessedRef)
})
} else if (sym.isModule && !(sym hasFlag LIFTED | BRIDGE)) {
val vdef = gen.mkModuleVarDef(sym)
addDef(position(sym), vdef)
val rhs = gen.newModule(sym, vdef.symbol.tpe)
val assignAndRet = gen.mkAssignAndReturn(vdef.symbol, rhs)
val attrThis = gen.mkAttributedThis(clazz)
val rhs1 = mkInnerClassAccessorDoubleChecked(attrThis, assignAndRet)
addDef(position(sym), DefDef(sym, rhs1))
} else if (!sym.isMethod) {
addDef(position(sym), ValDef(sym))
} else if (sym.isSuperAccessor) {
addDefDef(sym, vparams => EmptyTree)
} else {
assert(sym.alias != NoSymbol, sym)
addDefDef(sym, vparams =>
Apply(staticRef(sym.alias), gen.mkAttributedThis(clazz) :: (vparams map Ident)))
}
}
}
}
stats1 = add(stats1, newDefs.toList)
if (!clazz.isTrait) stats1 = stats1 map completeSuperAccessor
stats1
}
private def nullableFields(templ: Template) = {
val nullables = new mutable.HashMap[Symbol, mutable.Set[Symbol]] with mutable.MultiMap[Symbol, Symbol] {
override def default(key: Symbol) = mutable.Set.empty
}
if (templ.symbol.owner.info.decls.exists(_.isLazy)) {
val uses = singleUseFields(templ)
for ((field, users) <- uses; lazyFld <- users) {
nullables.addBinding(lazyFld, field)
}
}
nullables
}
private def postTransform(tree: Tree): Tree = {
val sym = tree.symbol
if (tree.tpe.typeSymbol.isImplClass &&
((tree.symbol eq null) || !tree.symbol.isImplClass))
tree.tpe = toInterface(tree.tpe);
tree match {
case Template(parents, self, body) =>
val parents1 = currentOwner.info.parents map (t => TypeTree(t) setPos tree.pos)
lazyValNullables = nullableFields(tree.asInstanceOf[Template])
val body1 = addNewDefs(currentOwner, body)
treeCopy.Template(tree, parents1, self, body1)
case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List())
if (tree.symbol == Object_asInstanceOf && (qual.tpe <:< targ.tpe)) =>
qual
case Apply(Select(qual, _), args) =>
def staticCall(target: Symbol) = {
if (target == NoSymbol)
assert(false, "" + sym + ":" + sym.tpe + " " + sym.owner + " " + implClass(sym.owner) + " " + implClass(sym.owner).info.member(sym.name) + " " + atPhase(phase.prev)(implClass(sym.owner).info.member(sym.name).tpe) + " " + phase);
typedPos(tree.pos)(Apply(staticRef(target), transformSuper(qual) :: args))
}
if (isStaticOnly(sym)) {
staticCall(sym)
} else qual match {
case Super(_, mix) =>
if (mix == tpnme.EMPTY && currentOwner.enclClass.isImplClass)
assert(false, "illegal super in trait: " + currentOwner.enclClass + " " + tree);
if (sym.owner hasFlag lateINTERFACE) {
if (sym.hasAccessorFlag) {
assert(args.isEmpty)
val sym1 = sym.overridingSymbol(currentOwner.enclClass)
typedPos(tree.pos)((transformSuper(qual) DOT sym1)())
} else {
staticCall(atPhase(phase.prev)(sym.overridingSymbol(implClass(sym.owner))))
}
} else {
assert(!currentOwner.enclClass.isImplClass)
tree
}
case _ =>
tree
}
case This(_) =>
transformThis(tree)
case Select(Super(_, _), name) =>
tree
case Select(qual, name) if sym.owner.isImplClass && !isStaticOnly(sym) =>
assert(!sym.isMethod, "no method allowed here: %s%s %s".format(sym, sym.isImplOnly, flagsToString(sym.flags)))
val iface = toInterface(sym.owner.tpe).typeSymbol
val getter = sym.getter(iface)
assert(getter != NoSymbol)
typedPos(tree.pos)((qual DOT getter)())
case Assign(Apply(lhs @ Select(qual, _), List()), rhs) =>
def setter = lhs.symbol.setter(
toInterface(lhs.symbol.owner.tpe).typeSymbol,
needsExpandedSetterName(lhs.symbol)
) setPos lhs.pos
typedPos(tree.pos) { (qual DOT setter)(rhs) }
case _ =>
tree
}
}
override def transform(tree: Tree): Tree = {
try {
val outerTyper = localTyper
val tree1 = super.transform(preTransform(tree))
val res = atPhase(phase.next)(postTransform(tree1))
localTyper = outerTyper
res
} catch {
case ex: Throwable =>
if (settings.debug.value) Console.println("exception when traversing " + tree)
throw ex
}
}
}
}