diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2013-01-29 20:52:10 +0100 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2013-02-11 19:17:46 +0100 |
commit | b2c2493b22e2b6b8feb8124a2503fbb794c0bde7 (patch) | |
tree | a2230acb42ab8cc2f2a1991b554eb502e934d338 /src/reflect | |
parent | a9dca512d8a99fd7b9866cfad6b8ae9b5f451f4b (diff) | |
download | scala-b2c2493b22e2b6b8feb8124a2503fbb794c0bde7.tar.gz scala-b2c2493b22e2b6b8feb8124a2503fbb794c0bde7.tar.bz2 scala-b2c2493b22e2b6b8feb8124a2503fbb794c0bde7.zip |
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.
Diffstat (limited to 'src/reflect')
4 files changed, 31 insertions, 28 deletions
diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index f75855f1ec..e096bac990 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -209,6 +209,8 @@ abstract class SymbolTable extends macros.Universe finally popPhase(saved) } + def slowButSafeAtPhase[T](ph: Phase)(op: => T): T = + if (isCompilerUniverse) atPhase(ph)(op) else op /** Since when it is to be "at" a phase is inherently ambiguous, * a couple unambiguously named methods. @@ -221,6 +223,9 @@ abstract class SymbolTable extends macros.Universe @inline final def atPhaseNotLaterThan[T](target: Phase)(op: => T): T = if (isAtPhaseAfter(target)) atPhase(target)(op) else op + def slowButSafeAtPhaseNotLaterThan[T](target: Phase)(op: => T): T = + if (isCompilerUniverse) atPhaseNotLaterThan(target)(op) else op + final def isValid(period: Period): Boolean = period != 0 && runId(period) == currentRunId && { val pid = phaseId(period) diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index 603fff4f1c..4d61e4178a 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -853,7 +853,7 @@ abstract class UnPickler { private val p = phase override def complete(sym: Symbol) : Unit = try { val tp = at(i, () => readType(sym.isTerm)) // after NMT_TRANSITION, revert `() => readType(sym.isTerm)` to `readType` - atPhase(p) (sym setInfo tp) + slowButSafeAtPhase(p)(sym setInfo tp) if (currentRunId != definedAtRunId) sym.setInfo(adaptToNewRunMap(tp)) } @@ -871,7 +871,7 @@ abstract class UnPickler { super.complete(sym) var alias = at(j, readSymbol) if (alias.isOverloaded) - alias = atPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt)))) + alias = slowButSafeAtPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt)))) sym.asInstanceOf[TermSymbol].setAlias(alias) } diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index 311db64b91..23c0e1889f 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -15,37 +15,13 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => * is found, a package is created instead. */ class TopClassCompleter(clazz: Symbol, module: Symbol) extends SymLoader with FlagAssigningCompleter { -// def makePackage() { -// println("wrong guess; making package "+clazz) -// val ptpe = newPackageType(module.moduleClass) -// for (sym <- List(clazz, module, module.moduleClass)) { -// sym setFlag Flags.PACKAGE -// sym setInfo ptpe -// } -// } - override def complete(sym: Symbol) = { debugInfo("completing "+sym+"/"+clazz.fullName) assert(sym == clazz || sym == module || sym == module.moduleClass) -// try { - atPhaseNotLaterThan(picklerPhase) { + slowButSafeAtPhaseNotLaterThan(picklerPhase) { val loadingMirror = mirrorThatLoaded(sym) val javaClass = loadingMirror.javaClass(clazz.javaClassName) loadingMirror.unpickleClass(clazz, module, javaClass) -// } catch { -// case ex: ClassNotFoundException => makePackage() -// case ex: NoClassDefFoundError => makePackage() - // Note: We catch NoClassDefFoundError because there are situations - // where a package and a class have the same name except for capitalization. - // It seems in this case the class is loaded even if capitalization differs - // but then a NoClassDefFound error is issued with a ("wrong name: ...") - // reason. (I guess this is a concession to Windows). - // The present behavior is a bit too forgiving, in that it masks - // all class load errors, not just wrong name errors. We should try - // to be more discriminating. To get on the right track simply delete - // the clause above and load a collection class such as collection.Iterable. - // You'll see an error that class `parallel` has the wrong name. -// } } } override def load(sym: Symbol) = complete(sym) diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala index ba8ff82b1b..cf4a3bc7e2 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala @@ -44,7 +44,29 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb override def updateInfo(info: Type): Symbol = synchronized { super.updateInfo(info) } override def rawInfo: Type = synchronized { super.rawInfo } - override def typeParams: List[Symbol] = synchronized { super.typeParams } + override def typeParams: List[Symbol] = synchronized { + if (isCompilerUniverse) super.typeParams + else { + if (isMonomorphicType) Nil + else { + // analogously to the "info" getter, here we allow for two completions: + // one: sourceCompleter to LazyType, two: LazyType to completed type + if (validTo == NoPeriod) + rawInfo load this + if (validTo == NoPeriod) + rawInfo load this + + rawInfo.typeParams + } + } + } + override def unsafeTypeParams: List[Symbol] = synchronized { + if (isCompilerUniverse) super.unsafeTypeParams + else { + if (isMonomorphicType) Nil + else rawInfo.typeParams + } + } override def reset(completer: Type): this.type = synchronized { super.reset(completer) } |