/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author Martin Odersky
*/
package scala.tools.nsc
import java.io.{ File, FileOutputStream, PrintWriter, IOException, FileNotFoundException }
import java.nio.charset.{ Charset, IllegalCharsetNameException, UnsupportedCharsetException }
import compat.Platform.currentTime
import io.{ SourceReader, AbstractFile, Path }
import reporters.{ Reporter, ConsoleReporter }
import util.{ ClassPath, SourceFile, Statistics, BatchSourceFile, ScriptSourceFile, returning }
import collection.mutable.{ HashSet, HashMap, ListBuffer }
import reflect.generic.{ PickleBuffer }
import symtab.{ Flags, SymbolTable, SymbolLoaders }
import symtab.classfile.Pickler
import dependencies.DependencyAnalysis
import plugins.Plugins
import ast._
import ast.parser._
import typechecker._
import transform._
import backend.icode.{ ICodes, GenICode, Checkers }
import backend.{ ScalaPrimitives, Platform, MSILPlatform, JavaPlatform }
import backend.jvm.GenJVM
import backend.opt.{ Inliners, ClosureElimination, DeadCodeElimination }
import backend.icode.analysis._
class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
with CompilationUnits
with Plugins
with PhaseAssembly
{
// alternate constructors ------------------------------------------
def this(reporter: Reporter) =
this(new Settings(err => reporter.error(null,err)),
reporter)
def this(settings: Settings) =
this(settings, new ConsoleReporter(settings))
// platform specific elements
type ThisPlatform = Platform[_] { val global: Global.this.type }
lazy val platform: ThisPlatform =
if (forMSIL) new { val global: Global.this.type = Global.this } with MSILPlatform
else new { val global: Global.this.type = Global.this } with JavaPlatform
def classPath: ClassPath[_] = platform.classPath
def rootLoader: LazyType = platform.rootLoader
// sub-components --------------------------------------------------
/** Print tree in detailed form */
object nodePrinters extends {
val global: Global.this.type = Global.this
} with NodePrinters {
infolevel = InfoLevel.Verbose
}
val nodeToString = nodePrinters.nodeToString
/** Generate ASTs */
object gen extends {
val global: Global.this.type = Global.this
} with TreeGen {
def mkAttributedCast(tree: Tree, pt: Type): Tree =
typer.typed(mkCast(tree, pt))
}
/** Fold constants */
object constfold extends {
val global: Global.this.type = Global.this
} with ConstantFolder
/** Tree checker (used for testing and debugging) */
object checker extends {
val global: Global.this.type = Global.this
} with TreeCheckers
/** ICode generator */
object icodes extends {
val global: Global.this.type = Global.this
} with ICodes
/** ICode analysis for optimization */
object analysis extends {
val global: Global.this.type = Global.this
} with TypeFlowAnalysis
/** Copy propagation for optimization */
object copyPropagation extends {
val global: Global.this.type = Global.this
} with CopyPropagation
/** Icode verification */
object checkers extends {
val global: Global.this.type = Global.this
} with Checkers
/** Some statistics (normally disabled) */
object statistics extends {
val global: Global.this.type = Global.this
} with Statistics
util.Statistics.enabled = settings.Ystatistics.value
/** Computing pairs of overriding/overridden symbols */
object overridingPairs extends {
val global: Global.this.type = Global.this
} with OverridingPairs
/** Representing ASTs as graphs */
object treeBrowsers extends {
val global: Global.this.type = Global.this
} with TreeBrowsers
val treeBrowser = treeBrowsers.create()
// ------------ Hooks for interactive mode-------------------------
/** Called every time an AST node is successfully typechecked in typerPhase.
*/
def signalDone(context: analyzer.Context, old: Tree, result: Tree) {}
/** Register new context; called for every created context
*/
def registerContext(c: analyzer.Context) {}
/** Register top level class (called on entering the class)
*/
def registerTopLevelSym(sym: Symbol) {}
// ------------------ Reporting -------------------------------------
def error(msg: String) = reporter.error(NoPosition, msg)
def warning(msg: String) =
if (settings.Xwarnfatal.value) reporter.error(NoPosition, msg)
else reporter.warning(NoPosition, msg)
def inform(msg: String) = reporter.info(NoPosition, msg, true)
def inform[T](msg: String, value: T): T = { inform(msg+value); value }
//reporter.info(null, msg, true)
def informProgress(msg: String) =
if (settings.verbose.value) inform("[" + msg + "]")
def informTime(msg: String, start: Long) =
informProgress(msg + " in " + (currentTime - start) + "ms")
/*@inline final*/ def log(msg: => AnyRef) {
if (settings.log contains phase.name) inform("[log " + phase + "] " + msg)
}
class ThrowableWithPosition(val pos: Int, val error: Throwable) extends Throwable
def tryWith[T](pos: Int, body: => T): T =
try body
catch {
case e : ThrowableWithPosition => throw e
case te: TypeError => throw te
case e : RuntimeException => throw new ThrowableWithPosition(pos, e)
}
def catchWith[T](source : SourceFile, body : => T) : T =
try body
catch {
case e : ThrowableWithPosition =>
logError("POS: " + source.dbg(e.pos), e)
throw e.error
}
def logError(msg: String, t: Throwable): Unit = ()
// ------------ File interface -----------------------------------------
private val reader: SourceReader = {
def stdCharset: Charset = {
settings.encoding.value = Properties.sourceEncoding // A mandatory charset
Charset.forName(settings.encoding.value)
}
val charset =
try {
Charset.forName(settings.encoding.value)
} catch {
case _: IllegalCharsetNameException =>
error("illegal charset name '" + settings.encoding.value + "'")
stdCharset
case _: UnsupportedCharsetException =>
error("unsupported charset '" + settings.encoding.value + "'")
stdCharset
}
try {
val clazz = Class.forName(settings.sourceReader.value)
val ccon = clazz.getConstructor(classOf[java.nio.charset.CharsetDecoder], classOf[Reporter])
ccon.newInstance(charset.newDecoder(), reporter).asInstanceOf[SourceReader]
//new SourceReader(charset.newDecoder())
} catch {
case e =>
error("exception while trying to instantiate source reader \""+settings.sourceReader.value+"\" ");
new SourceReader(charset.newDecoder(), reporter)
}
}
if (settings.make.value != "all")
settings.dependenciesFile.value match {
case "none" => ()
case x =>
val depFilePath = Path(x)
if (depFilePath.exists) {
/** The directory where file lookup should start */
val rootPath = depFilePath.parent
def toFile(path: String) = AbstractFile.getFile(rootPath resolve Path(path))
dependencyAnalysis.loadFrom(AbstractFile.getFile(depFilePath), toFile)
}
}
if (settings.verbose.value || settings.Ylogcp.value) {
inform("[search path for source files: " + classPath.sourcepaths.mkString(",") + "]")
inform("[search path for class files: " + classPath.asClasspathString + "]")
}
/** True if -Xscript has been set, indicating a script run.
*/
def isScriptRun = settings.script.value != ""
def getSourceFile(f: AbstractFile): BatchSourceFile =
if (isScriptRun) ScriptSourceFile(f, reader read f)
else new BatchSourceFile(f, reader read f)
def getSourceFile(name: String): SourceFile = {
val f = AbstractFile.getFile(name)
if (f eq null) throw new FileNotFoundException(
"source file '" + name + "' could not be found")
getSourceFile(f)
}
lazy val loaders = new SymbolLoaders {
val global: Global.this.type = Global.this
}
// ------------ Phases -------------------------------------------}
var globalPhase: Phase = NoPhase
val MaxPhases = 64
val phaseWithId = new Array[Phase](MaxPhases)
for (i <- List.range(0, MaxPhases)) phaseWithId(i) = NoPhase
abstract class GlobalPhase(prev: Phase) extends Phase(prev) {
phaseWithId(id) = this
def run { currentRun.units foreach applyPhase }
def apply(unit: CompilationUnit): Unit
private val isErased = prev.name == "erasure" || prev.erasedTypes
override def erasedTypes: Boolean = isErased
private val isFlat = prev.name == "flatten" || prev.flatClasses
override def flatClasses: Boolean = isFlat
private val isDevirtualized = prev.name == "devirtualize" || prev.devirtualized
override def devirtualized: Boolean = isDevirtualized // (part of DEVIRTUALIZE)
private val isSpecialized = prev.name == "specialize" || prev.specialized
override def specialized: Boolean = isSpecialized
/** Is current phase cancelled on this unit? */
def cancelled(unit: CompilationUnit) = {
// run the typer only if in `createJavadoc` mode
val maxJavaPhase = if (createJavadoc) currentRun.typerPhase.id else currentRun.namerPhase.id
reporter.cancelled || unit.isJava && this.id > maxJavaPhase
}
final def applyPhase(unit: CompilationUnit) {
if (settings.debug.value) inform("[running phase " + name + " on " + unit + "]")
val unit0 = currentRun.currentUnit
try {
currentRun.currentUnit = unit
if (!cancelled(unit))
reporter.withSource(unit.source) { apply(unit) }
currentRun.advanceUnit
} finally {
//assert(currentRun.currentUnit == unit)
currentRun.currentUnit = unit0
}
}
}
// phaseName = "parser"
object syntaxAnalyzer extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]()
val runsRightAfter = None
} with SyntaxAnalyzer
// factory method for
// phaseName = "namer"
// phaseName = "parser"
object analyzer extends {
val global: Global.this.type = Global.this
} with Analyzer
/** Switch to turn on detailed type logs */
var printTypings = settings.Ytyperdebug.value
// phaseName = "superaccessors"
object superAccessors extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("typer")
val runsRightAfter = None
} with SuperAccessors
// phaseName = "pickler"
object pickler extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("superaccessors")
val runsRightAfter = None
} with Pickler
// phaseName = "refchecks"
object refchecks extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("pickler")
val runsRightAfter = None
} with RefChecks
// object devirtualize extends {
// val global: Global.this.type = Global.this
// } with DeVirtualize
// phaseName = "liftcode"
object liftcode extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("refchecks")
val runsRightAfter = None
} with LiftCode
// phaseName = "uncurry"
object uncurry extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("refchecks","liftcode")
val runsRightAfter = None
} with UnCurry
// phaseName = "tailcalls"
object tailCalls extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("uncurry")
val runsRightAfter = None
} with TailCalls
// phaseName = "explicitouter"
object explicitOuter extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("tailcalls")
val runsRightAfter = None
} with ExplicitOuter
// phaseName = "specialize"
object specializeTypes extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("")
val runsRightAfter = Some("tailcalls")
} with SpecializeTypes
// phaseName = "erasure"
object erasure extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("explicitouter")
val runsRightAfter = Some("explicitouter")
} with Erasure
// phaseName = "lazyvals"
object lazyVals extends {
val global: Global.this.type = Global.this
final val FLAGS_PER_WORD = 32
val runsAfter = List[String]("erasure")
val runsRightAfter = None
} with LazyVals
// phaseName = "lambdalift"
object lambdaLift extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("lazyvals")
val runsRightAfter = None
} with LambdaLift
// phaseName = "detach"
// object detach extends {
// val global: Global.this.type = Global.this
// val runsAfter = List("lambdalift")
// val runsRightAfter = Some("lambdalift")
// } with Detach
// phaseName = "constructors"
object constructors extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("lambdalift")
val runsRightAfter = None
} with Constructors
// phaseName = "flatten"
object flatten extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("constructors")
val runsRightAfter = None
} with Flatten
// phaseName = "mixin"
object mixer extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("flatten","constructors")
val runsRightAfter = None
} with Mixin
// phaseName = "cleanup"
object cleanup extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("mixin")
val runsRightAfter = None
} with CleanUp
// phaseName = "icode"
object genicode extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("cleanup")
val runsRightAfter = None
} with GenICode
// object icodePrinter extends backend.icode.Printers {
// val global: Global.this.type = Global.this
// }
// phaseName = "???"
object scalaPrimitives extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]()
val runsRightAfter = None
} with ScalaPrimitives
// phaseName = "inliner"
object inliner extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("icode")
val runsRightAfter = None
} with Inliners
// phaseName = "closelim"
object closureElimination extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("inliner")
val runsRightAfter = None
} with ClosureElimination
// phaseName = "dce"
object deadCode extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("closelim")
val runsRightAfter = None
} with DeadCodeElimination
// phaseName = "jvm"
object genJVM extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]("dce")
val runsRightAfter = None
} with GenJVM
object dependencyAnalysis extends {
val global: Global.this.type = Global.this
val runsAfter = List("jvm")
val runsRightAfter = None
} with DependencyAnalysis
// phaseName = "terminal"
object terminal extends {
val global: Global.this.type = Global.this
val phaseName = "terminal"
val runsAfter = List[String]("jvm","msil")
val runsRightAfter = None
} with SubComponent {
private var cache: Option[GlobalPhase] = None
def newPhase(prev: Phase): GlobalPhase = {
if (cache.isEmpty) cache = Some(new TerminalPhase(prev))
cache.get
}
def reset() {
cache = None
}
class TerminalPhase(prev: Phase) extends GlobalPhase(prev) {
def name = "terminal"
def apply(unit: CompilationUnit) {}
}
}
// phaseName = "SAMPLE PHASE"
object sampleTransform extends {
val global: Global.this.type = Global.this
val runsAfter = List[String]()
val runsRightAfter = None
} with SampleTransform
object icodeChecker extends checkers.ICodeChecker()
object typer extends analyzer.Typer(
analyzer.NoContext.make(EmptyTree, Global.this.definitions.RootClass, new Scope))
/* Add the internal compiler phases to the phases set
*/
protected def computeInternalPhases() {
phasesSet += syntaxAnalyzer // The parser
phasesSet += analyzer.namerFactory // note: types are there because otherwise
phasesSet += analyzer.packageObjects // consistency check after refchecks would fail.
phasesSet += analyzer.typerFactory
phasesSet += superAccessors // add super accessors
phasesSet += pickler // serialize symbol tables
phasesSet += refchecks // perform reference and override checking, translate nested objects
// if (false && settings.YvirtClasses)
// phasesSet += devirtualize // Desugar virtual classes4
phasesSet += uncurry // uncurry, translate function values to anonymous classes
phasesSet += tailCalls // replace tail calls by jumps
phasesSet += specializeTypes
phasesSet += explicitOuter // replace C.this by explicit outer pointers, eliminate pattern matching
phasesSet += erasure // erase types, add interfaces for traits
phasesSet += lazyVals
phasesSet += lambdaLift // move nested functions to top level
// if (forJVM && settings.Xdetach.value)
// phasesSet += detach // convert detached closures
phasesSet += constructors // move field definitions into constructors
phasesSet += mixer // do mixin composition
phasesSet += cleanup // some platform-specific cleanups
phasesSet += genicode // generate portable intermediate code
phasesSet += inliner // optimization: do inlining
phasesSet += closureElimination // optimization: get rid of uncalled closures
phasesSet += deadCode // optimization: get rid of dead cpde
phasesSet += terminal // The last phase in the compiler chain
}
protected def computePlatformPhases() = platform.platformPhases foreach (phasesSet += _)
/* Helper method for sequncing the phase assembly
*/
private def computePhaseDescriptors: List[SubComponent] = {
computeInternalPhases() // Global.scala
computePlatformPhases() // backend/Platform.scala
computePluginPhases() // plugins/Plugins.scala
buildCompilerFromPhasesSet() // PhaseAssembly.scala
}
/* The phase descriptor list */
lazy val phaseDescriptors: List[SubComponent] = computePhaseDescriptors
/* The set of phase objects that is the basis for the compiler phase chain */
protected val phasesSet : HashSet[SubComponent] = new HashSet[SubComponent]
/** The names of the phases. */
lazy val phaseNames = {
new Run // force some initialization
phaseDescriptors map (_.phaseName)
}
/** A description of the phases that will run */
def phaseDescriptions: String =
phaseNames mkString "\n" // todo: + " - " + phase.description
// ----------- Runs ---------------------------------------
private var curRun: Run = null
private var curRunId = 0
/** The currently active run
*/
def currentRun: Run = curRun
/** The id of the currently active run
*/
override def currentRunId = curRunId
/** A Run is a single execution of the compiler on a sets of units
*/
class Run {
var isDefined = false
private val firstPhase = {
// ----------- Initialization code -------------------------
curRunId += 1
assert(curRunId > 0)
curRun = this
//Console.println("starting run: " + id)
// Can not take the phaseDescriptors.head even though its the syntaxAnalyzer, this will implicitly
// call definitions.init which uses phase and needs it to be != NoPhase
val phase1 = syntaxAnalyzer.newPhase(NoPhase)
phase = phase1
definitions.init // needs phase to be defined != NoPhase,
// that's why it is placed here.
// The first phase in the compiler phase chain
var p: Phase = phase1
// Reset the cache in terminal, the chain could have been build before where nobody used it
// This happens in the interpreter
terminal.reset
// Each subcomponent is asked to deliver a newPhase that is chained together. If -Ystop:phasename is
// given at command-line, this will stop with that phasename
for (pd <- phaseDescriptors.tail.takeWhile(pd => !(stopPhase(pd.phaseName))))
if (!(settings.skip contains pd.phaseName)) p = pd.newPhase(p)
// Ensure there is a terminal phase at the end, Normally there will then be two terminal phases at the end
// if -Ystop:phasename was given, this makes sure that there is a terminal phase at the end
p = terminal.newPhase(p)
phase1
}
// --------------- Miscellania -------------------------------
/** The currently compiled unit; set from GlobalPhase */
var currentUnit: CompilationUnit = _
/** Flags indicating whether deprecation warnings occurred */
var deprecationWarnings: Boolean = false
var uncheckedWarnings: Boolean = false
def cancel { reporter.cancelled = true }
// ------------------ Progress tracking -------------------------
def progress(current: Int, total: Int) {}
private var phasec: Int = 0
private var unitc: Int = 0
/** take note that phase is completed
* (for progress reporting)
*/
def advancePhase {
unitc = 0
phasec += 1
refreshProgress
}
/** take note that a phase on a unit is completed
* (for progress reporting)
*/
def advanceUnit {
unitc += 1
refreshProgress
}
private def refreshProgress =
if (compiledFiles.size > 0)
progress((phasec * compiledFiles.size) + unitc,
(phaseDescriptors.length-1) * compiledFiles.size) // terminal phase not part of the progress display
// ----- finding phases --------------------------------------------
def phaseNamed(name: String): Phase = {
var p: Phase = firstPhase
while (p.next != p && p.name != name) p = p.next
if (p.name != name) NoPhase else p
}
val namerPhase = phaseNamed("namer")
val typerPhase = phaseNamed("typer")
val picklerPhase = phaseNamed("pickler")
val refchecksPhase = phaseNamed("refchecks")
val uncurryPhase = phaseNamed("uncurry")
val explicitOuterPhase = phaseNamed("explicitouter")
val erasurePhase = phaseNamed("erasure")
val flattenPhase = phaseNamed("flatten")
val mixinPhase = phaseNamed("mixin")
val icodePhase = phaseNamed("icode")
isDefined = true
/** A test whether compilation should stop at phase with given name */
protected def stopPhase(name : String) = settings.stop.contains(name)
// ----------- Units and top-level classes and objects --------
private var unitbuf = new ListBuffer[CompilationUnit]
var compiledFiles = new HashSet[String]
/** add unit to be compiled in this run */
private def addUnit(unit: CompilationUnit) {
// unit.parseSettings()
unitbuf += unit
compiledFiles += unit.source.file.path
}
/* An iterator returning all the units being compiled in this run */
def units: Iterator[CompilationUnit] = unitbuf.iterator
/** A map from compiled top-level symbols to their source files */
val symSource = new HashMap[Symbol, AbstractFile]
/** A map from compiled top-level symbols to their picklers */
val symData = new HashMap[Symbol, PickleBuffer]
/** does this run compile given class, module, or case factory? */
def compiles(sym: Symbol): Boolean =
if (sym == NoSymbol) false
else if (symSource.isDefinedAt(sym)) true
else if (!sym.owner.isPackageClass) compiles(sym.toplevelClass)
else if (sym.isModuleClass) compiles(sym.sourceModule)
else false
/** Is this run allowed to redefine the given symbol? Usually this is true
* if the run does not already compile `sym`, but for interactive mode
* we have a more liberal interpretation.
*/
def canRedefine(sym: Symbol) = !compiles(sym)
// --------------- Compilation methods ----------------------------
/** Compile list of source files */
def compileSources(_sources: List[SourceFile]) {
val depSources = dependencyAnalysis.filter(_sources.distinct) // bug #1268, scalac confused by duplicated filenames
val sources = coreClassesFirst(depSources)
if (reporter.hasErrors)
return // there is a problem already, e.g. a
// plugin was passed a bad option
val startTime = currentTime
reporter.reset
for (source <- sources) addUnit(new CompilationUnit(source))
globalPhase = firstPhase
while (globalPhase != terminal.newPhase(NoPhase) && !reporter.hasErrors) {
val startTime = currentTime
phase = globalPhase
globalPhase.run
if (settings.Xprint contains globalPhase.name)
if (settings.writeICode.value && globalPhase.id >= icodePhase.id) writeICode()
else if (settings.Xshowtrees.value) nodePrinters.printAll()
else printAllUnits()
if (settings.printLate.value && globalPhase.name == "cleanup")
printAllUnits()
if (settings.browse contains globalPhase.name) treeBrowser.browse(units)
informTime(globalPhase.description, startTime)
globalPhase = globalPhase.next
if (settings.check contains globalPhase.prev.name) {
if (globalPhase.prev.checkable) {
phase = globalPhase
inform("[Now checking: " + phase.prev.name + "]")
if (globalPhase.id >= icodePhase.id) icodeChecker.checkICodes
else checker.checkTrees
}
else inform("[Not checkable: " + globalPhase.prev.name + "]")
}
if (settings.Ystatistics.value) statistics.print(phase)
advancePhase
}
//println(narrowCount+" narrowings")
if (settings.Xshowcls.value != "")
showDef(newTermName(settings.Xshowcls.value), false)
if (settings.Xshowobj.value != "")
showDef(newTermName(settings.Xshowobj.value), true)
if (reporter.hasErrors) {
for ((sym, file) <- symSource.iterator) {
sym.reset(new loaders.SourcefileLoader(file))
if (sym.isTerm) sym.moduleClass.reset(loaders.moduleClassLoader)
}
} else {
//assert(symData.isEmpty || !settings.stop.value.isEmpty || !settings.skip.value.isEmpty, symData)
if (deprecationWarnings) {
warning("there were deprecation warnings; re-run with " + settings.deprecation.name + " for details")
}
if (uncheckedWarnings) {
warning("there were unchecked warnings; re-run with " + settings.unchecked.name + " for details")
}
}
for ((sym, file) <- symSource.iterator) resetPackageClass(sym.owner)
informTime("total", startTime)
if (!dependencyAnalysis.off) {
settings.dependenciesFile.value match {
case "none" =>
case x =>
val depFilePath = Path(x)
if (!depFilePath.exists)
dependencyAnalysis.dependenciesFile = AbstractFile.getFile(depFilePath.createFile())
/** The directory where file lookup should start */
val rootPath = depFilePath.parent.normalize
def fromFile(file: AbstractFile): String =
rootPath.relativize(Path(file.file).normalize).path
dependencyAnalysis.saveDependencies(fromFile)
}
}
}
/** Compile list of abstract files */
def compileFiles(files: List[AbstractFile]) {
try compileSources(files map getSourceFile)
catch { case ex: IOException => error(ex.getMessage()) }
}
/** Compile list of files given by their names */
def compile(filenames: List[String]) {
try {
val sources: List[SourceFile] =
if (isScriptRun && filenames.size > 1) returning(Nil)(_ => error("can only compile one script at a time"))
else filenames map getSourceFile
compileSources(sources)
}
catch { case ex: IOException => error(ex.getMessage()) }
}
/** Compile abstract file until `globalPhase`, but at least
* to phase "namer".
*/
def compileLate(file: AbstractFile) {
if (compiledFiles eq null) {
val msg = "No class file for " + file +
" was found\n(This file cannot be loaded as a source file)"
inform(msg)
throw new FatalError(msg)
} else if (!(compiledFiles contains file.path)) {
compileLate(new CompilationUnit(getSourceFile(file)))
}
}
/** Compile abstract file until `globalPhase`, but at least
* to phase "namer".
*/
def compileLate(unit: CompilationUnit) {
addUnit(unit)
var localPhase = firstPhase.asInstanceOf[GlobalPhase]
while (localPhase != null && (localPhase.id < globalPhase.id || localPhase.id < typerPhase.id)/* && !reporter.hasErrors*/) {
val oldSource = reporter.getSource
reporter.withSource(unit.source) {
atPhase(localPhase)(localPhase.applyPhase(unit))
}
val newLocalPhase = localPhase.next.asInstanceOf[GlobalPhase]
localPhase = if (localPhase == newLocalPhase) null else newLocalPhase
}
refreshProgress
}
/**
* Attempt to locate a source file providing the given name as a top-level
* definition in the given context, and add it to the run via compileLate
* if found.
*/
def compileSourceFor(context : analyzer.Context, name : Name) = false
/**
* Attempt to locate a source file providing the given name as a top-level
* definition with the given prefix, and add it to the run via compileLate
* if found.
*/
def compileSourceFor(qual : Tree, name : Name) = false
/** Reset package class to state at typer (not sure what this
* is needed for?)
*/
private def resetPackageClass(pclazz: Symbol) {
atPhase(firstPhase) {
pclazz.setInfo(atPhase(typerPhase)(pclazz.info))
}
if (!pclazz.isRoot) resetPackageClass(pclazz.owner)
}
/**
* Re-orders the source files to
* 1. ScalaObject
* 2. LowPriorityImplicits / StandardEmbeddings (i.e. parents of Predef)
* 3. the rest
*
* 1 is to avoid cyclic reference errors.
* 2 is due to the following. When completing "Predef" (*), typedIdent is called
* for its parents (e.g. "LowPriorityImplicits"). typedIdent checks wethter
* the symbol reallyExists, which tests if the type of the symbol after running
* its completer is != NoType.
* If the "namer" phase has not yet run for "LowPriorityImplicits", the symbol
* has a SourcefileLoader as type. Calling "doComplete" on it does nothing at
* all, because the source file is part of the files to be compiled anyway.
* So the "reallyExists" test will return "false".
* Only after the namer, the symbol has a lazy type which actually computes
* the info, and "reallyExists" behaves as expected.
* So we need to make sure that the "namer" phase is run on predef's parents
* before running it on predef.
*
* (*) Predef is completed early when calling "mkAttributedRef" during the
* addition of "import Predef._" to sourcefiles. So this situation can't
* happen for user classes.
*
*/
private def coreClassesFirst(files: List[SourceFile]) = {
def inScalaFolder(f: SourceFile) =
f.file.container.name == "scala"
var scalaObject: Option[SourceFile] = None
val res = new ListBuffer[SourceFile]
for (file <- files) file.file.name match {
case "ScalaObject.scala" if inScalaFolder(file) => scalaObject = Some(file)
case "LowPriorityImplicits.scala" if inScalaFolder(file) => file +=: res
case "StandardEmbeddings.scala" if inScalaFolder(file) => file +=: res
case _ => res += file
}
for (so <- scalaObject) so +=: res
res.toList
}
} // class Run
def printAllUnits() {
print("[[syntax trees at end of " + phase + "]]")
atPhase(phase.next) {
for (unit <- currentRun.units) treePrinter.print(unit)
}
}
def showDef(name: Name, module: Boolean) {
def getSym(name: Name, module: Boolean): Symbol = {
var i = name.length - 1
while (i != 0 && name(i) != '#' && name(i) != '.') i -= 1
if (i == 0)
definitions.getModule(name)
else {
val root = getSym(name.subName(0, i), name(i) == '.')
var selector = name.subName(i+1, name.length)
if (module) selector = selector.toTypeName
root.info.member(selector)
}
}
val sym = getSym(name, module)
inform("" + sym.name + ":" +(if (module) sym.tpe.typeSymbol.info else sym.info))
}
/** Returns the file with the given suffix for the given class. Used for icode writing. */
def getFile(clazz: Symbol, suffix: String): File = {
val outdirname = settings.outputDirs.outputDirFor(clazz.sourceFile)
var outdir = new File(if (outdirname.path == "") "." else outdirname.path)
val filename = clazz.fullName
var start = 0
var end = filename.indexOf('.', start)
while (end >= start) {
outdir = new File(outdir, filename.substring(start, end))
if (!outdir.exists()) outdir.mkdir()
start = end + 1
end = filename.indexOf('.', start)
}
new File(outdir, filename.substring(start) + suffix)
}
private def writeICode() {
val printer = new icodes.TextPrinter(null, icodes.linearizer)
icodes.classes.values.foreach((cls) => {
val suffix = if (cls.symbol hasFlag Flags.MODULE) "$.icode" else ".icode"
var file = getFile(cls.symbol, suffix)
// if (file.exists())
// file = new File(file.getParentFile(), file.getName() + "1")
try {
val stream = new FileOutputStream(file)
printer.setWriter(new PrintWriter(stream, true))
printer.printClass(cls)
informProgress("wrote " + file)
} catch {
case ex: IOException =>
if (settings.debug.value) ex.printStackTrace()
error("could not write file " + file)
}
})
}
def forJVM : Boolean = settings.target.value startsWith "jvm"
def forMSIL: Boolean = settings.target.value == "msil"
def onlyPresentation = false
def createJavadoc = false
}