scala.tools.nsc.symtab.SymbolLoaders.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-compiler Show documentation
Show all versions of scala-compiler Show documentation
Compiler for the Scala Programming Language
The newest version!
/*
* 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.tools.nsc
package symtab
import classfile.{ClassfileParser, ReusableDataReader}
import java.io.IOException
import scala.reflect.internal.MissingRequirementError
import scala.reflect.io.{AbstractFile, NoAbstractFile}
import scala.tools.nsc.util.{ClassPath, ClassRepresentation}
import scala.reflect.internal.util.{ReusableInstance, StatisticsStatics}
import scala.tools.nsc.Reporting.WarningCategory
/** This class ...
*
* @author Martin Odersky
*/
abstract class SymbolLoaders {
val symbolTable: symtab.SymbolTable {
def settings: Settings
}
val platform: backend.Platform {
val symbolTable: SymbolLoaders.this.symbolTable.type
}
import symbolTable._
/**
* Required by ClassfileParser. Check documentation in that class for details.
*/
def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol
/**
* Should forward to `Run.compileLate`. The more principled fix would be to
* determine why this functionality is needed and extract it into a separate
* interface.
*/
protected def compileLate(srcfile: AbstractFile): Unit
// forwards to runReporting.warning, but we don't have global in scope here
def warning(pos: Position, msg: String, category: WarningCategory, site: String): Unit
protected def enterIfNew(owner: Symbol, member: Symbol, completer: SymbolLoader): Symbol = {
assert(owner.info.decls.lookup(member.name) == NoSymbol, owner.fullName + "." + member.name)
owner.info.decls enter member
member
}
protected def signalError(root: Symbol, ex: Throwable): Unit = {
if (settings.debug) ex.printStackTrace()
globalError(ex.getMessage() match {
case null => "i/o error while loading " + root.name
case msg => "error while loading " + root.name + ", " + msg
})
}
def newClass(owner: Symbol, name: String): ClassSymbol = owner.newClass(newTypeName(name))
/** Enter class with given `name` into scope of `root`
* and give them `completer` as type.
*/
def enterClass(owner: Symbol, name: String, completer: SymbolLoader): Symbol =
enterClass(owner, newClass(owner, name), completer)
def enterClass(owner: Symbol, clazz: ClassSymbol, completer: SymbolLoader): Symbol = {
clazz setInfo completer
enterIfNew(owner, clazz, completer)
}
def newModule(owner: Symbol, name: String): ModuleSymbol = owner.newModule(newTermName(name))
/** Enter module with given `name` into scope of `root`
* and give them `completer` as type.
*/
def enterModule(owner: Symbol, name: String, completer: SymbolLoader): Symbol =
enterModule(owner, newModule(owner, name), completer)
def enterModule(owner: Symbol, module: ModuleSymbol, completer: SymbolLoader): Symbol = {
module setInfo completer
module.moduleClass setInfo moduleClassLoader
enterIfNew(owner, module, completer)
}
/** Enter package with given `name` into scope of `root`
* and give them `completer` as type.
*/
def enterPackage(root: Symbol, name: String, completer: SymbolLoader): Symbol = {
val pname = newTermName(name)
val preExisting = root.info.decls lookup pname
if (preExisting != NoSymbol) {
// Some jars (often, obfuscated ones) include a package and
// object with the same name. Rather than render them unusable,
// offer a setting to resolve the conflict one way or the other.
// This was motivated by the desire to use YourKit probes, which
// require yjp.jar at runtime. See scala/bug#2089.
if (settings.termConflict.isDefault)
throw new TypeError(
s"$root contains object and package with same name: $name\none of them needs to be removed from classpath"
)
else if (settings.termConflict.value == "package") {
warning(
NoPosition,
"Resolving package/object name conflict in favor of package " +
preExisting.fullName + ". The object will be inaccessible.",
WarningCategory.Other,
site = "")
root.info.decls.unlink(preExisting)
}
else {
warning(
NoPosition,
"Resolving package/object name conflict in favor of object " +
preExisting.fullName + ". The package will be inaccessible.",
WarningCategory.Other,
site = "")
return NoSymbol
}
}
// todo: find out initialization sequence for pkg/pkg.moduleClass is different from enterModule
val pkg = root.newPackage(pname)
pkg.moduleClass setInfo completer
pkg setInfo pkg.moduleClass.tpe
root.info.decls enter pkg
pkg
}
/** Enter class and module with given `name` into scope of `root`
* and give them `completer` as type.
*/
def enterClassAndModule(root: Symbol, name: TermName, getCompleter: (ClassSymbol, ModuleSymbol) => SymbolLoader): Unit = {
val clazz0 = root.newClass(name.toTypeName)
val module0 = root.newModule(name)
val completer = getCompleter(clazz0, module0)
// enterClass/Module may return an existing symbol instead of the ones we created above
// this may happen when there's both sources and binaries on the classpath, but the class
// name is different from the file name, so the classpath can't match the binary and source
// representation. `companionModule/Class` prefers the source version, so we should be careful
// to reuse the symbols returned below.
val clazz = enterClass(root, clazz0, completer)
val module = enterModule(root, module0, completer)
if (!clazz.isAnonymousClass) {
// Diagnostic for scala/bug#7147
def msg: String = {
def symLocation(sym: Symbol) = if (sym == null) "null" else s"${clazz.fullLocationString} (from ${clazz.associatedFile})"
sm"""Inconsistent class/module symbol pair for `$name` loaded from ${symLocation(root)}.
|clazz = ${symLocation(clazz)}; clazz.companionModule = ${clazz.companionModule}
|module = ${symLocation(module)}; module.companionClass = ${module.companionClass}"""
}
assert(clazz.companionModule == module, msg)
assert(module.companionClass == clazz, msg)
}
}
/** In batch mode: Enter class and module with given `name` into scope of `root`
* and give them a source completer for given `src` as type.
* In IDE mode: Find all toplevel definitions in `src` and enter then into scope of `root`
* with source completer for given `src` as type.
* (overridden in interactive.Global).
*/
def enterToplevelsFromSource(root: Symbol, name: TermName, src: AbstractFile): Unit = {
enterClassAndModule(root, name, (_, _) => new SourcefileLoader(src))
}
/** The package objects of scala and scala.reflect should always
* be loaded in binary if classfiles are available, even if sourcefiles
* are newer. Late-compiling these objects from source leads to compilation
* order issues.
* Note: We do a name-base comparison here because the method is called before we even
* have ReflectPackage defined.
*/
def binaryOnly(owner: Symbol, name: TermName): Boolean =
name == nme.PACKAGE &&
(owner.fullName == "scala" || owner.fullName == "scala.reflect")
/** Initialize toplevel class and module symbols in `owner` from class path representation `classRep`
*/
def initializeFromClassPath(owner: Symbol, classRep: ClassRepresentation): Unit = {
((classRep.binary, classRep.source) : @unchecked) match {
case (Some(bin), Some(src))
if platform.needCompile(bin, src) && !binaryOnly(owner, nameOf(classRep)) =>
if (settings.verbose) inform("[symloader] picked up newer source file for " + src.path)
enterToplevelsFromSource(owner, nameOf(classRep), src)
case (None, Some(src)) =>
if (settings.verbose) inform("[symloader] no class, picked up source file for " + src.path)
enterToplevelsFromSource(owner, nameOf(classRep), src)
case (Some(bin), _) =>
enterClassAndModule(owner, nameOf(classRep), new ClassfileLoader(bin, _, _))
}
}
private def nameOf(classRep: ClassRepresentation): TermName = {
while(true) {
val len = classRep.nameChars(nameCharBuffer)
if (len == -1) nameCharBuffer = new Array[Char](nameCharBuffer.length * 2)
else return newTermName(nameCharBuffer, 0, len)
}
throw new IllegalStateException()
}
private var nameCharBuffer = new Array[Char](256)
/**
* A lazy type that completes itself by calling parameter doComplete.
* Any linked modules/classes or module classes are also initialized.
* Todo: consider factoring out behavior from TopClassCompleter/SymbolLoader into
* supertrait SymLoader
*/
abstract class SymbolLoader extends SymLoader {
/** Load source or class file for `root`, return */
protected def doComplete(root: Symbol): Unit
def sourcefile: Option[AbstractFile] = None
def associatedFile(self: Symbol): AbstractFile = NoAbstractFile
/**
* Description of the resource (ClassPath, AbstractFile)
* being processed by this loader
*/
protected def description: String
private var ok = false
private def setSource(sym: Symbol): Unit = {
sourcefile foreach (sf => sym match {
case cls: ClassSymbol => cls.associatedFile = sf
case mod: ModuleSymbol => mod.moduleClass.associatedFile = sf
case _ => ()
})
}
override def complete(root: Symbol): Unit = {
val assocFile = associatedFile(root)
currentRunProfilerBeforeCompletion(root, assocFile)
try {
try {
informingProgress("loaded " + description) {
val currentphase = phase
try doComplete(root)
finally phase = currentphase
}
ok = true
setSource(root)
setSource(root.companionSymbol) // module -> class, class -> module
}
catch {
case ex@(_: IOException | _: MissingRequirementError) =>
ok = false
signalError(root, ex)
}
initRoot(root)
if (!root.isPackageClass) initRoot(root.companionSymbol)
} finally {
currentRunProfilerAfterCompletion(root, assocFile)
}
}
override def load(root: Symbol): Unit = { complete(root) }
private def markAbsent(sym: Symbol): Unit = {
val tpe: Type = if (ok) NoType else ErrorType
if (sym != NoSymbol)
sym setInfo tpe
}
private def initRoot(root: Symbol): Unit = {
if (root.rawInfo == this)
List(root, root.moduleClass) foreach markAbsent
else if (root.isClass && !root.isModuleClass)
root.rawInfo.load(root)
}
}
/**
* Loads contents of a package
*/
class PackageLoader(packageName: String, classPath: ClassPath) extends SymbolLoader with FlagAgnosticCompleter {
protected def description = {
val shownPackageName = if (packageName == ClassPath.RootPackage) "" else packageName
s"package loader $shownPackageName"
}
protected def doComplete(root: Symbol): Unit = {
assert(root.isPackageClass, root)
root.setInfo(new PackageClassInfoType(newScope, root))
val classPathEntries = classPath.list(packageName)
if (!root.isRoot)
for (entry <- classPathEntries.classesAndSources) initializeFromClassPath(root, entry)
if (!root.isEmptyPackageClass) {
for (pkg <- classPathEntries.packages) {
val fullName = pkg.name
val name =
if (packageName == ClassPath.RootPackage) fullName
else fullName.substring(packageName.length + 1)
val packageLoader = new PackageLoader(fullName, classPath)
enterPackage(root, name, packageLoader)
}
openPackageModule(root)
}
}
}
private lazy val classFileDataReader: ReusableInstance[ReusableDataReader] = ReusableInstance[ReusableDataReader](new ReusableDataReader(), enabled = isCompilerUniverse)
class ClassfileLoader(val classfile: AbstractFile, clazz: ClassSymbol, module: ModuleSymbol) extends SymbolLoader with FlagAssigningCompleter {
private object classfileParser extends {
val symbolTable: SymbolLoaders.this.symbolTable.type = SymbolLoaders.this.symbolTable
} with ClassfileParser(classFileDataReader) {
override protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol =
SymbolLoaders.this.lookupMemberAtTyperPhaseIfPossible(sym, name)
/*
* The type alias and the cast (where the alias is used) is needed due to problem described
* in scala/bug#7585. In this particular case, the problem is that we need to make sure that symbol
* table used by symbol loaders is exactly the same as they one used by classfileParser.
* If you look at the path-dependent types we have here everything should work out ok but
* due to issue described in scala/bug#7585 type-checker cannot tie the knot here.
*
*/
private type SymbolLoadersRefined = SymbolLoaders { val symbolTable: classfileParser.symbolTable.type }
val loaders = SymbolLoaders.this.asInstanceOf[SymbolLoadersRefined]
override def classPath: ClassPath = platform.classPath
}
protected def description = "class file "+ classfile.toString
protected def doComplete(root: Symbol): Unit = {
val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.classReadNanos) else null
classfileParser.parse(classfile, clazz, module)
if (clazz.associatedFile eq NoAbstractFile) clazz.associatedFile = classfile
if (module.associatedFile eq NoAbstractFile) module.associatedFile = classfile
if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.classReadNanos, start)
}
override def sourcefile: Option[AbstractFile] = classfileParser.srcfile
override def associatedFile(self: Symbol): AbstractFile = classfile
}
class SourcefileLoader(val srcfile: AbstractFile) extends SymbolLoader with FlagAssigningCompleter {
protected def description = "source file "+ srcfile.toString
override def fromSource = true
override def sourcefile = Some(srcfile)
override def associatedFile(self: Symbol): AbstractFile = srcfile
protected def doComplete(root: Symbol): Unit = compileLate(srcfile)
}
object moduleClassLoader extends SymbolLoader with FlagAssigningCompleter {
protected def description = "module class loader"
protected def doComplete(root: Symbol): Unit = { root.sourceModule.initialize }
override def associatedFile(self: Symbol): AbstractFile = {
val sourceModule = self.sourceModule
sourceModule.rawInfo match {
case loader: SymbolLoader => loader.associatedFile(sourceModule)
case _ => super.associatedFile(self)
}
}
}
/** used from classfile parser to avoid cycles */
var parentsLevel = 0
var pendingLoadActions: List[() => Unit] = Nil
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy