Skip to content

Commit

Permalink
translation for DelayedInit keeps more code in original class
Browse files Browse the repository at this point in the history
As before, the translation for DelayedInit creates a closure-class.
With this commit, its apply() method now just delegates to a synthetic method (in the original class) that contains the statements whose execution is to be delayed.

Previously the closure body would have the statements in question transplanted into it, so to say, which in turn required creating accessors in the original class, as well as reformulating accesses-on-this into accesses-via-outer.

Details about the mechanics of the rewriting can be found in the source comment for delayedEndpointDef()
  • Loading branch information
magarciaEPFL committed Apr 24, 2013
1 parent 7211432 commit b4fbb7b
Showing 1 changed file with 95 additions and 101 deletions.
196 changes: 95 additions & 101 deletions src/compiler/scala/tools/nsc/transform/Constructors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
val constrInfo: ConstrInfo = {
stats find (_.symbol.isPrimaryConstructor) match {
case Some(ddef @ DefDef(_, _, _, List(vparams), _, rhs @ Block(_, _))) =>
ConstrInfo(ddef, vparams map (_.symbol), rhs)
ConstrInfo(ddef, vparams map (_.symbol), rhs)
case x =>
// AnyVal constructor is OK
assert(clazz eq AnyValClass, "no constructor in template: impl = " + impl)
Expand Down Expand Up @@ -258,7 +258,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {

// Is symbol known to be accessed outside of the primary constructor,
// or is it a symbol whose definition cannot be omitted anyway?
def mustbeKept(sym: Symbol) = !maybeOmittable(sym) || (accessedSyms contains sym)
def mustbeKept(sym: Symbol) = isDelayedInitSubclass || !maybeOmittable(sym) || (accessedSyms contains sym)

// A traverser to set accessedSyms and outerAccessors
val accessTraverser = new Traverser {
Expand Down Expand Up @@ -382,12 +382,12 @@ abstract class Constructors extends Transform with ast.TreeDSL {
* 'specInstance$' is added in phase specialize.
*/
def guardSpecializedInitializer(stats: List[Tree]): List[Tree] = if (settings.nospecialization.value) stats else {
// split the statements in presuper and postsuper
// var (prefix, postfix) = stats0.span(tree => !((tree.symbol ne null) && tree.symbol.isConstructor))
// if (postfix.nonEmpty) {
// prefix = prefix :+ postfix.head
//postfix = postfix.tail
//}
// // split the statements in presuper and postsuper
// var (prefix, postfix) = stats0.span(tree => !((tree.symbol ne null) && tree.symbol.isConstructor))
// if (postfix.nonEmpty) {
// prefix = prefix :+ postfix.head
// postfix = postfix.tail
// }

if (usesSpecializedField && shouldGuard && stats.nonEmpty) {
// save them for duplication in the specialized subclass
Expand Down Expand Up @@ -417,76 +417,88 @@ abstract class Constructors extends Transform with ast.TreeDSL {
}
} else stats
}
/*
def isInitDef(stat: Tree) = stat match {
case dd: DefDef => dd.symbol == delayedInitMethod
case _ => false
}
*/

/* Create a getter or a setter and enter into `clazz` scope */
def addAccessor(sym: Symbol, name: TermName, flags: Long) = {
val m = clazz.newMethod(name, sym.pos, flags & ~(LOCAL | PRIVATE)) setPrivateWithin clazz
clazz.info.decls enter m
}
// def isInitDef(stat: Tree) = stat match {
// case dd: DefDef => dd.symbol == delayedInitMethod
// case _ => false
// }

def addGetter(sym: Symbol): Symbol = {
val getr = addAccessor(sym, sym.getterName, getterFlags(sym.flags))
getr setInfo MethodType(List(), sym.tpe)
defBuf += localTyper.typedPos(sym.pos)(DefDef(getr, Select(This(clazz), sym)))
getr
}

def addSetter(sym: Symbol): Symbol = {
sym setFlag MUTABLE
val setr = addAccessor(sym, sym.setterName, setterFlags(sym.flags))
setr setInfo MethodType(setr.newSyntheticValueParams(List(sym.tpe)), UnitClass.tpe)
defBuf += localTyper.typed {
//util.trace("adding setter def for "+setr) {
atPos(sym.pos) {
DefDef(setr, paramss =>
Assign(Select(This(clazz), sym), Ident(paramss.head.head)))
}//}
}
setr
}
/*
* Translation scheme for DelayedInit
* ----------------------------------
*
* Before returning, transformClassTemplate() rewrites DelayedInit subclasses.
* The list of statements that will end up in the primary constructor can be split into:
*
* (a) up to and including the super-constructor call.
* These statements can occur only in the (bytecode-level) primary constructor.
*
* (b) remaining statements
*
* The purpose of DelayedInit is leaving (b) out of the primary constructor and have their execution "delayed".
*
* The rewriting to achieve "delayed initialization" involves:
* (c) an additional, synthetic, public method encapsulating (b)
* (d) an additional, synthetic closure whose argless apply() just invokes (c)
* (e) after executing the statements in (a),
* the primary constructor instantiates (d) and passes it as argument
* to a `delayedInit()` invocation on the current instance.
* In turn, `delayedInit()` is a method defined as abstract in the `DelayedInit` trait
* so that it can be overridden (for an example see `scala.App`)
*
* The following helper methods prepare Trees as part of this rewriting:
*
* (f) `delayedEndpointDef()` prepares (c).
* A transformer, `constrStatTransformer`, is used to re-locate statements (b) from template-level
* to become statements in method (c). The main task here is re-formulating accesses to params
* of the primary constructors (to recap, (c) has zero-params) in terms of param-accessor fields.
* In a Delayed-Init subclass, each class-constructor gets a param-accessor field because `mustbeKept()` forces it.
*
* (g) `delayedInitClosure()` prepares (d)
*
* (h) `delayedInitCall()` prepares the `delayedInit()` invocation referred to in (e)
*
* Both (c) and (d) are added to the Template returned by `transformClassTemplate()`
*
* A note of historic interest: Previously the rewriting for DelayedInit would include in the closure body
* all of the delayed initialization sequence, which in turn required:
* - reformulating "accesses-on-this" into "accesses-on-outer", and
* - adding public getters and setters.
*
* @param stats the statements in (b) above
*
* @return the DefDef for (c) above
*
* */
def delayedEndpointDef(stats: List[Tree]): DefDef = {

def ensureAccessor(sym: Symbol)(acc: => Symbol) =
if (sym.owner == clazz && !sym.isMethod && sym.isPrivate) { // there's an access to a naked field of the enclosing class
val getr = acc
getr makeNotPrivate clazz
getr
} else {
if (sym.owner == clazz) sym makeNotPrivate clazz
NoSymbol
}
val methodName = currentUnit.freshTermName("delayedEndpoint$" + clazz.fullNameAsName('$').toString + "$")
val methodSym = clazz.newMethod(methodName, impl.pos, SYNTHETIC | FINAL)
methodSym setInfoAndEnter MethodType(Nil, UnitClass.tpe)

def ensureGetter(sym: Symbol): Symbol = ensureAccessor(sym) {
val getr = sym.getter(clazz)
if (getr != NoSymbol) getr else addGetter(sym)
}
// changeOwner needed because the `stats` contained in the DefDef were owned by the template, not long ago.
val blk = Block(stats, gen.mkZero(UnitClass.tpe)).changeOwner(impl.symbol -> methodSym)
val delayedDD = localTyper typed { DefDef(methodSym, Nil, blk) }

def ensureSetter(sym: Symbol): Symbol = ensureAccessor(sym) {
var setr = sym.setter(clazz, hasExpandedName = false)
if (setr == NoSymbol) setr = sym.setter(clazz, hasExpandedName = true)
if (setr == NoSymbol) setr = addSetter(sym)
setr
delayedDD.asInstanceOf[DefDef]
}

def delayedInitClosure(stats: List[Tree]) =
localTyper.typed {
/* @see overview at `delayedEndpointDef()` of the translation scheme for DelayedInit */
def delayedInitClosure(delayedEndPointSym: MethodSymbol): ClassDef = {
val dicl = localTyper.typed {
atPos(impl.pos) {
val closureClass = clazz.newClass(nme.delayedInitArg.toTypeName, impl.pos, SYNTHETIC | FINAL)
val closureParents = List(AbstractFunctionClass(0).tpe)

closureClass setInfoAndEnter new ClassInfoType(closureParents, newScope, closureClass)

val outerField = (
val outerField: TermSymbol = (
closureClass
newValue(nme.OUTER, impl.pos, PrivateLocal | PARAMACCESSOR)
setInfoAndEnter clazz.tpe
)
val applyMethod = (
val applyMethod: MethodSymbol = (
closureClass
newMethod(nme.apply, impl.pos, FINAL)
setInfoAndEnter MethodType(Nil, ObjectClass.tpe)
Expand All @@ -495,58 +507,32 @@ abstract class Constructors extends Transform with ast.TreeDSL {
val closureClassTyper = localTyper.atOwner(closureClass)
val applyMethodTyper = closureClassTyper.atOwner(applyMethod)

val constrStatTransformer = new Transformer {
override def transform(tree: Tree): Tree = tree match {
case This(_) if tree.symbol == clazz =>
applyMethodTyper.typed {
atPos(tree.pos) {
Select(This(closureClass), outerField)
}
}
case _ =>
super.transform {
tree match {
case Select(qual, _) =>
val getter = ensureGetter(tree.symbol)
if (getter != NoSymbol)
applyMethodTyper.typed {
atPos(tree.pos) {
Apply(Select(qual, getter), List())
}
}
else tree
case Assign(lhs @ Select(qual, _), rhs) =>
val setter = ensureSetter(lhs.symbol)
if (setter != NoSymbol)
applyMethodTyper.typed {
atPos(tree.pos) {
Apply(Select(qual, setter), List(rhs))
}
}
else tree
case _ =>
tree.changeOwner(impl.symbol -> applyMethod)
}
}
def applyMethodStat =
applyMethodTyper.typed {
atPos(impl.pos) {
val receiver = Select(This(closureClass), outerField)
Apply(Select(receiver, delayedEndPointSym), Nil)
}
}
}

def applyMethodStats = constrStatTransformer.transformTrees(stats)

val applyMethodDef = DefDef(
sym = applyMethod,
vparamss = ListOfNil,
rhs = Block(applyMethodStats, gen.mkAttributedRef(BoxedUnit_UNIT)))
rhs = Block(applyMethodStat, gen.mkAttributedRef(BoxedUnit_UNIT)))

ClassDef(
sym = closureClass,
constrMods = Modifiers(0),
vparamss = List(List(outerFieldDef)),
body = List(applyMethodDef),
body = applyMethodDef :: Nil,
superPos = impl.pos)
}
}

dicl.asInstanceOf[ClassDef]
}

/* @see overview at `delayedEndpointDef()` of the translation scheme for DelayedInit */
def delayedInitCall(closure: Tree) = localTyper.typedPos(impl.pos) {
gen.mkMethodCall(This(clazz), delayedInitMethod, Nil, List(New(closure.symbol.tpe, This(clazz))))
}
Expand All @@ -573,9 +559,15 @@ abstract class Constructors extends Transform with ast.TreeDSL {
isDelayedInitSubclass /*&& !(defBuf exists isInitDef)*/ && remainingConstrStats.nonEmpty

if (needsDelayedInit) {
val dicl = new ConstructorTransformer(unit) transform delayedInitClosure(remainingConstrStats)
val dlydEpDef: DefDef = delayedEndpointDef(remainingConstrStats)
defBuf += dlydEpDef
val dicl = {
// transform to make the closure-class' default constructor assign the the outer instance to its param-accessor field.
val diclx = new ConstructorTransformer(unit)
diclx transform delayedInitClosure(dlydEpDef.symbol.asInstanceOf[MethodSymbol])
}
defBuf += dicl
remainingConstrStats = List(delayedInitCall(dicl))
remainingConstrStats = delayedInitCall(dicl) :: Nil
}

// Assemble final constructor
Expand All @@ -597,12 +589,14 @@ abstract class Constructors extends Transform with ast.TreeDSL {
deriveTemplate(impl)(_ => defBuf.toList filter (stat => mustbeKept(stat.symbol)))
} // transformClassTemplate

override def transform(tree: Tree): Tree =
override def transform(tree: Tree): Tree = {
tree match {
case ClassDef(_,_,_,_) if !tree.symbol.isInterface && !isPrimitiveValueClass(tree.symbol) =>
deriveClassDef(tree)(transformClassTemplate)
case _ =>
super.transform(tree)
}
}

} // ConstructorTransformer
}

0 comments on commit b4fbb7b

Please sign in to comment.