@@ -9,9 +9,8 @@ package transform
9
9
10
10
import symtab ._
11
11
import Flags ._
12
- import scala .collection .mutable .ListBuffer
13
12
import scala .tools .nsc .util .{Position ,NoPosition }
14
- import scala . collection .mutable .HashMap
13
+ import collection .mutable .{ ListBuffer , HashMap }
15
14
16
15
abstract class Mixin extends InfoTransform with ast.TreeDSL {
17
16
import global ._
@@ -364,6 +363,50 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
364
363
tp
365
364
}
366
365
366
+ import scala .collection ._
367
+
368
+ /** Return a map of single-use fields to the lazy value that uses them during initialization.
369
+ * Each field has to be private and defined in the enclosing class, and there must
370
+ * be exactly one lazy value using it.
371
+ *
372
+ * Such fields will be nulled after the initializer has memoized the lazy value.
373
+ */
374
+ def singleUseFields (templ : Template ): collection.Map [Symbol , List [Symbol ]] = {
375
+ val usedIn = new mutable.HashMap [Symbol , List [Symbol ]] {
376
+ override def default (key : Symbol ) = Nil
377
+ }
378
+
379
+ object SingleUseTraverser extends Traverser {
380
+ override def traverse (tree : Tree ) {
381
+ tree match {
382
+ case Assign (lhs, rhs) => traverse(rhs) // assignments don't count
383
+ case _ =>
384
+ if (tree.hasSymbol && tree.symbol != NoSymbol ) {
385
+ val sym = tree.symbol
386
+ if ((sym.hasFlag(ACCESSOR ) || (sym.isTerm && ! sym.isMethod))
387
+ && sym.hasFlag(PRIVATE )
388
+ && ! (currentOwner.isGetter && currentOwner.accessed == sym) // getter
389
+ && ! definitions.isValueClass(sym.tpe.resultType.typeSymbol)
390
+ && sym.owner == templ.symbol.owner
391
+ && ! tree.isDef) {
392
+ log(" added use in: " + currentOwner + " -- " + tree)
393
+ usedIn(sym) ::= currentOwner
394
+
395
+ }
396
+ }
397
+ super .traverse(tree)
398
+ }
399
+ }
400
+ }
401
+ SingleUseTraverser (templ)
402
+ log(" usedIn: " + usedIn)
403
+ usedIn filter { pair =>
404
+ val member = pair._2.head
405
+ (member.isValue
406
+ && member.hasFlag(LAZY )
407
+ && pair._2.tail.isEmpty) }
408
+ }
409
+
367
410
// --------- term transformation -----------------------------------------------
368
411
369
412
protected def newTransformer (unit : CompilationUnit ): Transformer =
@@ -384,6 +427,9 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
384
427
private var localTyper : erasure.Typer = _
385
428
private def typedPos (pos : Position )(tree : Tree ) = localTyper typed { atPos(pos)(tree) }
386
429
430
+ /** Map lazy values to the fields they should null after initialization. */
431
+ private var lazyValNullables : mutable.MultiMap [Symbol , Symbol ] = _
432
+
387
433
import scala .collection ._
388
434
389
435
/** Map a field symbol to a unique integer denoting its position in the class layout.
@@ -612,17 +658,27 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
612
658
* where bitmap$n is an int value acting as a bitmap of initialized values. It is
613
659
* the 'n' is (offset / 32), the MASK is (1 << (offset % 32)).
614
660
*/
615
- def mkLazyDef (clazz : Symbol , init : List [Tree ], retVal : Tree , offset : Int ): Tree = {
661
+ def mkLazyDef (clazz : Symbol , lzyVal : Symbol , init : List [Tree ], retVal : Tree , offset : Int ): Tree = {
662
+ def nullify (sym : Symbol ): Tree = {
663
+ val sym1 = if (sym.hasFlag(ACCESSOR )) sym.accessed else sym
664
+ Select (This (clazz), sym1) === LIT (null )
665
+ }
666
+
667
+
616
668
val bitmapSym = bitmapFor(clazz, offset)
617
669
val mask = LIT (1 << (offset % FLAGS_PER_WORD ))
618
670
def cond = mkTest(clazz, mask, bitmapSym, true )
671
+ val nulls = (lazyValNullables(lzyVal).toList.sort(_.id < _.id) map nullify)
619
672
def syncBody = init ::: List (mkSetFlag(clazz, offset), UNIT )
620
673
674
+ log(" nulling fields inside " + lzyVal + " : " + nulls)
621
675
val result =
622
- IF (cond) THEN gen.mkSynchronized(
623
- gen mkAttributedThis clazz,
624
- IF (cond) THEN BLOCK (syncBody : _* ) ENDIF
625
- ) ENDIF
676
+ IF (cond) THEN BLOCK (
677
+ (gen.mkSynchronized(
678
+ gen mkAttributedThis clazz,
679
+ IF (cond) THEN BLOCK (syncBody : _* ) ENDIF
680
+ )
681
+ :: nulls): _* ) ENDIF
626
682
627
683
typedPos(init.head.pos)(BLOCK (result, retVal))
628
684
}
@@ -653,10 +709,10 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
653
709
if sym.hasFlag(LAZY ) && rhs != EmptyTree && ! clazz.isImplClass =>
654
710
assert(fieldOffset.isDefinedAt(sym))
655
711
val rhs1 = if (sym.tpe.resultType.typeSymbol == UnitClass )
656
- mkLazyDef(clazz, List (rhs), UNIT , fieldOffset(sym))
712
+ mkLazyDef(clazz, sym, List (rhs), UNIT , fieldOffset(sym))
657
713
else {
658
714
val Block (stats, res) = rhs
659
- mkLazyDef(clazz, stats, Select (This (clazz), res.symbol), fieldOffset(sym))
715
+ mkLazyDef(clazz, sym, stats, Select (This (clazz), res.symbol), fieldOffset(sym))
660
716
}
661
717
treeCopy.DefDef (stat, mods, name, tp, vp, tpt, rhs1)
662
718
@@ -790,13 +846,13 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
790
846
if (sym.hasFlag(LAZY ) && sym.isGetter) {
791
847
val rhs1 =
792
848
if (sym.tpe.resultType.typeSymbol == UnitClass )
793
- mkLazyDef(clazz, List (Apply (staticRef(initializer(sym)), List (gen.mkAttributedThis(clazz)))), UNIT , fieldOffset(sym))
849
+ mkLazyDef(clazz, sym, List (Apply (staticRef(initializer(sym)), List (gen.mkAttributedThis(clazz)))), UNIT , fieldOffset(sym))
794
850
else {
795
851
val assign = atPos(sym.pos) {
796
852
Assign (Select (This (sym.accessed.owner), sym.accessed) /* gen.mkAttributedRef(sym.accessed)*/ ,
797
853
Apply (staticRef(initializer(sym)), gen.mkAttributedThis(clazz) :: Nil ))
798
854
}
799
- mkLazyDef(clazz, List (assign), Select (This (clazz), sym.accessed), fieldOffset(sym))
855
+ mkLazyDef(clazz, sym, List (assign), Select (This (clazz), sym.accessed), fieldOffset(sym))
800
856
}
801
857
rhs1
802
858
} else if (sym.getter(sym.owner).tpe.resultType.typeSymbol == UnitClass ) {
@@ -855,6 +911,22 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
855
911
stats1
856
912
}
857
913
914
+ private def nullableFields (templ : Template ) = {
915
+ val nullables = new mutable.HashMap [Symbol , mutable.Set [Symbol ]] with mutable.MultiMap [Symbol , Symbol ] {
916
+ override def default (key : Symbol ) = mutable.Set .empty
917
+ }
918
+
919
+ // if there are no lazy fields, take the fast path and save a traversal of the whole AST
920
+ if (templ.symbol.owner.info.decls.exists(_.hasFlag(LAZY ))) {
921
+ // check what fields can be nulled for
922
+ val uses = singleUseFields(templ)
923
+ for ((field, users) <- uses; lazyFld <- users) {
924
+ nullables.addBinding(lazyFld, field)
925
+ }
926
+ }
927
+ nullables
928
+ }
929
+
858
930
/** The transform that gets applied to a tree after it has been completely
859
931
* traversed and possible modified by a preTransform.
860
932
* This step will
@@ -884,6 +956,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
884
956
// change parents of templates to conform to parents in the symbol info
885
957
val parents1 = currentOwner.info.parents map (t => TypeTree (t) setPos tree.pos)
886
958
959
+ lazyValNullables = nullableFields(tree.asInstanceOf [Template ])
887
960
// add all new definitions to current class or interface
888
961
val body1 = addNewDefs(currentOwner, body)
889
962
0 commit comments