scala.reflect.runtime.JavaUniverse.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-reflect Show documentation
Show all versions of scala-reflect Show documentation
Reflection Library for the Scala Programming Language
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala
package reflect
package runtime
import scala.annotation.nowarn
import scala.reflect.internal.{SomePhase, TreeInfo}
import scala.reflect.internal.{SymbolTable => InternalSymbolTable}
import scala.reflect.runtime.{SymbolTable => RuntimeSymbolTable}
import scala.reflect.api.{TypeCreator, Universe}
import scala.reflect.internal.util.Statistics
/** An implementation of [[scala.reflect.api.Universe]] for runtime reflection using JVM classloaders.
*
* Should not be instantiated directly, use [[scala.reflect.runtime.universe]] instead.
*
* @contentDiagram hideNodes "*Api" "*Extractor"
*/
class JavaUniverse extends InternalSymbolTable with JavaUniverseForce with ReflectSetup with RuntimeSymbolTable { self =>
def picklerPhase = SomePhase
def erasurePhase = SomePhase
lazy val settings = new Settings
override final val statistics = new Statistics(JavaUniverse.this, settings) with ReflectStats
private[this] val isLogging = System.getProperty("scala.debug.reflect") != null
def log(msg: => AnyRef): Unit = if (isLogging) Console.err.println("[reflect] " + msg)
// TODO: why put output under isLogging? Calls to inform are already conditional on debug/verbose/...
import scala.reflect.internal.Reporter
override def reporter: Reporter = new Reporter {
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = log(msg)
}
// minimal Run to get Reporting wired
def currentRun = new RunReporting {}
class PerRunReporting extends PerRunReportingBase {
def deprecationWarning(pos: Position, msg: String, since: String, site: String, origin: String): Unit = reporter.warning(pos, msg)
}
protected def PerRunReporting = new PerRunReporting
type TreeCopier = InternalTreeCopierOps
implicit val TreeCopierTag: ClassTag[TreeCopier] = ClassTag[TreeCopier](classOf[TreeCopier])
def newStrictTreeCopier: TreeCopier = new StrictTreeCopier
def newLazyTreeCopier: TreeCopier = new LazyTreeCopier
def currentFreshNameCreator = globalFreshNameCreator
override lazy val internal: Internal = new SymbolTableInternal {
override def typeTagToManifest[T: ClassTag](mirror0: Any, tag: Universe # TypeTag[T]): Manifest[T] = {
// scala/bug#6239: make this conversion more precise
val mirror = mirror0.asInstanceOf[Mirror]
val runtimeClass = mirror.runtimeClass(tag.in(mirror).tpe)
Manifest.classType(runtimeClass).asInstanceOf[Manifest[T]]
}
override def manifestToTypeTag[T](mirror0: Any, manifest: Manifest[T]): Universe # TypeTag[T] =
TypeTag(mirror0.asInstanceOf[Mirror], new TypeCreator {
def apply[U <: Universe with Singleton](mirror: scala.reflect.api.Mirror[U]): U # Type = {
mirror.universe match {
case ju: JavaUniverse =>
val jm = mirror.asInstanceOf[ju.Mirror]
val sym = jm.classSymbol(manifest.runtimeClass)
val tpe =
if (manifest.typeArguments.isEmpty) sym.toType
else {
val tags = manifest.typeArguments map (targ => ju.internal.manifestToTypeTag(jm, targ))
ju.appliedType(sym.toTypeConstructor, tags map (_.in(jm).tpe))
}
tpe.asInstanceOf[U # Type]
case u =>
u.internal.manifestToTypeTag(mirror.asInstanceOf[u.Mirror], manifest).in(mirror).tpe
}
}
})
}
// can't put this in runtime.Trees since that's mixed with Global in ReflectGlobal, which has the definition from internal.Trees
@nowarn("cat=deprecation&msg=early initializers")
object treeInfo extends {
val global: JavaUniverse.this.type = JavaUniverse.this
} with TreeInfo
init()
// ======= Initialization of runtime reflection =======
//
// This doc describes the carefully laid out sequence of actions used to initialize reflective universes.
//
// Before reading the text below, read up the section Mirrors in the reflection pre-SIP
// https://docs.google.com/document/d/1nAwSw4TmMplsIlzh2shYLUJ5mVh3wndDa1Zm1H6an9A/edit.
// Take an especially good look at Figure 2, because it illustrates fundamental principles underlying runtime reflection:
// 1) For each universe we have one mirror per classloader
// 2) Package symbols are per-mirror
// 3) Other symbols are per-universe, which means that a symbol (e.g. Seq on the picture) might be shared between multiple owners
//
// Main challenges that runtime reflection presents wrt initialization are:
// 1) Extravagant completion scheme that enters package members on-demand rather than a result of scanning a directory with class files.
// (That's a direct consequence of the fact that in general case we can't enumerate all classes in a classloader.
// As Paul rightfully mentioned, we could special case classloaders that point to filesystems, but that is left for future work).
// 2) Presence of synthetic symbols that aren't loaded by normal means (from classfiles) but are synthesized on-the-fly,
// and the necessity to propagate these synthetic symbols from rootMirror to other mirrors,
// complicated by the fact that such symbols depend on normal symbols (e.g. AnyRef depends on Object).
// 3) Necessity to remain thread-safe, which limits our options related to lazy initialization
// (E.g. we cannot use missingHook to enter synthetic symbols, because that's thread-unsafe).
//
// Directly addressing the challenge #3, we create all synthetic symbols fully in advance during init().
// However, it's not that simple as just calling definitions.symbolsNotPresentInBytecode.
// Before doing that, we need to first initialize ObjectClass, then ScalaPackageClass, and only then deal with synthetics.
// Below you can find a detailed explanation for that.
//
// ### Why ScalaPackageClass? ###
//
// Forcing ScalaPackageClass first thing during startup is important, because syntheticCoreClasses such as AnyRefClass
// need to be entered into ScalaPackageClass, which entails calling ScalaPackageClass.info.decls.enter.
// If ScalaPackageClass isn't initialized by that moment, the following will happen for runtime reflection:
// 1) Initialization of ScalaPackageClass will trigger unpickling.
// 2) Unpickling will need to load some auxiliary types such as, for example, String.
// 3) To load String, runtime reflection will call mirrorDefining(classOf[String]).
// 4) This, in turn, will call runtimeMirror(classOf[String].getClassLoader).
// 5) For some classloader configurations, the resulting mirror will be different from rootMirror.
// 6) In that case, initialization of the resulting mirror will try to import definitions.syntheticCoreClasses into the mirror.
// 7) This will force all the lazy vals corresponding to syntheticCoreClasses.
// 8) By that time, the completer of ScalaPackageClass will have already called setInfo on ScalaPackageClass, so there won't be any stack overflow.
//
// So far so good, no crashes, no problems, right? Not quite.
// If forcing of ScalaPackageClass was called by a syntheticCoreClasses lazy val,
// then this lazy val will be entered twice: once during step 7 and once when returning from the original call.
// To avoid this we need to initialize ScalaPackageClass prior to other synthetics.
//
// ### Why ObjectClass? ###
//
// 1) As explained in JavaMirrors.missingHook, initialization of ScalaPackageClass critically depends on AnyRefClass.
// 2) AnyRefClass is defined as "lazy val AnyRefClass = newAlias(ScalaPackageClass, tpnme.AnyRef, ObjectTpe)",
// which means that initialization of AnyRefClass depends on ObjectClass.
// 3) ObjectClass is defined as "lazy val ObjectClass = getRequiredClass(sn.Object.toString)",
// which means that under some classloader configurations (see JavaMirrors.missingHook for more details)
// dereferencing ObjectClass might trigger an avalanche of initializations calling back into AnyRefClass
// while another AnyRefClass initializer is still on stack.
// 4) That will lead to AnyRefClass being entered two times (once when the recursive call returns and once when the original one returns)
// 5) That will crash PackageScope.enter that helpfully detects double-enters.
//
// Therefore, before initializing ScalaPackageClass, we must pre-initialize ObjectClass
def init(): Unit = {
definitions.init()
// workaround for https://groups.google.com/group/scala-internals/browse_thread/thread/97840ba4fd37b52e
// constructors are by definition single-threaded, so we initialize all lazy vals (and local object) in advance
// in order to avoid deadlocks later (e.g. one thread holds a global reflection lock and waits for definitions.Something to initialize,
// whereas another thread holds a definitions.Something initialization lock and needs a global reflection lock to complete the initialization)
// TODO Convert this into a macro
force()
}
}