summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMiguel Garcia <miguelalfredo.garcia@epfl.ch>2013-07-08 20:57:20 +0200
committerAdriaan Moors <adriaan.moors@typesafe.com>2013-08-08 12:09:48 -0700
commit1974313f70a021a8686510bcbcb92c3cc2b7c605 (patch)
tree8b548d85664c10267f51c82205900bdfee4d1b1d /src
parent6d5f3a05eae5bca391615b00e64a0f00c1f0928c (diff)
downloadscala-1974313f70a021a8686510bcbcb92c3cc2b7c605.tar.gz
scala-1974313f70a021a8686510bcbcb92c3cc2b7c605.tar.bz2
scala-1974313f70a021a8686510bcbcb92c3cc2b7c605.zip
separation of concerns: delayed-init in constructors
This commit modularizes TemplateTransformer by moving a few methods from TemplateTransformer into the newly added DelayedInitHelper. The methods in question - delayedInitCall() - delayedInitClosure() - delayedEndpointDef() build trees that rewriteDelayedInit() puts together. That way, all trace of rewriting related to delayed-init have vanished from TemplateTransformer and are encapsulated in DelayedInitHelper.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/transform/Constructors.scala271
1 files changed, 137 insertions, 134 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala
index 7757ae49bf..320d63c917 100644
--- a/src/compiler/scala/tools/nsc/transform/Constructors.scala
+++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala
@@ -90,7 +90,142 @@ abstract class Constructors extends Transform with ast.TreeDSL {
} // ConstructorTransformer
- class TemplateTransformer(unit: CompilationUnit, impl: Template) extends Transformer {
+ /*
+ * TemplateTransformer 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
+ *
+ * */
+ trait DelayedInitHelper { self: TemplateTransformer =>
+
+ private def delayedEndpointDef(stats: List[Tree]): DefDef = {
+
+ val methodName = currentUnit.freshTermName("delayedEndpoint$" + clazz.fullNameAsName('$').toString + "$")
+ val methodSym = clazz.newMethod(methodName, impl.pos, SYNTHETIC | FINAL)
+ methodSym setInfoAndEnter MethodType(Nil, UnitTpe)
+
+ // changeOwner needed because the `stats` contained in the DefDef were owned by the template, not long ago.
+ val blk = Block(stats, gen.mkZero(UnitTpe)).changeOwner(impl.symbol -> methodSym)
+ val delayedDD = localTyper typed { DefDef(methodSym, Nil, blk) }
+
+ delayedDD.asInstanceOf[DefDef]
+ }
+
+ private def delayedInitClosure(delayedEndPointSym: MethodSymbol): ClassDef = {
+ val satelliteClass = 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: TermSymbol = (
+ closureClass
+ newValue(nme.OUTER, impl.pos, PrivateLocal | PARAMACCESSOR)
+ setInfoAndEnter clazz.tpe
+ )
+ val applyMethod: MethodSymbol = (
+ closureClass
+ newMethod(nme.apply, impl.pos, FINAL)
+ setInfoAndEnter MethodType(Nil, ObjectTpe)
+ )
+ val outerFieldDef = ValDef(outerField)
+ val closureClassTyper = localTyper.atOwner(closureClass)
+ val applyMethodTyper = closureClassTyper.atOwner(applyMethod)
+
+ def applyMethodStat =
+ applyMethodTyper.typed {
+ atPos(impl.pos) {
+ val receiver = Select(This(closureClass), outerField)
+ Apply(Select(receiver, delayedEndPointSym), Nil)
+ }
+ }
+
+ val applyMethodDef = DefDef(
+ sym = applyMethod,
+ vparamss = ListOfNil,
+ rhs = Block(applyMethodStat, gen.mkAttributedRef(BoxedUnit_UNIT)))
+
+ ClassDef(
+ sym = closureClass,
+ constrMods = Modifiers(0),
+ vparamss = List(List(outerFieldDef)),
+ body = applyMethodDef :: Nil,
+ superPos = impl.pos)
+ }
+ }
+
+ satelliteClass.asInstanceOf[ClassDef]
+ }
+
+ private def delayedInitCall(closure: Tree) = localTyper.typedPos(impl.pos) {
+ gen.mkMethodCall(This(clazz), delayedInitMethod, Nil, List(New(closure.symbol.tpe, This(clazz))))
+ }
+
+ def rewriteDelayedInit() {
+ /* XXX This is not corect: remainingConstrStats.nonEmpty excludes too much,
+ * but excluding it includes too much. The constructor sequence being mimicked
+ * needs to be reproduced with total fidelity.
+ *
+ * See test case files/run/bug4680.scala, the output of which is wrong in many
+ * particulars.
+ */
+ val needsDelayedInit = (isDelayedInitSubclass && remainingConstrStats.nonEmpty)
+
+ if (needsDelayedInit) {
+ val delayedHook: DefDef = delayedEndpointDef(remainingConstrStats)
+ defBuf += delayedHook
+ val hookCallerClass = {
+ // transform to make the closure-class' default constructor assign the the outer instance to its param-accessor field.
+ val drillDown = new ConstructorTransformer(unit)
+ drillDown transform delayedInitClosure(delayedHook.symbol.asInstanceOf[MethodSymbol])
+ }
+ defBuf += hookCallerClass
+ remainingConstrStats = delayedInitCall(hookCallerClass) :: Nil
+ }
+ }
+
+ } // DelayedInitHelper
+
+ class TemplateTransformer(val unit: CompilationUnit, val impl: Template) extends Transformer with DelayedInitHelper {
val clazz = impl.symbol.owner // the transformed class
val stats = impl.body // the transformed template body
@@ -506,119 +641,6 @@ abstract class Constructors extends Transform with ast.TreeDSL {
} else stats
}
- /*
- * 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 = {
-
- val methodName = currentUnit.freshTermName("delayedEndpoint$" + clazz.fullNameAsName('$').toString + "$")
- val methodSym = clazz.newMethod(methodName, impl.pos, SYNTHETIC | FINAL)
- methodSym setInfoAndEnter MethodType(Nil, UnitTpe)
-
- // changeOwner needed because the `stats` contained in the DefDef were owned by the template, not long ago.
- val blk = Block(stats, gen.mkZero(UnitTpe)).changeOwner(impl.symbol -> methodSym)
- val delayedDD = localTyper typed { DefDef(methodSym, Nil, blk) }
-
- delayedDD.asInstanceOf[DefDef]
- }
-
- /* @see overview at `delayedEndpointDef()` of the translation scheme for DelayedInit */
- def delayedInitClosure(delayedEndPointSym: MethodSymbol): ClassDef = {
- val satelliteClass = 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: TermSymbol = (
- closureClass
- newValue(nme.OUTER, impl.pos, PrivateLocal | PARAMACCESSOR)
- setInfoAndEnter clazz.tpe
- )
- val applyMethod: MethodSymbol = (
- closureClass
- newMethod(nme.apply, impl.pos, FINAL)
- setInfoAndEnter MethodType(Nil, ObjectTpe)
- )
- val outerFieldDef = ValDef(outerField)
- val closureClassTyper = localTyper.atOwner(closureClass)
- val applyMethodTyper = closureClassTyper.atOwner(applyMethod)
-
- def applyMethodStat =
- applyMethodTyper.typed {
- atPos(impl.pos) {
- val receiver = Select(This(closureClass), outerField)
- Apply(Select(receiver, delayedEndPointSym), Nil)
- }
- }
-
- val applyMethodDef = DefDef(
- sym = applyMethod,
- vparamss = ListOfNil,
- rhs = Block(applyMethodStat, gen.mkAttributedRef(BoxedUnit_UNIT)))
-
- ClassDef(
- sym = closureClass,
- constrMods = Modifiers(0),
- vparamss = List(List(outerFieldDef)),
- body = applyMethodDef :: Nil,
- superPos = impl.pos)
- }
- }
-
- satelliteClass.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))))
- }
-
/* Return a pair consisting of (all statements up to and including superclass and trait constr calls, rest) */
def splitAtSuper(stats: List[Tree]) = {
def isConstr(tree: Tree): Boolean = tree match {
@@ -633,26 +655,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
val (uptoSuperStats, remainingConstrStats0) = splitAtSuper(constrStatBuf.toList)
var remainingConstrStats = remainingConstrStats0
- /* XXX This is not corect: remainingConstrStats.nonEmpty excludes too much,
- * but excluding it includes too much. The constructor sequence being mimicked
- * needs to be reproduced with total fidelity.
- *
- * See test case files/run/bug4680.scala, the output of which is wrong in many
- * particulars.
- */
- val needsDelayedInit = (isDelayedInitSubclass && remainingConstrStats.nonEmpty)
-
- if (needsDelayedInit) {
- val delayedHook: DefDef = delayedEndpointDef(remainingConstrStats)
- defBuf += delayedHook
- val hookCallerClass = {
- // transform to make the closure-class' default constructor assign the the outer instance to its param-accessor field.
- val drillDown = new ConstructorTransformer(unit)
- drillDown transform delayedInitClosure(delayedHook.symbol.asInstanceOf[MethodSymbol])
- }
- defBuf += hookCallerClass
- remainingConstrStats = delayedInitCall(hookCallerClass) :: Nil
- }
+ rewriteDelayedInit()
// Assemble final constructor
defBuf += deriveDefDef(constr)(_ =>