Skip to content
This repository
Browse code

translation for DelayedInit keeps more code in original class

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...
commit b4fbb7be0e24b41dec22b57fd24c04f865290a9f 1 parent 7211432
Miguel Garcia magarciaEPFL authored

Showing 1 changed file with 95 additions and 101 deletions. Show diff stats Hide diff stats

  1. +95 101 src/compiler/scala/tools/nsc/transform/Constructors.scala
196 src/compiler/scala/tools/nsc/transform/Constructors.scala
@@ -81,7 +81,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
81 81 val constrInfo: ConstrInfo = {
82 82 stats find (_.symbol.isPrimaryConstructor) match {
83 83 case Some(ddef @ DefDef(_, _, _, List(vparams), _, rhs @ Block(_, _))) =>
84   - ConstrInfo(ddef, vparams map (_.symbol), rhs)
  84 + ConstrInfo(ddef, vparams map (_.symbol), rhs)
85 85 case x =>
86 86 // AnyVal constructor is OK
87 87 assert(clazz eq AnyValClass, "no constructor in template: impl = " + impl)
@@ -258,7 +258,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
258 258
259 259 // Is symbol known to be accessed outside of the primary constructor,
260 260 // or is it a symbol whose definition cannot be omitted anyway?
261   - def mustbeKept(sym: Symbol) = !maybeOmittable(sym) || (accessedSyms contains sym)
  261 + def mustbeKept(sym: Symbol) = isDelayedInitSubclass || !maybeOmittable(sym) || (accessedSyms contains sym)
262 262
263 263 // A traverser to set accessedSyms and outerAccessors
264 264 val accessTraverser = new Traverser {
@@ -382,12 +382,12 @@ abstract class Constructors extends Transform with ast.TreeDSL {
382 382 * 'specInstance$' is added in phase specialize.
383 383 */
384 384 def guardSpecializedInitializer(stats: List[Tree]): List[Tree] = if (settings.nospecialization.value) stats else {
385   - // split the statements in presuper and postsuper
386   - // var (prefix, postfix) = stats0.span(tree => !((tree.symbol ne null) && tree.symbol.isConstructor))
387   - // if (postfix.nonEmpty) {
388   - // prefix = prefix :+ postfix.head
389   - //postfix = postfix.tail
390   - //}
  385 + // // split the statements in presuper and postsuper
  386 + // var (prefix, postfix) = stats0.span(tree => !((tree.symbol ne null) && tree.symbol.isConstructor))
  387 + // if (postfix.nonEmpty) {
  388 + // prefix = prefix :+ postfix.head
  389 + // postfix = postfix.tail
  390 + // }
391 391
392 392 if (usesSpecializedField && shouldGuard && stats.nonEmpty) {
393 393 // save them for duplication in the specialized subclass
@@ -417,76 +417,88 @@ abstract class Constructors extends Transform with ast.TreeDSL {
417 417 }
418 418 } else stats
419 419 }
420   -/*
421   - def isInitDef(stat: Tree) = stat match {
422   - case dd: DefDef => dd.symbol == delayedInitMethod
423   - case _ => false
424   - }
425   -*/
426 420
427   - /* Create a getter or a setter and enter into `clazz` scope */
428   - def addAccessor(sym: Symbol, name: TermName, flags: Long) = {
429   - val m = clazz.newMethod(name, sym.pos, flags & ~(LOCAL | PRIVATE)) setPrivateWithin clazz
430   - clazz.info.decls enter m
431   - }
  421 + // def isInitDef(stat: Tree) = stat match {
  422 + // case dd: DefDef => dd.symbol == delayedInitMethod
  423 + // case _ => false
  424 + // }
432 425
433   - def addGetter(sym: Symbol): Symbol = {
434   - val getr = addAccessor(sym, sym.getterName, getterFlags(sym.flags))
435   - getr setInfo MethodType(List(), sym.tpe)
436   - defBuf += localTyper.typedPos(sym.pos)(DefDef(getr, Select(This(clazz), sym)))
437   - getr
438   - }
439 426
440   - def addSetter(sym: Symbol): Symbol = {
441   - sym setFlag MUTABLE
442   - val setr = addAccessor(sym, sym.setterName, setterFlags(sym.flags))
443   - setr setInfo MethodType(setr.newSyntheticValueParams(List(sym.tpe)), UnitClass.tpe)
444   - defBuf += localTyper.typed {
445   - //util.trace("adding setter def for "+setr) {
446   - atPos(sym.pos) {
447   - DefDef(setr, paramss =>
448   - Assign(Select(This(clazz), sym), Ident(paramss.head.head)))
449   - }//}
450   - }
451   - setr
452   - }
  427 + /*
  428 + * Translation scheme for DelayedInit
  429 + * ----------------------------------
  430 + *
  431 + * Before returning, transformClassTemplate() rewrites DelayedInit subclasses.
  432 + * The list of statements that will end up in the primary constructor can be split into:
  433 + *
  434 + * (a) up to and including the super-constructor call.
  435 + * These statements can occur only in the (bytecode-level) primary constructor.
  436 + *
  437 + * (b) remaining statements
  438 + *
  439 + * The purpose of DelayedInit is leaving (b) out of the primary constructor and have their execution "delayed".
  440 + *
  441 + * The rewriting to achieve "delayed initialization" involves:
  442 + * (c) an additional, synthetic, public method encapsulating (b)
  443 + * (d) an additional, synthetic closure whose argless apply() just invokes (c)
  444 + * (e) after executing the statements in (a),
  445 + * the primary constructor instantiates (d) and passes it as argument
  446 + * to a `delayedInit()` invocation on the current instance.
  447 + * In turn, `delayedInit()` is a method defined as abstract in the `DelayedInit` trait
  448 + * so that it can be overridden (for an example see `scala.App`)
  449 + *
  450 + * The following helper methods prepare Trees as part of this rewriting:
  451 + *
  452 + * (f) `delayedEndpointDef()` prepares (c).
  453 + * A transformer, `constrStatTransformer`, is used to re-locate statements (b) from template-level
  454 + * to become statements in method (c). The main task here is re-formulating accesses to params
  455 + * of the primary constructors (to recap, (c) has zero-params) in terms of param-accessor fields.
  456 + * In a Delayed-Init subclass, each class-constructor gets a param-accessor field because `mustbeKept()` forces it.
  457 + *
  458 + * (g) `delayedInitClosure()` prepares (d)
  459 + *
  460 + * (h) `delayedInitCall()` prepares the `delayedInit()` invocation referred to in (e)
  461 + *
  462 + * Both (c) and (d) are added to the Template returned by `transformClassTemplate()`
  463 + *
  464 + * A note of historic interest: Previously the rewriting for DelayedInit would include in the closure body
  465 + * all of the delayed initialization sequence, which in turn required:
  466 + * - reformulating "accesses-on-this" into "accesses-on-outer", and
  467 + * - adding public getters and setters.
  468 + *
  469 + * @param stats the statements in (b) above
  470 + *
  471 + * @return the DefDef for (c) above
  472 + *
  473 + * */
  474 + def delayedEndpointDef(stats: List[Tree]): DefDef = {
453 475
454   - def ensureAccessor(sym: Symbol)(acc: => Symbol) =
455   - if (sym.owner == clazz && !sym.isMethod && sym.isPrivate) { // there's an access to a naked field of the enclosing class
456   - val getr = acc
457   - getr makeNotPrivate clazz
458   - getr
459   - } else {
460   - if (sym.owner == clazz) sym makeNotPrivate clazz
461   - NoSymbol
462   - }
  476 + val methodName = currentUnit.freshTermName("delayedEndpoint$" + clazz.fullNameAsName('$').toString + "$")
  477 + val methodSym = clazz.newMethod(methodName, impl.pos, SYNTHETIC | FINAL)
  478 + methodSym setInfoAndEnter MethodType(Nil, UnitClass.tpe)
463 479
464   - def ensureGetter(sym: Symbol): Symbol = ensureAccessor(sym) {
465   - val getr = sym.getter(clazz)
466   - if (getr != NoSymbol) getr else addGetter(sym)
467   - }
  480 + // changeOwner needed because the `stats` contained in the DefDef were owned by the template, not long ago.
  481 + val blk = Block(stats, gen.mkZero(UnitClass.tpe)).changeOwner(impl.symbol -> methodSym)
  482 + val delayedDD = localTyper typed { DefDef(methodSym, Nil, blk) }
468 483
469   - def ensureSetter(sym: Symbol): Symbol = ensureAccessor(sym) {
470   - var setr = sym.setter(clazz, hasExpandedName = false)
471   - if (setr == NoSymbol) setr = sym.setter(clazz, hasExpandedName = true)
472   - if (setr == NoSymbol) setr = addSetter(sym)
473   - setr
  484 + delayedDD.asInstanceOf[DefDef]
474 485 }
475 486
476   - def delayedInitClosure(stats: List[Tree]) =
477   - localTyper.typed {
  487 + /* @see overview at `delayedEndpointDef()` of the translation scheme for DelayedInit */
  488 + def delayedInitClosure(delayedEndPointSym: MethodSymbol): ClassDef = {
  489 + val dicl = localTyper.typed {
478 490 atPos(impl.pos) {
479 491 val closureClass = clazz.newClass(nme.delayedInitArg.toTypeName, impl.pos, SYNTHETIC | FINAL)
480 492 val closureParents = List(AbstractFunctionClass(0).tpe)
481 493
482 494 closureClass setInfoAndEnter new ClassInfoType(closureParents, newScope, closureClass)
483 495
484   - val outerField = (
  496 + val outerField: TermSymbol = (
485 497 closureClass
486 498 newValue(nme.OUTER, impl.pos, PrivateLocal | PARAMACCESSOR)
487 499 setInfoAndEnter clazz.tpe
488 500 )
489   - val applyMethod = (
  501 + val applyMethod: MethodSymbol = (
490 502 closureClass
491 503 newMethod(nme.apply, impl.pos, FINAL)
492 504 setInfoAndEnter MethodType(Nil, ObjectClass.tpe)
@@ -495,58 +507,32 @@ abstract class Constructors extends Transform with ast.TreeDSL {
495 507 val closureClassTyper = localTyper.atOwner(closureClass)
496 508 val applyMethodTyper = closureClassTyper.atOwner(applyMethod)
497 509
498   - val constrStatTransformer = new Transformer {
499   - override def transform(tree: Tree): Tree = tree match {
500   - case This(_) if tree.symbol == clazz =>
501   - applyMethodTyper.typed {
502   - atPos(tree.pos) {
503   - Select(This(closureClass), outerField)
504   - }
505   - }
506   - case _ =>
507   - super.transform {
508   - tree match {
509   - case Select(qual, _) =>
510   - val getter = ensureGetter(tree.symbol)
511   - if (getter != NoSymbol)
512   - applyMethodTyper.typed {
513   - atPos(tree.pos) {
514   - Apply(Select(qual, getter), List())
515   - }
516   - }
517   - else tree
518   - case Assign(lhs @ Select(qual, _), rhs) =>
519   - val setter = ensureSetter(lhs.symbol)
520   - if (setter != NoSymbol)
521   - applyMethodTyper.typed {
522   - atPos(tree.pos) {
523   - Apply(Select(qual, setter), List(rhs))
524   - }
525   - }
526   - else tree
527   - case _ =>
528   - tree.changeOwner(impl.symbol -> applyMethod)
529   - }
530   - }
  510 + def applyMethodStat =
  511 + applyMethodTyper.typed {
  512 + atPos(impl.pos) {
  513 + val receiver = Select(This(closureClass), outerField)
  514 + Apply(Select(receiver, delayedEndPointSym), Nil)
  515 + }
531 516 }
532   - }
533   -
534   - def applyMethodStats = constrStatTransformer.transformTrees(stats)
535 517
536 518 val applyMethodDef = DefDef(
537 519 sym = applyMethod,
538 520 vparamss = ListOfNil,
539   - rhs = Block(applyMethodStats, gen.mkAttributedRef(BoxedUnit_UNIT)))
  521 + rhs = Block(applyMethodStat, gen.mkAttributedRef(BoxedUnit_UNIT)))
540 522
541 523 ClassDef(
542 524 sym = closureClass,
543 525 constrMods = Modifiers(0),
544 526 vparamss = List(List(outerFieldDef)),
545   - body = List(applyMethodDef),
  527 + body = applyMethodDef :: Nil,
546 528 superPos = impl.pos)
547 529 }
548 530 }
549 531
  532 + dicl.asInstanceOf[ClassDef]
  533 + }
  534 +
  535 + /* @see overview at `delayedEndpointDef()` of the translation scheme for DelayedInit */
550 536 def delayedInitCall(closure: Tree) = localTyper.typedPos(impl.pos) {
551 537 gen.mkMethodCall(This(clazz), delayedInitMethod, Nil, List(New(closure.symbol.tpe, This(clazz))))
552 538 }
@@ -573,9 +559,15 @@ abstract class Constructors extends Transform with ast.TreeDSL {
573 559 isDelayedInitSubclass /*&& !(defBuf exists isInitDef)*/ && remainingConstrStats.nonEmpty
574 560
575 561 if (needsDelayedInit) {
576   - val dicl = new ConstructorTransformer(unit) transform delayedInitClosure(remainingConstrStats)
  562 + val dlydEpDef: DefDef = delayedEndpointDef(remainingConstrStats)
  563 + defBuf += dlydEpDef
  564 + val dicl = {
  565 + // transform to make the closure-class' default constructor assign the the outer instance to its param-accessor field.
  566 + val diclx = new ConstructorTransformer(unit)
  567 + diclx transform delayedInitClosure(dlydEpDef.symbol.asInstanceOf[MethodSymbol])
  568 + }
577 569 defBuf += dicl
578   - remainingConstrStats = List(delayedInitCall(dicl))
  570 + remainingConstrStats = delayedInitCall(dicl) :: Nil
579 571 }
580 572
581 573 // Assemble final constructor
@@ -597,12 +589,14 @@ abstract class Constructors extends Transform with ast.TreeDSL {
597 589 deriveTemplate(impl)(_ => defBuf.toList filter (stat => mustbeKept(stat.symbol)))
598 590 } // transformClassTemplate
599 591
600   - override def transform(tree: Tree): Tree =
  592 + override def transform(tree: Tree): Tree = {
601 593 tree match {
602 594 case ClassDef(_,_,_,_) if !tree.symbol.isInterface && !isPrimitiveValueClass(tree.symbol) =>
603 595 deriveClassDef(tree)(transformClassTemplate)
604 596 case _ =>
605 597 super.transform(tree)
606 598 }
  599 + }
  600 +
607 601 } // ConstructorTransformer
608 602 }

0 comments on commit b4fbb7b

Jason Zaugg

This mangling would be more consistently handled with:

val methodName = nme.expandedName(currentUnit.freshTermName("delayedEndpoint$"), clazz)
Jason Zaugg

typedPos can clean this up a touch (likewise the enclosing typed { atPos pair.)

Please sign in to comment.
Something went wrong with that request. Please try again.