Skip to content

Commit b2c2493

Browse files
committed
reflection no longer uses atPhase and friends
Mentioned methods mutate the global `atPhaseStack` variable, which can easily lead to imbalances and, ultimately, to the empty stack error. Luckily for us, there's only one dummy phase, SomePhase, which is used by runtime reflection, so there is absolutely zero need to invoke atPhase in non-compiler reflexive universes. The cleanest solution would be to override `atPhase` for runtime reflection, but it's @inline final, so I didn't want to pay performance penalties for something that's used three times in runtime reflection (during unpickling, in reflection-specific completers and in `Symbol.typeParams/unsafeTypeParams`). Therefore I added overrideable analogues of `atPhase` and `atPhaseNotLaterThan` which are called from the aforementioned code shared between the compiler and runtime reflection. I also had to duplicate the code of `Symbol.XXXtypeParams`, again due to them being very performance-sensitive.
1 parent a9dca51 commit b2c2493

File tree

4 files changed

+31
-28
lines changed

4 files changed

+31
-28
lines changed

src/reflect/scala/reflect/internal/SymbolTable.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ abstract class SymbolTable extends macros.Universe
209209
finally popPhase(saved)
210210
}
211211

212+
def slowButSafeAtPhase[T](ph: Phase)(op: => T): T =
213+
if (isCompilerUniverse) atPhase(ph)(op) else op
212214

213215
/** Since when it is to be "at" a phase is inherently ambiguous,
214216
* a couple unambiguously named methods.
@@ -221,6 +223,9 @@ abstract class SymbolTable extends macros.Universe
221223
@inline final def atPhaseNotLaterThan[T](target: Phase)(op: => T): T =
222224
if (isAtPhaseAfter(target)) atPhase(target)(op) else op
223225

226+
def slowButSafeAtPhaseNotLaterThan[T](target: Phase)(op: => T): T =
227+
if (isCompilerUniverse) atPhaseNotLaterThan(target)(op) else op
228+
224229
final def isValid(period: Period): Boolean =
225230
period != 0 && runId(period) == currentRunId && {
226231
val pid = phaseId(period)

src/reflect/scala/reflect/internal/pickling/UnPickler.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,7 @@ abstract class UnPickler {
853853
private val p = phase
854854
override def complete(sym: Symbol) : Unit = try {
855855
val tp = at(i, () => readType(sym.isTerm)) // after NMT_TRANSITION, revert `() => readType(sym.isTerm)` to `readType`
856-
atPhase(p) (sym setInfo tp)
856+
slowButSafeAtPhase(p)(sym setInfo tp)
857857
if (currentRunId != definedAtRunId)
858858
sym.setInfo(adaptToNewRunMap(tp))
859859
}
@@ -871,7 +871,7 @@ abstract class UnPickler {
871871
super.complete(sym)
872872
var alias = at(j, readSymbol)
873873
if (alias.isOverloaded)
874-
alias = atPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt))))
874+
alias = slowButSafeAtPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt))))
875875

876876
sym.asInstanceOf[TermSymbol].setAlias(alias)
877877
}

src/reflect/scala/reflect/runtime/SymbolLoaders.scala

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,13 @@ private[reflect] trait SymbolLoaders { self: SymbolTable =>
1515
* is found, a package is created instead.
1616
*/
1717
class TopClassCompleter(clazz: Symbol, module: Symbol) extends SymLoader with FlagAssigningCompleter {
18-
// def makePackage() {
19-
// println("wrong guess; making package "+clazz)
20-
// val ptpe = newPackageType(module.moduleClass)
21-
// for (sym <- List(clazz, module, module.moduleClass)) {
22-
// sym setFlag Flags.PACKAGE
23-
// sym setInfo ptpe
24-
// }
25-
// }
26-
2718
override def complete(sym: Symbol) = {
2819
debugInfo("completing "+sym+"/"+clazz.fullName)
2920
assert(sym == clazz || sym == module || sym == module.moduleClass)
30-
// try {
31-
atPhaseNotLaterThan(picklerPhase) {
21+
slowButSafeAtPhaseNotLaterThan(picklerPhase) {
3222
val loadingMirror = mirrorThatLoaded(sym)
3323
val javaClass = loadingMirror.javaClass(clazz.javaClassName)
3424
loadingMirror.unpickleClass(clazz, module, javaClass)
35-
// } catch {
36-
// case ex: ClassNotFoundException => makePackage()
37-
// case ex: NoClassDefFoundError => makePackage()
38-
// Note: We catch NoClassDefFoundError because there are situations
39-
// where a package and a class have the same name except for capitalization.
40-
// It seems in this case the class is loaded even if capitalization differs
41-
// but then a NoClassDefFound error is issued with a ("wrong name: ...")
42-
// reason. (I guess this is a concession to Windows).
43-
// The present behavior is a bit too forgiving, in that it masks
44-
// all class load errors, not just wrong name errors. We should try
45-
// to be more discriminating. To get on the right track simply delete
46-
// the clause above and load a collection class such as collection.Iterable.
47-
// You'll see an error that class `parallel` has the wrong name.
48-
// }
4925
}
5026
}
5127
override def load(sym: Symbol) = complete(sym)

src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,29 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb
4444
override def updateInfo(info: Type): Symbol = synchronized { super.updateInfo(info) }
4545
override def rawInfo: Type = synchronized { super.rawInfo }
4646

47-
override def typeParams: List[Symbol] = synchronized { super.typeParams }
47+
override def typeParams: List[Symbol] = synchronized {
48+
if (isCompilerUniverse) super.typeParams
49+
else {
50+
if (isMonomorphicType) Nil
51+
else {
52+
// analogously to the "info" getter, here we allow for two completions:
53+
// one: sourceCompleter to LazyType, two: LazyType to completed type
54+
if (validTo == NoPeriod)
55+
rawInfo load this
56+
if (validTo == NoPeriod)
57+
rawInfo load this
58+
59+
rawInfo.typeParams
60+
}
61+
}
62+
}
63+
override def unsafeTypeParams: List[Symbol] = synchronized {
64+
if (isCompilerUniverse) super.unsafeTypeParams
65+
else {
66+
if (isMonomorphicType) Nil
67+
else rawInfo.typeParams
68+
}
69+
}
4870

4971
override def reset(completer: Type): this.type = synchronized { super.reset(completer) }
5072

0 commit comments

Comments
 (0)