diff options
author | Paul Phillips <paulp@improving.org> | 2010-11-07 23:48:57 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2010-11-07 23:48:57 +0000 |
commit | 1016d68bef18ea078a83af6e550adf2d8a818ddd (patch) | |
tree | 95aaa82c97f1d90f27ebb306d7c98304504cd4ca /src | |
parent | 4659d81554c1eb5bda6730d48e0dfed747679862 (diff) | |
download | scala-1016d68bef18ea078a83af6e550adf2d8a818ddd.tar.gz scala-1016d68bef18ea078a83af6e550adf2d8a818ddd.tar.bz2 scala-1016d68bef18ea078a83af6e550adf2d8a818ddd.zip |
An overhaul of Global.
consistently, and remove things which are not being used anywhere
in the visible universe. Beyond general polish here are some of the
feature-like additions I can remember:
* -Xshow-phases now includes descriptions of the phases.
* -Xshow-class and -Xshow-object did not work as far as I could tell:
if they didn't, now they do. If they did, now they work better.
And you don't have to give it a fully qualified name anymore.
* -Xprint-icode will generate *.icode files (don't also have to say -Xprint:icode)
* counts of deprecation and unchcked warnings are given
* More documentation of what global is doing.
I tried not to break anything which might be using Global, but let me
know if I overshot somewhere. No review.
Diffstat (limited to 'src')
9 files changed, 408 insertions, 323 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index 564e4f2705..3c5e033c74 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -76,12 +76,12 @@ trait CompilationUnits { self: Global => reporter.warning(pos, msg) def deprecationWarning(pos: Position, msg: String) = - if (settings.deprecation.value) warning(pos, msg) - else currentRun.deprecationWarnings = true + if (opt.deprecation) warning(pos, msg) + else currentRun.deprecationWarnings += 1 def uncheckedWarning(pos: Position, msg: String) = - if (settings.unchecked.value) warning(pos, msg) - else currentRun.uncheckedWarnings = true + if (opt.unchecked) warning(pos, msg) + else currentRun.uncheckedWarnings += 1 def incompleteInputError(pos: Position, msg:String) = reporter.incompleteInputError(pos, msg) diff --git a/src/compiler/scala/tools/nsc/CompilerRun.scala b/src/compiler/scala/tools/nsc/CompilerRun.scala index 9cac12d896..e17ec6fd4c 100644 --- a/src/compiler/scala/tools/nsc/CompilerRun.scala +++ b/src/compiler/scala/tools/nsc/CompilerRun.scala @@ -11,7 +11,7 @@ class CompilerRun { def namerPhase: Phase = NoPhase def typerPhase: Phase = NoPhase def refchecksPhase: Phase = NoPhase - def explicitOuterPhase: Phase = NoPhase + def explicitouterPhase: Phase = NoPhase def erasurePhase: Phase = NoPhase def flattenPhase: Phase = NoPhase def mixinPhase: Phase = NoPhase diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 12fbad6adb..60b43a3d17 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -6,13 +6,13 @@ package scala.tools.nsc import java.io.{ File, FileOutputStream, PrintWriter, IOException, FileNotFoundException } -import java.nio.charset.{ Charset, IllegalCharsetNameException, UnsupportedCharsetException } +import java.nio.charset.{ Charset, CharsetDecoder, IllegalCharsetNameException, UnsupportedCharsetException } import compat.Platform.currentTime +import scala.collection.{ mutable, immutable } 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 } @@ -38,8 +38,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable // alternate constructors ------------------------------------------ def this(reporter: Reporter) = - this(new Settings(err => reporter.error(null,err)), - reporter) + this(new Settings(err => reporter.error(null, err)), reporter) def this(settings: Settings) = this(settings, new ConsoleReporter(settings)) @@ -57,14 +56,6 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable // 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 @@ -83,6 +74,18 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val global: Global.this.type = Global.this } with ICodes + /** Scala primitives, used in genicode */ + object scalaPrimitives extends { + val global: Global.this.type = Global.this + } with ScalaPrimitives + + /** Computing pairs of overriding/overridden symbols */ + object overridingPairs extends { + val global: Global.this.type = Global.this + } with OverridingPairs + + // Optimizer components + /** ICode analysis for optimization */ object analysis extends { val global: Global.this.type = Global.this @@ -93,21 +96,26 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val global: Global.this.type = Global.this } with CopyPropagation + // Components for collecting and generating output + /** Some statistics (normally disabled) set with -Ystatistics */ object statistics extends { val global: Global.this.type = Global.this } with Statistics - /** Computing pairs of overriding/overridden symbols */ - object overridingPairs extends { + /** Print tree in detailed form */ + object nodePrinters extends { val global: Global.this.type = Global.this - } with OverridingPairs + } with NodePrinters { + infolevel = InfoLevel.Verbose + } /** Representing ASTs as graphs */ object treeBrowsers extends { val global: Global.this.type = Global.this } with TreeBrowsers + val nodeToString = nodePrinters.nodeToString val treeBrowser = treeBrowsers.create() // ------------ Hooks for interactive mode------------------------- @@ -126,96 +134,109 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable // ------------------ Reporting ------------------------------------- - def error(msg: String) = reporter.error(NoPosition, msg) + def inform(msg: String) = reporter.info(NoPosition, msg, true) + def error(msg: String) = reporter.error(NoPosition, msg) def warning(msg: String) = - if (settings.Xwarnfatal.value) reporter.error(NoPosition, msg) + if (opt.fatalWarnings) error(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 informProgress(msg: String) = if (opt.verbose) inform("[" + msg + "]") + def inform[T](msg: String, value: T): T = returning(value)(x => inform(msg + x)) + def informTime(msg: String, start: Long) = informProgress(msg + " in " + (currentTime - start) + "ms") def logError(msg: String, t: Throwable): Unit = () + def log(msg: => AnyRef): Unit = if (opt.logPhase) inform("[log " + phase + "] " + msg) // ------------ 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 { + val defaultEncoding = Properties.sourceEncoding + val defaultReader = Properties.sourceReader + + def loadCharset(name: String) = + try Some(Charset.forName(name)) + catch { case _: IllegalCharsetNameException => - error("illegal charset name '" + settings.encoding.value + "'") - stdCharset + error("illegal charset name '" + name + "'") + None case _: UnsupportedCharsetException => - error("unsupported charset '" + settings.encoding.value + "'") - stdCharset + error("unsupported charset '" + name + "'") + None } - 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) + + val charset = opt.encoding flatMap loadCharset getOrElse { + settings.encoding.value = defaultEncoding // A mandatory charset + Charset.forName(defaultEncoding) } - } - 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) - } + def loadReader(name: String): Option[SourceReader] = { + def ccon = Class.forName(name).getConstructor(classOf[CharsetDecoder], classOf[Reporter]) + + try Some(ccon.newInstance(charset.newDecoder(), reporter).asInstanceOf[SourceReader]) + catch { case x => + error("exception while trying to instantiate source reader '" + name + "'") + None + } + } + + opt.sourceReader flatMap loadReader getOrElse { + new SourceReader(charset.newDecoder(), reporter) } + } - if (settings.verbose.value || settings.Ylogcp.value) { + if (!dependencyAnalysis.off) + dependencyAnalysis.loadDependencyAnalysis() + + if (opt.verbose || opt.logClasspath) { 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 != "" + /** Taking flag checking to a somewhat higher level. */ + object opt { + // True if the given PhasesSetting includes the current phase. + def isActive(ph: Settings#PhasesSetting) = ph contains globalPhase.name + def wasActive(ph: Settings#PhasesSetting) = ph contains globalPhase.prev.name + + // Some(value) if setting has been set by user, None otherwise. + def optSetting[T](s: Settings#Setting): Option[T] = + if (s.isDefault) None else Some(s.value.asInstanceOf[T]) + + def showClass = optSetting[String](settings.Xshowcls) + def showObject = optSetting[String](settings.Xshowobj) + def script = optSetting[String](settings.script) + def encoding = optSetting[String](settings.encoding) + def sourceReader = optSetting[String](settings.sourceReader) + + def debug = settings.debug.value + def deprecation = settings.deprecation.value + def fatalWarnings = settings.Xwarnfatal.value + def logClasspath = settings.Ylogcp.value + def printLate = settings.printLate.value + def printStats = settings.Ystatistics.value + def showTrees = settings.Xshowtrees.value + def target = settings.target.value + def typerDebug = settings.Ytyperdebug.value + def unchecked = settings.unchecked.value + def verbose = settings.verbose.value + def writeICode = settings.writeICode.value + + /** Flags as applied to the current or previous phase */ + def browsePhase = isActive(settings.browse) + def logPhase = isActive(settings.log) + def printPhase = isActive(settings.Xprint) + + def checkPhase = wasActive(settings.check) + + /** Derived values */ + def jvm = target startsWith "jvm" + def msil = target == "msil" + def verboseDebug = debug && verbose + def echoFilenames = opt.debug && (opt.verbose || currentRun.size < 5) + } + + // True if -Xscript has been set, indicating a script run. + def isScriptRun = opt.script.isDefined def getSourceFile(f: AbstractFile): BatchSourceFile = if (isScriptRun) ScriptSourceFile(f, reader read f) @@ -238,8 +259,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val MaxPhases = 64 - val phaseWithId = new Array[Phase](MaxPhases) - for (i <- List.range(0, MaxPhases)) phaseWithId(i) = NoPhase + val phaseWithId: Array[Phase] = Array.fill(MaxPhases)(NoPhase) abstract class GlobalPhase(prev: Phase) extends Phase(prev) { phaseWithId(id) = this @@ -270,7 +290,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable } final def applyPhase(unit: CompilationUnit) { - if (doEchoFilenames) + if (opt.echoFilenames) inform("[running phase " + name + " on " + unit + "]") val unit0 = currentRun.currentUnit @@ -286,6 +306,9 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable } } + /** Switch to turn on detailed type logs */ + var printTypings = opt.typerDebug + // phaseName = "parser" object syntaxAnalyzer extends { val global: Global.this.type = Global.this @@ -293,16 +316,11 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val runsRightAfter = None } with SyntaxAnalyzer - // factory method for - // phaseName = "namer" - // phaseName = "parser" + // factory for phases: namer, packageobjects, typer 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 @@ -334,7 +352,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable // phaseName = "uncurry" object uncurry extends { val global: Global.this.type = Global.this - val runsAfter = List[String]("refchecks","liftcode") + val runsAfter = List[String]("refchecks", "liftcode") val runsRightAfter = None } with UnCurry @@ -368,8 +386,8 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable // phaseName = "lazyvals" object lazyVals extends { - val global: Global.this.type = Global.this final val FLAGS_PER_WORD = 32 + val global: Global.this.type = Global.this val runsAfter = List[String]("erasure") val runsRightAfter = None } with LazyVals @@ -398,7 +416,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable // phaseName = "mixin" object mixer extends { val global: Global.this.type = Global.this - val runsAfter = List[String]("flatten","constructors") + val runsAfter = List[String]("flatten", "constructors") val runsRightAfter = None } with Mixin @@ -416,13 +434,6 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val runsRightAfter = None } with GenICode - // 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 @@ -451,6 +462,8 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val runsRightAfter = None } with GenJVM + // This phase is optional: only added if settings.make option is given. + // phaseName = "dependencyAnalysis" object dependencyAnalysis extends { val global: Global.this.type = Global.this val runsAfter = List("jvm") @@ -461,19 +474,14 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable object terminal extends { val global: Global.this.type = Global.this val phaseName = "terminal" - val runsAfter = List[String]("jvm","msil") + val runsAfter = List[String]("jvm", "msil") val runsRightAfter = None } with SubComponent { private var cache: Option[GlobalPhase] = None + def reset(): Unit = cache = None - def newPhase(prev: Phase): GlobalPhase = { - if (cache.isEmpty) cache = Some(new TerminalPhase(prev)) - cache.get - } - - def reset() { - cache = None - } + def newPhase(prev: Phase): GlobalPhase = + cache getOrElse returning(new TerminalPhase(prev))(x => cache = Some(x)) class TerminalPhase(prev: Phase) extends GlobalPhase(prev) { def name = "terminal" @@ -505,40 +513,56 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable object icodeChecker extends icodeCheckers.ICodeChecker() object typer extends analyzer.Typer( - analyzer.NoContext.make(EmptyTree, Global.this.definitions.RootClass, new Scope)) + analyzer.NoContext.make(EmptyTree, Global.this.definitions.RootClass, new Scope) + ) - /* Add the internal compiler phases to the phases set + /** Add the internal compiler phases to the phases set. + * This implementation creates a description map at the same time. */ 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 - 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 - 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 + // Note: this fits -Xshow-phases into 80 column width, which it is + // desirable to preserve. + val phs = List( + syntaxAnalyzer -> "parse source into ASTs, perform simple desugaring", + analyzer.namerFactory -> "resolve names, attach symbols to named trees", + analyzer.packageObjects -> "load package objects", + analyzer.typerFactory -> "the meat and potatoes: type the trees", + superAccessors -> "add super accessors in traits and nested classes", + pickler -> "serialize symbol tables", + refchecks -> "reference and override checking, translate nested objects", + uncurry -> "uncurry, translate function values to anonymous classes", + tailCalls -> "replace tail calls by jumps", + specializeTypes -> "@specialized-driven class and method specialization", + explicitOuter -> "C.this refs become outer pointers, translate pattern matches", + erasure -> "erase types, add interfaces for traits", + lazyVals -> "allocate bitmaps, translate lazy vals into lazified defs", + lambdaLift -> "move nested functions to top level", + constructors -> "move field definitions into constructors", + mixer -> "mixin composition", + cleanup -> "platform-specific cleanups, generate reflective calls", + genicode -> "generate portable intermediate code", + inliner -> "optimization: do inlining", + closureElimination -> "optimization: eliminate uncalled closures", + deadCode -> "optimization: eliminate dead code", + terminal -> "The last phase in the compiler chain" + ) + + phs foreach (addToPhasesSet _).tupled + } + // This is slightly inelegant but it avoids adding a new member to SubComponent, + // and attractive -Xshow-phases output is unlikely if the descs span 20 files anyway. + private val otherPhaseDescriptions = Map( + "flatten" -> "eliminate inner classes", + "liftcode" -> "reify trees", + "jvm" -> "generate JVM bytecode" + ) withDefaultValue "" + + protected def computePlatformPhases() = platform.platformPhases foreach { sub => + addToPhasesSet(sub, otherPhaseDescriptions(sub.phaseName)) } - protected def computePlatformPhases() = platform.platformPhases foreach (phasesSet += _) - - /* Helper method for sequncing the phase assembly - */ - private def computePhaseDescriptors: List[SubComponent] = { + // sequences the phase assembly + protected def computePhaseDescriptors: List[SubComponent] = { computeInternalPhases() // Global.scala computePlatformPhases() // backend/Platform.scala computePluginPhases() // plugins/Plugins.scala @@ -549,7 +573,12 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable 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] + protected val phasesSet = new mutable.HashSet[SubComponent] + protected val phasesDescMap = new mutable.HashMap[SubComponent, String] withDefaultValue "" + protected def addToPhasesSet(sub: SubComponent, descr: String) { + phasesSet += sub + phasesDescMap(sub) = descr + } /** The names of the phases. */ lazy val phaseNames = { @@ -558,8 +587,12 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable } /** A description of the phases that will run */ - def phaseDescriptions: String = - phaseNames mkString "\n" // todo: + " - " + phase.description + def phaseDescriptions: String = { + val width = phaseNames map (_.length) max + val fmt = "%" + width + "s %s\n" + + phaseDescriptors map (ph => fmt.format(ph.phaseName, phasesDescMap(ph))) mkString + } // ----------- Runs --------------------------------------- @@ -574,51 +607,55 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable */ override def currentRunId = curRunId - def currentRunSize = currentRun.unitbufSize - def doEchoFilenames = settings.debug.value && (settings.verbose.value || currentRunSize < 5) def echoPhaseSummary(ph: Phase) = { /** Only output a summary message under debug if we aren't echoing each file. */ - if (settings.debug.value && !doEchoFilenames) - inform("[running phase " + ph.name + " on " + currentRunSize + " compilation units]") + if (opt.debug && !opt.echoFilenames) + inform("[running phase " + ph.name + " on " + currentRun.size + " compilation units]") } /** A Run is a single execution of the compiler on a sets of units */ class Run { - var isDefined = false + /** To be initialized from firstPhase. */ + private var terminalPhase: Phase = NoPhase + + /** Whether compilation should stop at or skip the phase with given name. */ + protected def stopPhase(name: String) = settings.stop contains name + protected def skipPhase(name: String) = settings.skip contains name + /** As definitions.init requires phase != NoPhase, and calling phaseDescriptors.head + * will force init, there is some jockeying herein regarding init order: instead of + * taking the head descriptor we create a parser phase directly. + */ private val firstPhase = { - // ----------- Initialization code ------------------------- + /** Initialization. */ 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 + /** Set phase to a newly created syntaxAnalyzer and call definitions.init. */ + val parserPhase: Phase = syntaxAnalyzer.newPhase(NoPhase) + phase = parserPhase + definitions.init - // Reset the cache in terminal, the chain could have been build before where nobody used it - // This happens in the interpreter + // Flush the cache in the terminal phase: the chain could have been built + // before without being used. (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) + // Each subcomponent supplies a phase, which are chained together. + // If -Ystop:phase is given, neither that phase nor any beyond it is added. + // If -Yskip:phase is given, that phase will be skipped. + val lastPhase = phaseDescriptors.tail . + takeWhile (pd => !stopPhase(pd.phaseName)) . + filterNot (pd => skipPhase(pd.phaseName)) . + foldLeft (parserPhase) ((chain, ph) => ph newPhase chain) - // 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) + // Ensure there is a terminal phase at the end, since -Ystop may have limited the phases. + terminalPhase = + if (lastPhase.name == "terminal") lastPhase + else terminal newPhase lastPhase - phase1 + parserPhase } // --------------- Miscellania ------------------------------- @@ -626,19 +663,18 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable /** 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 ------------------------- + /** Counts for certain classes of warnings during this run. */ + var deprecationWarnings: Int = 0 + var uncheckedWarnings: Int = 0 + /** Progress tracking. Measured in "progress units" which are 1 per + * compilation unit per phase completed. + * + * @param current number of "progress units" completed + * @param total total number of "progress units" in run + */ 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) */ @@ -654,10 +690,14 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable 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 + + def cancel { reporter.cancelled = true } + + private var phasec: Int = 0 // phases completed + private var unitc: Int = 0 // units completed this phase + private def currentProgress = (phasec * size) + unitc + private def totalProgress = (phaseDescriptors.size - 1) * size // -1: drops terminal phase + private def refreshProgress = if (size > 0) progress(currentProgress, totalProgress) // ----- finding phases -------------------------------------------- @@ -667,34 +707,40 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable 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") + val parserPhase = phaseNamed("parser") + val namerPhase = phaseNamed("namer") + // packageobjects + val typerPhase = phaseNamed("typer") + // superaccessors + val picklerPhase = phaseNamed("pickler") + val refchecksPhase = phaseNamed("refchecks") + val uncurryPhase = phaseNamed("uncurry") + // tailcalls, specialize + val explicitouterPhase = phaseNamed("explicitouter") + val erasurePhase = phaseNamed("erasure") + // lazyvals, lambdalift, constructors + val flattenPhase = phaseNamed("flatten") + val mixinPhase = phaseNamed("mixin") + val cleanupPhase = phaseNamed("cleanup") + val icodePhase = phaseNamed("icode") + // inliner, closelim, dce + val jvmPhase = phaseNamed("jvm") + + def runIsAt(ph: Phase) = globalPhase.id == ph.id + def runIsPast(ph: Phase) = globalPhase.id > ph.id 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] + private val unitbuf = new mutable.ListBuffer[CompilationUnit] + val compiledFiles = new mutable.HashSet[String] private var _unitbufSize = 0 - def unitbufSize = _unitbufSize + def size = _unitbufSize /** add unit to be compiled in this run */ private def addUnit(unit: CompilationUnit) { -// unit.parseSettings() unitbuf += unit _unitbufSize += 1 // counting as they're added so size is cheap compiledFiles += unit.source.file.path @@ -704,10 +750,10 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable def units: Iterator[CompilationUnit] = unitbuf.iterator /** A map from compiled top-level symbols to their source files */ - val symSource = new HashMap[Symbol, AbstractFile] + val symSource = new mutable.HashMap[Symbol, AbstractFile] /** A map from compiled top-level symbols to their picklers */ - val symData = new HashMap[Symbol, PickleBuffer] + val symData = new mutable.HashMap[Symbol, PickleBuffer] /** does this run compile given class, module, or case factory? */ def compiles(sym: Symbol): Boolean = @@ -725,85 +771,92 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable // --------------- Compilation methods ---------------------------- + protected def runCheckers() { + val toCheck = globalPhase.prev + val canCheck = toCheck.checkable + val fmt = if (canCheck) "[Now checking: %s]" else "[Not checkable: %s]" + + inform(fmt format toCheck.name) + + if (canCheck) { + phase = globalPhase + if (globalPhase.id >= icodePhase.id) icodeChecker.checkICodes + else treeChecker.checkTrees + } + } + /** 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) + val depSources = dependencyAnalysis.calculateFiles(_sources.distinct) // bug #1268, scalac confused by duplicated filenames + val sources = coreClassesFirst(depSources) if (reporter.hasErrors) // there is a problem already, e.g. a plugin was passed a bad option return + val startTime = currentTime reporter.reset for (source <- sources) addUnit(new CompilationUnit(source)) globalPhase = firstPhase - while (globalPhase != terminal.newPhase(NoPhase) && !reporter.hasErrors) { + while (globalPhase != terminalPhase && !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() + + // write icode to *.icode files + if (opt.writeICode && (runIsAt(icodePhase) || opt.printPhase && runIsPast(icodePhase))) + writeICode() + + // print trees + if (opt.printPhase || opt.printLate && runIsAt(cleanupPhase)) + if (opt.showTrees) nodePrinters.printAll() else printAllUnits() - if (settings.printLate.value && globalPhase.name == "cleanup") - printAllUnits() - if (settings.browse contains globalPhase.name) treeBrowser.browse(units) + // browse trees with swing tree viewer + if (opt.browsePhase) + treeBrowser browse units + + // progress update 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 treeChecker.checkTrees - } - else inform("[Not checkable: " + globalPhase.prev.name + "]") - } - if (settings.Ystatistics.value) statistics.print(phase) + // run tree/icode checkers + if (opt.checkPhase) + runCheckers() + + // output collected statistics + if (opt.printStats) + 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) + // print class and object definitions + opt.showClass foreach (x => showDef(newTypeName(x))) + opt.showObject foreach (x => showDef(newTermName(x))) if (reporter.hasErrors) { for ((sym, file) <- symSource.iterator) { sym.reset(new loaders.SourcefileLoader(file)) - if (sym.isTerm) sym.moduleClass.reset(loaders.moduleClassLoader) + if (sym.isTerm) + sym.moduleClass reset loaders.moduleClassLoader } } else { - 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") - } + def warn(count: Int, what: String, option: Settings#BooleanSetting) = ( + if (option.isDefault && count > 0) + warning("there were %d %s warnings; re-run with %s for details".format(count, what, option.name)) + ) + warn(deprecationWarnings, "deprecation", settings.deprecation) + warn(uncheckedWarnings, "unchecked", settings.unchecked) + // todo: migrationWarnings } + symSource.keys foreach (x => resetPackageClass(x.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) - } - } + // record dependency data + if (!dependencyAnalysis.off) + dependencyAnalysis.saveDependencyAnalysis() } /** Compile list of abstract files */ @@ -828,31 +881,28 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable * 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)) { + if (!compiledFiles(file.path)) compileLate(new CompilationUnit(getSourceFile(file))) - } } - /** Compile abstract file until `globalPhase`, but at least - * to phase "namer". + /** 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)) + def stop(ph: Phase) = ph == null || ph.id >= (globalPhase.id max typerPhase.id) + def loop(ph: Phase) { + if (stop(ph)) refreshProgress + else { + reporter.withSource(unit.source) { + atPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit) + } + loop(ph.next match { + case `ph` => null // ph == ph.next implies terminal, and null ends processing + case x => x + }) } - val newLocalPhase = localPhase.next.asInstanceOf[GlobalPhase] - localPhase = if (localPhase == newLocalPhase) null else newLocalPhase } - refreshProgress + addUnit(unit) + loop(firstPhase) } /** @@ -920,40 +970,43 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable def printAllUnits() { print("[[syntax trees at end of " + phase + "]]") - atPhase(phase.next) { currentRun.units foreach (treePrinter print _) } + atPhase(phase.next) { currentRun.units foreach treePrinter.print } } - 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) - } + def showDef(name: Name) { + val segments = name.toString split '.' + val container = segments.init.foldLeft(definitions.RootClass: Symbol)(_.info member _) + val target = if (name.isTypeName) newTypeName(segments.last) else newTermName(segments.last) + + // If the name as given resolves to an existing symbol, show that. + // Otherwise we'll show every symbol in the current run with a matching name. + val syms = (container.info member target) match { + case NoSymbol => currentRun.symSource.keys filter (_.name == name) toList + case sym => List(sym) + } + + syms foreach { sym => + val name = "\n<<-- " + sym.kindString + " " + sym.fullName + " -->>\n" + val baseClasses = sym.info.baseClasses map (_.fullName) mkString ("Base classes:\n ", "\n ", "\n") + val members = atPhase(currentRun.typerPhase.next)(sym.info.members map (_.defString)) + val memberStr = members.mkString("Members after phase typer:\n ", "\n ", "\n") + + inform(List(name, baseClasses, memberStr) mkString "\n") } - 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) + val outDir = Path( + settings.outputDirs.outputDirFor(clazz.sourceFile).path match { + case "" => "." + case path => path + } + ) + val segments = clazz.fullName split '.' + val dir = segments.init.foldLeft(outDir)(_ / _).createDirectory() + + new File(dir.path, segments.last + suffix) } private def writeICode() { @@ -970,14 +1023,14 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable informProgress("wrote " + file) } catch { case ex: IOException => - if (settings.debug.value) ex.printStackTrace() + if (opt.debug) ex.printStackTrace() error("could not write file " + file) } }) } - def forJVM : Boolean = settings.target.value startsWith "jvm" - def forMSIL: Boolean = settings.target.value == "msil" + def forJVM = opt.jvm + def forMSIL = opt.msil def onlyPresentation = false - def createJavadoc = false + def createJavadoc = false } diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala index c4365a82ac..ec367b61bb 100644 --- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala @@ -18,7 +18,10 @@ trait JavaPlatform extends Platform[AbstractFile] { lazy val classPath = new PathResolver(settings).result def rootLoader = new loaders.JavaPackageLoader(classPath) - private def depAnalysisPhase = if (settings.make.value != "all") List(dependencyAnalysis) else Nil + private def depAnalysisPhase = + if (settings.make.isDefault) Nil + else List(dependencyAnalysis) + def platformPhases = List( flatten, // get rid of inner classes liftcode, // generate reified trees diff --git a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala index 30acdff594..e9cc191167 100644 --- a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala +++ b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala @@ -1,7 +1,8 @@ package scala.tools.nsc package dependencies + import util.SourceFile -import io.AbstractFile +import io.{ AbstractFile, Path } import collection._ import symtab.Flags @@ -10,18 +11,35 @@ trait DependencyAnalysis extends SubComponent with Files { val phaseName = "dependencyAnalysis" - def off = settings.make.value == "all" + def off = settings.make.isDefault + def shouldCheckClasspath = settings.make.value != "transitivenocp" def newPhase(prev: Phase) = new AnalysisPhase(prev) + private def depPath = Path(settings.dependenciesFile.value) + def loadDependencyAnalysis(): Boolean = ( + depPath.path != "none" && depPath.isFile && loadFrom( + AbstractFile.getFile(depPath), + path => AbstractFile.getFile(depPath.parent resolve Path(path)) + ) + ) + def saveDependencyAnalysis(): Unit = { + if (!depPath.exists) + dependenciesFile = AbstractFile.getFile(depPath.createFile()) + + /** The directory where file lookup should start */ + val rootPath = depPath.parent.normalize + saveDependencies( + file => rootPath.relativize(Path(file.file).normalize).path + ) + } + lazy val maxDepth = settings.make.value match { case "changed" => 0 case "transitive" | "transitivenocp" => Int.MaxValue case "immediate" => 1 } - def shouldCheckClasspath = settings.make.value != "transitivenocp" - // todo: order insensible checking and, also checking timestamp? def validateClasspath(cp1: String, cp2: String): Boolean = cp1 == cp2 @@ -87,7 +105,7 @@ trait DependencyAnalysis extends SubComponent with Files { } } - def filter(files: List[SourceFile]): List[SourceFile] = + def calculateFiles(files: List[SourceFile]): List[SourceFile] = if (off) files else if (dependencies.isEmpty) { println("No known dependencies. Compiling " + diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 71b4f6d7bd..7156da076f 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -77,7 +77,7 @@ trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings { val Xshowcls = StringSetting ("-Xshow-class", "class", "Show class info", "") val Xshowobj = StringSetting ("-Xshow-object", "object", "Show object info", "") val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases") - val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files", "scala.tools.nsc.io.SourceReader") + val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files", "") val Xwarnfatal = BooleanSetting ("-Xfatal-warnings", "Fail the compilation if there are any warnings.") val Xwarninit = BooleanSetting ("-Xwarninit", "Warn about possible changes in initialization semantics") diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 6fab87e9f6..2acfbaf098 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -1093,22 +1093,32 @@ abstract class Pickler extends SubComponent { writeNat(MajorVersion) writeNat(MinorVersion) writeNat(ep) - if (showSig) { - println("Pickled info for "+rootName+" V"+MajorVersion+"."+MinorVersion) - } + def matchesRoot(name: String) = ( + rootName.toString == ( + if (name contains '.') name.split('.').last + else name + ) + ) + val showClass = opt.showClass exists matchesRoot + def versionString = "V" + MajorVersion + "." + MinorVersion + + if (showSig) + println("Pickled info for " + rootName + " in " + rootOwner.fullName + " " + versionString) + for (i <- 0 until ep) { - if (showSig/* || rootName.toString == "StaticCompletion"*/) { + if (showSig) { print((i formatted "%3d: ")+(writeIndex formatted "%5d: ")) printEntry(entries(i)) } writeEntry(entries(i)) } - if (settings.Xshowcls.value == rootName.toString) { + + if (showClass) { readIndex = 0 ShowPickled.printFile(this, Console.out) } } - override def toString() = "" + rootName + " in " + rootOwner + override def toString = "" + rootName + " in " + rootOwner } } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 9a436d86a3..906297a4b8 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -809,7 +809,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast. val bridgesScope = new Scope val bridgeTarget = new mutable.HashMap[Symbol, Symbol] var bridges: List[Tree] = List() - val opc = atPhase(currentRun.explicitOuterPhase) { + val opc = atPhase(currentRun.explicitouterPhase) { new overridingPairs.Cursor(owner) { override def parents: List[Type] = List(owner.info.parents.head) override def exclude(sym: Symbol): Boolean = @@ -820,7 +820,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast. val member = opc.overriding val other = opc.overridden //Console.println("bridge? " + member + ":" + member.tpe + member.locationString + " to " + other + ":" + other.tpe + other.locationString);//DEBUG - if (atPhase(currentRun.explicitOuterPhase)(!member.isDeferred)) { + if (atPhase(currentRun.explicitouterPhase)(!member.isDeferred)) { val otpe = erasure(other.tpe); val bridgeNeeded = atPhase(phase.next) ( !(other.tpe =:= member.tpe) && diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala index e4da90ee28..202fd63ef9 100644 --- a/src/library/scala/util/Properties.scala +++ b/src/library/scala/util/Properties.scala @@ -69,6 +69,7 @@ private[scala] trait PropertiesTrait * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. */ def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") /** This is the default text encoding, overridden (unreliably) with * JAVA_OPTS="-Dfile.encoding=Foo" |