@@ -55,16 +55,6 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
55
55
def newFreeTypeSymbol (name : TypeName , flags : Long = 0L , origin : String ): FreeTypeSymbol =
56
56
new FreeTypeSymbol (name, origin) initFlags flags
57
57
58
- /** Determines whether the given information request should trigger the given symbol's completer.
59
- * See comments to `Symbol.needsInitialize` for details.
60
- */
61
- protected def shouldTriggerCompleter (symbol : Symbol , completer : Type , isFlagRelated : Boolean , mask : Long ) =
62
- completer match {
63
- case null => false
64
- case _ : FlagAgnosticCompleter => ! isFlagRelated
65
- case _ => abort(s " unsupported completer: $completer of class ${if (completer != null ) completer.getClass else null } for symbol ${symbol.fullName}" )
66
- }
67
-
68
58
/** The original owner of a class. Used by the backend to generate
69
59
* EnclosingMethod attributes.
70
60
*/
@@ -106,12 +96,14 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
106
96
}
107
97
108
98
def knownDirectSubclasses = {
109
- if (! isCompilerUniverse && needsInitialize(isFlagRelated = false , mask = 0 )) initialize
99
+ // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method.
100
+ if (! isCompilerUniverse && ! isThreadsafe(purpose = AllOps )) initialize
110
101
children
111
102
}
112
103
113
104
def selfType = {
114
- if (! isCompilerUniverse && needsInitialize(isFlagRelated = false , mask = 0 )) initialize
105
+ // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method.
106
+ if (! isCompilerUniverse && ! isThreadsafe(purpose = AllOps )) initialize
115
107
typeOfThis
116
108
}
117
109
@@ -147,6 +139,11 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
147
139
with Annotatable [Symbol ]
148
140
with Attachable {
149
141
142
+ // makes sure that all symbols that runtime reflection deals with are synchronized
143
+ private def isSynchronized = this .isInstanceOf [scala.reflect.runtime.SynchronizedSymbols # SynchronizedSymbol ]
144
+ private def isAprioriThreadsafe = isThreadsafe(AllOps )
145
+ assert(isCompilerUniverse || isSynchronized || isAprioriThreadsafe, s " unsafe symbol $initName (child of $initOwner) in runtime reflection universe " )
146
+
150
147
type AccessBoundaryType = Symbol
151
148
type AnnotationType = AnnotationInfo
152
149
@@ -616,20 +613,55 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
616
613
&& isTopLevel
617
614
&& nme.isReplWrapperName(name)
618
615
)
616
+
617
+ /** In our current architecture, symbols for top-level classes and modules
618
+ * are created as dummies. Package symbols just call newClass(name) or newModule(name) and
619
+ * consider their job done.
620
+ *
621
+ * In order for such a dummy to provide meaningful info (e.g. a list of its members),
622
+ * it needs to go through unpickling. Unpickling is a process of reading Scala metadata
623
+ * from ScalaSignature annotations and assigning it to symbols and types.
624
+ *
625
+ * A single unpickling session takes a top-level class or module, parses the ScalaSignature annotation
626
+ * and then reads metadata for the unpicklee, its companion (if any) and all their members recursively
627
+ * (i.e. the pickle not only contains info about directly nested classes/modules, but also about
628
+ * classes/modules nested into those and so on).
629
+ *
630
+ * Unpickling is triggered automatically whenever typeSignature (info in compiler parlance) is called.
631
+ * This happens because package symbols assign completer thunks to the dummies they create.
632
+ * Therefore metadata loading happens lazily and transparently.
633
+ *
634
+ * Almost transparently. Unfortunately metadata isn't limited to just signatures (i.e. lists of members).
635
+ * It also includes flags (which determine e.g. whether a class is sealed or not), annotations and privateWithin.
636
+ * This gives rise to unpleasant effects like in SI-6277, when a flag test called on an uninitialize symbol
637
+ * produces incorrect results.
638
+ *
639
+ * One might think that the solution is simple: automatically call the completer
640
+ * whenever one needs flags, annotations and privateWithin - just like it's done for typeSignature.
641
+ * Unfortunately, this leads to weird crashes in scalac, and currently we can't attempt
642
+ * to fix the core of the compiler risk stability a few weeks before the final release.
643
+ * upd. Haha, "a few weeks before the final release". This surely sounds familiar :)
644
+ *
645
+ * However we do need to fix this for runtime reflection, since this idionsynchrazy is not something
646
+ * we'd like to expose to reflection users. Therefore a proposed solution is to check whether we're in a
647
+ * runtime reflection universe, and if yes and if we've not yet loaded the requested info, then to commence initialization.
648
+ */
619
649
final def getFlag (mask : Long ): Long = {
620
- if (! isCompilerUniverse && needsInitialize(isFlagRelated = true , mask = mask )) initialize
650
+ if (! isCompilerUniverse && ! isThreadsafe(purpose = FlagOps ( mask) )) initialize
621
651
flags & mask
622
652
}
623
653
/** Does symbol have ANY flag in `mask` set? */
624
654
final def hasFlag (mask : Long ): Boolean = {
625
- if (! isCompilerUniverse && needsInitialize(isFlagRelated = true , mask = mask)) initialize
655
+ // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method.
656
+ if (! isCompilerUniverse && ! isThreadsafe(purpose = FlagOps (mask))) initialize
626
657
(flags & mask) != 0
627
658
}
628
659
def hasFlag (mask : Int ): Boolean = hasFlag(mask.toLong)
629
660
630
661
/** Does symbol have ALL the flags in `mask` set? */
631
662
final def hasAllFlags (mask : Long ): Boolean = {
632
- if (! isCompilerUniverse && needsInitialize(isFlagRelated = true , mask = mask)) initialize
663
+ // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method.
664
+ if (! isCompilerUniverse && ! isThreadsafe(purpose = FlagOps (mask))) initialize
633
665
(flags & mask) == mask
634
666
}
635
667
@@ -950,12 +982,21 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
950
982
final def isInitialized : Boolean =
951
983
validTo != NoPeriod
952
984
953
- /** Some completers call sym.setInfo when still in-flight and then proceed with initialization (e.g. see LazyPackageType)
954
- * setInfo sets _validTo to current period, which means that after a call to setInfo isInitialized will start returning true.
955
- * Unfortunately, this doesn't mean that info becomes ready to be used, because subsequent initialization might change the info.
956
- * Therefore we need this method to distinguish between initialized and really initialized symbol states.
985
+ /** We consider a symbol to be thread-safe, when multiple concurrent threads can call its methods
986
+ * (either directly or indirectly via public reflection or internal compiler infrastructure),
987
+ * without any locking and everything works as it should work.
988
+ *
989
+ * In its basic form, `isThreadsafe` always returns false. Runtime reflection augments reflection infrastructure
990
+ * with threadsafety-tracking mechanism implemented in `SynchronizedSymbol` that communicates with underlying completers
991
+ * and can sometimes return true if the symbol has been completed to the point of thread safety.
992
+ *
993
+ * The `purpose` parameter signifies whether we want to just check immutability of certain flags for the given mask.
994
+ * This is necessary to enable robust auto-initialization of `Symbol.flags` for runtime reflection, and is also quite handy
995
+ * in avoiding unnecessary initializations when requesting for flags that have already been set.
957
996
*/
958
- final def isFullyInitialized : Boolean = _validTo != NoPeriod && (flags & LOCKED ) == 0
997
+ def isThreadsafe (purpose : SymbolOps ): Boolean = false
998
+ def markFlagsCompleted (mask : Long ): this .type = this
999
+ def markAllCompleted (): this .type = this
959
1000
960
1001
/** Can this symbol be loaded by a reflective mirror?
961
1002
*
@@ -1232,7 +1273,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
1232
1273
*/
1233
1274
private [this ] var _privateWithin : Symbol = _
1234
1275
def privateWithin = {
1235
- if (! isCompilerUniverse && needsInitialize(isFlagRelated = false , mask = 0 )) initialize
1276
+ // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method.
1277
+ if (! isCompilerUniverse && ! isThreadsafe(purpose = AllOps )) initialize
1236
1278
_privateWithin
1237
1279
}
1238
1280
def privateWithin_= (sym : Symbol ) { _privateWithin = sym }
@@ -1490,46 +1532,6 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
1490
1532
catch { case _ : CyclicReference => debuglog(" Hit cycle in maybeInitialize of $this" ) ; false }
1491
1533
}
1492
1534
1493
- /** Called when the programmer requests information that might require initialization of the underlying symbol.
1494
- *
1495
- * `isFlagRelated` and `mask` describe the nature of this information.
1496
- * isFlagRelated = true means that the programmer needs particular bits in flags.
1497
- * isFlagRelated = false means that the request is unrelated to flags (annotations or privateWithin).
1498
- *
1499
- * In our current architecture, symbols for top-level classes and modules
1500
- * are created as dummies. Package symbols just call newClass(name) or newModule(name) and
1501
- * consider their job done.
1502
- *
1503
- * In order for such a dummy to provide meaningful info (e.g. a list of its members),
1504
- * it needs to go through unpickling. Unpickling is a process of reading Scala metadata
1505
- * from ScalaSignature annotations and assigning it to symbols and types.
1506
- *
1507
- * A single unpickling session takes a top-level class or module, parses the ScalaSignature annotation
1508
- * and then reads metadata for the unpicklee, its companion (if any) and all their members recursively
1509
- * (i.e. the pickle not only contains info about directly nested classes/modules, but also about
1510
- * classes/modules nested into those and so on).
1511
- *
1512
- * Unpickling is triggered automatically whenever typeSignature (info in compiler parlance) is called.
1513
- * This happens because package symbols assign completer thunks to the dummies they create.
1514
- * Therefore metadata loading happens lazily and transparently.
1515
- *
1516
- * Almost transparently. Unfortunately metadata isn't limited to just signatures (i.e. lists of members).
1517
- * It also includes flags (which determine e.g. whether a class is sealed or not), annotations and privateWithin.
1518
- * This gives rise to unpleasant effects like in SI-6277, when a flag test called on an uninitialize symbol
1519
- * produces incorrect results.
1520
- *
1521
- * One might think that the solution is simple: automatically call the completer whenever one needs
1522
- * flags, annotations and privateWithin - just like it's done for typeSignature. Unfortunately, this
1523
- * leads to weird crashes in scalac, and currently we can't attempt to fix the core of the compiler
1524
- * risk stability a few weeks before the final release.
1525
- *
1526
- * However we do need to fix this for runtime reflection, since it's not something we'd like to
1527
- * expose to reflection users. Therefore a proposed solution is to check whether we're in a
1528
- * runtime reflection universe and if yes then to commence initialization.
1529
- */
1530
- protected def needsInitialize (isFlagRelated : Boolean , mask : Long ) =
1531
- ! isInitialized && (flags & LOCKED ) == 0 && shouldTriggerCompleter(this , if (infos ne null ) infos.info else null , isFlagRelated, mask)
1532
-
1533
1535
/** Was symbol's type updated during given phase? */
1534
1536
final def hasTypeAt (pid : Phase # Id ): Boolean = {
1535
1537
assert(isCompilerUniverse)
@@ -1688,7 +1690,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
1688
1690
* the annotations attached to member a definition (class, method, type, field).
1689
1691
*/
1690
1692
def annotations : List [AnnotationInfo ] = {
1691
- if (! isCompilerUniverse && needsInitialize(isFlagRelated = false , mask = 0 )) initialize
1693
+ // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method.
1694
+ if (! isCompilerUniverse && ! isThreadsafe(purpose = AllOps )) initialize
1692
1695
_annotations
1693
1696
}
1694
1697
@@ -3512,6 +3515,17 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
3512
3515
3513
3516
Statistics .newView(" #symbols" )(ids)
3514
3517
3518
+
3519
+ // -------------- Completion --------------------------------------------------------
3520
+
3521
+ // is used to differentiate levels of thread-safety in `Symbol.isThreadsafe`
3522
+ case class SymbolOps (isFlagRelated : Boolean , mask : Long )
3523
+ val AllOps = SymbolOps (isFlagRelated = false , mask = 0L )
3524
+ def FlagOps (mask : Long ) = SymbolOps (isFlagRelated = true , mask = mask)
3525
+
3526
+ private def relevantSymbols (syms : Seq [Symbol ]) = syms.flatMap(sym => List (sym, sym.moduleClass, sym.sourceModule))
3527
+ def markFlagsCompleted (syms : Symbol * )(mask : Long ): Unit = relevantSymbols(syms).foreach(_.markFlagsCompleted(mask))
3528
+ def markAllCompleted (syms : Symbol * ): Unit = relevantSymbols(syms).foreach(_.markAllCompleted)
3515
3529
}
3516
3530
3517
3531
object SymbolsStats {
0 commit comments