diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/Global.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/Global.scala | 510 |
1 files changed, 193 insertions, 317 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index a54b92cef8..56ad4738d9 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -7,17 +7,17 @@ package scala package tools package nsc -import java.io.{ File, FileOutputStream, PrintWriter, IOException, FileNotFoundException } +import java.io.{File, FileNotFoundException, IOException} import java.net.URL -import java.nio.charset.{ Charset, CharsetDecoder, IllegalCharsetNameException, UnsupportedCharsetException } -import scala.collection.{ mutable, immutable } -import io.{ SourceReader, AbstractFile, Path } -import reporters.{ Reporter, ConsoleReporter } -import util.{ ClassFileLookup, ClassPath, MergedClassPath, StatisticsInfo, returning } +import java.nio.charset.{Charset, CharsetDecoder, IllegalCharsetNameException, UnsupportedCharsetException} +import scala.collection.{immutable, mutable} +import io.{AbstractFile, Path, SourceReader} +import reporters.Reporter +import util.{ClassPath, StatisticsInfo, returning} import scala.reflect.ClassTag -import scala.reflect.internal.util.{ SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile } +import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, ScalaClassLoader, ScriptSourceFile, SourceFile} import scala.reflect.internal.pickling.PickleBuffer -import symtab.{ Flags, SymbolTable, SymbolTrackers } +import symtab.{Flags, SymbolTable, SymbolTrackers} import symtab.classfile.Pickler import plugins.Plugins import ast._ @@ -25,16 +25,11 @@ import ast.parser._ import typechecker._ import transform.patmat.PatternMatching import transform._ -import backend.icode.{ ICodes, GenICode, ICodeCheckers } -import backend.{ ScalaPrimitives, JavaPlatform } +import backend.{JavaPlatform, ScalaPrimitives} import backend.jvm.GenBCode -import backend.jvm.GenASM -import backend.opt.{ Inliners, InlineExceptionHandlers, ConstantOptimization, ClosureElimination, DeadCodeElimination } -import backend.icode.analysis._ import scala.language.postfixOps import scala.tools.nsc.ast.{TreeGen => AstTreeGen} -import scala.tools.nsc.classpath.FlatClassPath -import scala.tools.nsc.settings.ClassPathRepresentationType +import scala.tools.nsc.classpath._ class Global(var currentSettings: Settings, var reporter: Reporter) extends SymbolTable @@ -58,12 +53,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) class GlobalMirror extends Roots(NoSymbol) { val universe: self.type = self - def rootLoader: LazyType = { - settings.YclasspathImpl.value match { - case ClassPathRepresentationType.Flat => new loaders.PackageLoaderUsingFlatClassPath(FlatClassPath.RootPackage, flatClassPath) - case ClassPathRepresentationType.Recursive => new loaders.PackageLoader(recursiveClassPath) - } - } + def rootLoader: LazyType = new loaders.PackageLoader(ClassPath.RootPackage, classPath) override def toString = "compiler mirror" } implicit val MirrorTag: ClassTag[Mirror] = ClassTag[Mirror](classOf[GlobalMirror]) @@ -90,7 +80,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) this(new Settings(err => reporter.error(null, err)), reporter) def this(settings: Settings) = - this(settings, new ConsoleReporter(settings)) + this(settings, Global.reporter(settings)) def picklerPhase: Phase = if (currentRun.isDefined) currentRun.picklerPhase else NoPhase @@ -100,9 +90,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) * to the last tree to typer, whose position is the trigger of stub errors. */ override def newStubSymbol(owner: Symbol, name: Name, - missingMessage: String, - isPackage: Boolean = false): Symbol = { - val stubSymbol = super.newStubSymbol(owner, name, missingMessage, isPackage) + missingMessage: String): Symbol = { + val stubSymbol = super.newStubSymbol(owner, name, missingMessage) val stubErrorPosition = { val lastTreeToTyper = analyzer.lastTreeToTyper if (lastTreeToTyper != EmptyTree) lastTreeToTyper.pos else stubSymbol.pos @@ -119,18 +108,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter) type ThisPlatform = JavaPlatform { val global: Global.this.type } lazy val platform: ThisPlatform = new GlobalPlatform + /* A hook for the REPL to add a classpath entry containing products of previous runs to inliner's bytecode repository*/ + // Fixes SI-8779 + def optimizerClassPath(base: ClassPath): ClassPath = base - type PlatformClassPath = ClassPath[AbstractFile] - type OptClassPath = Option[PlatformClassPath] - - def classPath: ClassFileLookup[AbstractFile] = settings.YclasspathImpl.value match { - case ClassPathRepresentationType.Flat => flatClassPath - case ClassPathRepresentationType.Recursive => recursiveClassPath - } - - private def recursiveClassPath: ClassPath[AbstractFile] = platform.classPath - - private def flatClassPath: FlatClassPath = platform.flatClassPath + def classPath: ClassPath = platform.classPath // sub-components -------------------------------------------------- @@ -154,12 +136,12 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val global: Global.this.type = Global.this } with ConstantFolder - /** ICode generator */ - object icodes extends { - val global: Global.this.type = Global.this - } with ICodes + /** For sbt compatibility (https://github.com/scala/scala/pull/4588) */ + object icodes { + class IClass(val symbol: Symbol) + } - /** Scala primitives, used in genicode */ + /** Scala primitives, used the backend */ object scalaPrimitives extends { val global: Global.this.type = Global.this } with ScalaPrimitives @@ -171,18 +153,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) type SymbolPair = overridingPairs.SymbolPair - // Optimizer components - - /** 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 - // Components for collecting and generating output /** Some statistics (normally disabled) set with -Ystatistics */ @@ -329,7 +299,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // Over 200 closure objects are eliminated by inlining this. @inline final def log(msg: => AnyRef) { if (shouldLogAtThisPhase) - inform("[log %s%s] %s".format(globalPhase, atPhaseStackMessage, msg)) + inform(s"[log $globalPhase$atPhaseStackMessage] $msg") } @inline final override def debuglog(msg: => String) { @@ -351,10 +321,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) try Some(Charset.forName(name)) catch { case _: IllegalCharsetNameException => - globalError("illegal charset name '" + name + "'") + globalError(s"illegal charset name '$name'") None case _: UnsupportedCharsetException => - globalError("unsupported charset '" + name + "'") + globalError(s"unsupported charset '$name'") None } @@ -384,14 +354,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) s"[search path for class files: ${classPath.asClassPathString}]" ) - // The current division between scala.reflect.* and scala.tools.nsc.* is pretty - // clunky. It is often difficult to have a setting influence something without having - // to create it on that side. For this one my strategy is a constant def at the file - // where I need it, and then an override in Global with the setting. - override protected val etaExpandKeepsStar = settings.etaExpandKeepsStar.value - // Here comes another one... - override protected val enableTypeVarExperimentals = settings.Xexperimental.value - def getSourceFile(f: AbstractFile): BatchSourceFile = new BatchSourceFile(f, reader read f) def getSourceFile(name: String): SourceFile = { @@ -427,15 +389,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) 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 isSpecialized = prev.name == "specialize" || prev.specialized - override def specialized: Boolean = isSpecialized - private val isRefChecked = prev.name == "refchecks" || prev.refChecked - override def refChecked: Boolean = isRefChecked - /** Is current phase cancelled on this unit? */ def cancelled(unit: CompilationUnit) = { // run the typer only if in `createJavadoc` mode @@ -449,15 +402,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter) if (settings.debug && (settings.verbose || currentRun.size < 5)) inform("[running phase " + name + " on " + unit + "]") + if (!cancelled(unit)) { + currentRun.informUnitStarting(this, unit) + try withCurrentUnitNoLog(unit)(task) + finally currentRun.advanceUnit() + } + } + final def withCurrentUnitNoLog(unit: CompilationUnit)(task: => Unit) { val unit0 = currentUnit try { currentRun.currentUnit = unit - if (!cancelled(unit)) { - currentRun.informUnitStarting(this, unit) - task - } - currentRun.advanceUnit() + task } finally { //assert(currentUnit == unit) currentRun.currentUnit = unit0 @@ -476,7 +432,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) override val initial = true } - import syntaxAnalyzer.{ UnitScanner, UnitParser } + import syntaxAnalyzer.{ UnitScanner, UnitParser, JavaUnitParser } // !!! I think we're overdue for all these phase objects being lazy vals. // There's no way for a Global subclass to provide a custom typer @@ -521,7 +477,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } with Pickler // phaseName = "refchecks" - override object refChecks extends { + object refChecks extends { val global: Global.this.type = Global.this val runsAfter = List("pickler") val runsRightAfter = None @@ -541,10 +497,22 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val runsRightAfter = None } with TailCalls + // phaseName = "fields" + object fields extends { + val global: Global.this.type = Global.this + // after refchecks, so it doesn't have to make weird exceptions for synthetic accessors + // after uncurry as it produces more work for the fields phase as well as being confused by it: + // - sam expansion synthesizes classes, which may need trait fields mixed in + // - the fields phase adds synthetic abstract methods to traits that should not disqualify them from being a SAM type + // before erasure: correct signatures & bridges for accessors + val runsAfter = List("uncurry") + val runsRightAfter = None + } with Fields + // phaseName = "explicitouter" object explicitOuter extends { val global: Global.this.type = Global.this - val runsAfter = List("tailcalls") + val runsAfter = List("fields") val runsRightAfter = None } with ExplicitOuter @@ -569,17 +537,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val runsRightAfter = Some("erasure") } with PostErasure - // phaseName = "lazyvals" - object lazyVals extends { - val global: Global.this.type = Global.this - val runsAfter = List("erasure") - val runsRightAfter = None - } with LazyVals // phaseName = "lambdalift" object lambdaLift extends { val global: Global.this.type = Global.this - val runsAfter = List("lazyvals") + val runsAfter = List("erasure") val runsRightAfter = None } with LambdaLift @@ -618,59 +580,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val runsRightAfter = None } with Delambdafy - // phaseName = "icode" - object genicode extends { - val global: Global.this.type = Global.this - val runsAfter = List("cleanup") - val runsRightAfter = None - } with GenICode - - // phaseName = "inliner" - object inliner extends { - val global: Global.this.type = Global.this - val runsAfter = List("icode") - val runsRightAfter = None - } with Inliners - - // phaseName = "inlinehandlers" - object inlineExceptionHandlers extends { - val global: Global.this.type = Global.this - val runsAfter = List("inliner") - val runsRightAfter = None - } with InlineExceptionHandlers - - // phaseName = "closelim" - object closureElimination extends { - val global: Global.this.type = Global.this - val runsAfter = List("inlinehandlers") - val runsRightAfter = None - } with ClosureElimination - - // phaseName = "constopt" - object constantOptimization extends { - val global: Global.this.type = Global.this - val runsAfter = List("closelim") - val runsRightAfter = None - } with ConstantOptimization - - // phaseName = "dce" - object deadCode extends { - val global: Global.this.type = Global.this - val runsAfter = List("closelim") - val runsRightAfter = None - } with DeadCodeElimination - - // phaseName = "jvm", ASM-based version - object genASM extends { - val global: Global.this.type = Global.this - val runsAfter = List("dce") - val runsRightAfter = None - } with GenASM - // phaseName = "bcode" object genBCode extends { val global: Global.this.type = Global.this - val runsAfter = List("dce") + val runsAfter = List("cleanup") val runsRightAfter = None } with GenBCode @@ -701,13 +614,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val global: Global.this.type = Global.this } with TreeCheckers - /** Icode verification */ - object icodeCheckers extends { - val global: Global.this.type = Global.this - } with ICodeCheckers - - object icodeChecker extends icodeCheckers.ICodeChecker() - object typer extends analyzer.Typer( analyzer.NoContext.make(EmptyTree, RootClass, newScope) ) @@ -716,7 +622,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) * This implementation creates a description map at the same time. */ protected def computeInternalPhases(): Unit = { - // Note: this fits -Xshow-phases into 80 column width, which it is + // Note: this fits -Xshow-phases into 80 column width, which is // desirable to preserve. val phs = List( syntaxAnalyzer -> "parse source into ASTs, perform simple desugaring", @@ -729,23 +635,17 @@ class Global(var currentSettings: Settings, var reporter: Reporter) pickler -> "serialize symbol tables", refChecks -> "reference/override checking, translate nested objects", uncurry -> "uncurry, translate function values to anonymous classes", + fields -> "synthesize accessors and fields, add bitmaps for lazy vals", tailCalls -> "replace tail calls by jumps", specializeTypes -> "@specialized-driven class and method specialization", explicitOuter -> "this refs to outer pointers", erasure -> "erase types, add interfaces for traits", postErasure -> "clean up erased inline classes", - 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", delambdafy -> "remove lambdas", cleanup -> "platform-specific cleanups, generate reflective calls", - genicode -> "generate portable intermediate code", - inliner -> "optimization: do inlining", - inlineExceptionHandlers -> "optimization: inline exception handlers", - closureElimination -> "optimization: eliminate uncalled closures", - constantOptimization -> "optimization: optimize null and other constants", - deadCode -> "optimization: eliminate dead code", terminal -> "the last phase during a compilation run" ) @@ -797,7 +697,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } /** A description of the phases that will run in this configuration, or all if -Ydebug. */ - def phaseDescriptions: String = phaseHelp("description", elliptically = true, phasesDescMap) + def phaseDescriptions: String = phaseHelp("description", elliptically = !settings.debug, phasesDescMap) /** Summary of the per-phase values of nextFlags and newFlags, shown under -Xshow-phases -Ydebug. */ def phaseFlagDescriptions: String = { @@ -808,7 +708,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) else if (ph.phaseNewFlags != 0L && ph.phaseNextFlags != 0L) fstr1 + " " + fstr2 else fstr1 + fstr2 } - phaseHelp("new flags", elliptically = false, fmt) + phaseHelp("new flags", elliptically = !settings.debug, fmt) } /** Emit a verbose phase table. @@ -820,7 +720,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) * @param elliptically whether to truncate the description with an ellipsis (...) * @param describe how to describe a component */ - def phaseHelp(title: String, elliptically: Boolean, describe: SubComponent => String) = { + private def phaseHelp(title: String, elliptically: Boolean, describe: SubComponent => String): String = { val Limit = 16 // phase names should not be absurdly long val MaxCol = 80 // because some of us edit on green screens val maxName = phaseNames map (_.length) max @@ -835,13 +735,13 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // built-in string precision merely truncates import java.util.{ Formattable, FormattableFlags, Formatter } def dotfmt(s: String) = new Formattable { - def elliptically(s: String, max: Int) = ( + def foreshortened(s: String, max: Int) = ( if (max < 0 || s.length <= max) s else if (max < 4) s.take(max) else s.take(max - 3) + "..." ) override def formatTo(formatter: Formatter, flags: Int, width: Int, precision: Int) { - val p = elliptically(s, precision) + val p = foreshortened(s, precision) val w = if (width > 0 && p.length < width) { import FormattableFlags.LEFT_JUSTIFY val leftly = (flags & LEFT_JUSTIFY) == LEFT_JUSTIFY @@ -867,7 +767,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) else (p.phaseName, describe(p)) fmt.format(name, idOf(p), text) } - line1 :: line2 :: (phaseDescriptors map mkText) mkString + (line1 :: line2 :: (phaseDescriptors map mkText)).mkString } /** Returns List of (phase, value) pairs, including only those @@ -885,12 +785,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Extend classpath of `platform` and rescan updated packages. */ def extendCompilerClassPath(urls: URL*): Unit = { - if (settings.YclasspathImpl.value == ClassPathRepresentationType.Flat) - throw new UnsupportedOperationException("Flat classpath doesn't support extending the compiler classpath") - - val newClassPath = platform.classPath.mergeUrlsIntoClassPath(urls: _*) + val urlClasspaths = urls.map(u => ClassPathFactory.newClassPath(AbstractFile.getURL(u), settings)) + val newClassPath = AggregateClassPath.createAggregate(platform.classPath +: urlClasspaths : _*) platform.currentClassPath = Some(newClassPath) - // Reload all specified jars into this compiler instance invalidateClassPathEntries(urls.map(_.getPath): _*) } @@ -923,43 +820,54 @@ class Global(var currentSettings: Settings, var reporter: Reporter) * entries on the classpath. */ def invalidateClassPathEntries(paths: String*): Unit = { - if (settings.YclasspathImpl.value == ClassPathRepresentationType.Flat) - throw new UnsupportedOperationException("Flat classpath doesn't support the classpath invalidation") - - implicit object ClassPathOrdering extends Ordering[PlatformClassPath] { - def compare(a:PlatformClassPath, b:PlatformClassPath) = a.asClassPathString compare b.asClassPathString + implicit object ClassPathOrdering extends Ordering[ClassPath] { + def compare(a: ClassPath, b: ClassPath): Int = a.asClassPathString compareTo b.asClassPathString } val invalidated, failed = new mutable.ListBuffer[ClassSymbol] - classPath match { - case cp: MergedClassPath[_] => - def assoc(path: String): List[(PlatformClassPath, PlatformClassPath)] = { - val dir = AbstractFile.getDirectory(path) - val canonical = dir.canonicalPath - def matchesCanonical(e: ClassPath[_]) = e.origin match { - case Some(opath) => - AbstractFile.getDirectory(opath).canonicalPath == canonical - case None => - false - } - cp.entries find matchesCanonical match { - case Some(oldEntry) => - List(oldEntry -> cp.context.newClassPath(dir)) - case None => - error(s"Error adding entry to classpath. During invalidation, no entry named $path in classpath $classPath") - List() - } - } - val subst = immutable.TreeMap(paths flatMap assoc: _*) - if (subst.nonEmpty) { - platform updateClassPath subst - informProgress(s"classpath updated on entries [${subst.keys mkString ","}]") - def mkClassPath(elems: Iterable[PlatformClassPath]): PlatformClassPath = - if (elems.size == 1) elems.head - else new MergedClassPath(elems, recursiveClassPath.context) - val oldEntries = mkClassPath(subst.keys) - val newEntries = mkClassPath(subst.values) - mergeNewEntries(newEntries, RootClass, Some(recursiveClassPath), Some(oldEntries), invalidated, failed) - } + + def assoc(path: String): Option[(ClassPath, ClassPath)] = { + def origin(lookup: ClassPath): Option[String] = lookup match { + case cp: JFileDirectoryLookup[_] => Some(cp.dir.getPath) + case cp: ZipArchiveFileLookup[_] => Some(cp.zipFile.getPath) + case _ => None + } + + def entries(lookup: ClassPath): Seq[ClassPath] = lookup match { + case cp: AggregateClassPath => cp.aggregates + case cp: ClassPath => Seq(cp) + } + + val dir = AbstractFile.getDirectory(path) // if path is a `jar`, this is a FileZipArchive (isDirectory is true) + val canonical = dir.canonicalPath // this is the canonical path of the .jar + def matchesCanonical(e: ClassPath) = origin(e) match { + case Some(opath) => + AbstractFile.getDirectory(opath).canonicalPath == canonical + case None => + false + } + entries(classPath) find matchesCanonical match { + case Some(oldEntry) => + Some(oldEntry -> ClassPathFactory.newClassPath(dir, settings)) + case None => + error(s"Error adding entry to classpath. During invalidation, no entry named $path in classpath $classPath") + None + } + } + val subst = immutable.TreeMap(paths flatMap assoc: _*) + if (subst.nonEmpty) { + platform updateClassPath subst + informProgress(s"classpath updated on entries [${subst.keys mkString ","}]") + def mkClassPath(elems: Iterable[ClassPath]): ClassPath = + if (elems.size == 1) elems.head + else AggregateClassPath.createAggregate(elems.toSeq: _*) + val oldEntries = mkClassPath(subst.keys) + val newEntries = mkClassPath(subst.values) + classPath match { + case cp: ClassPath => mergeNewEntries( + RootClass, "", + oldEntries, newEntries, cp, + invalidated, failed) + } } def show(msg: String, syms: scala.collection.Traversable[Symbol]) = if (syms.nonEmpty) @@ -968,66 +876,61 @@ class Global(var currentSettings: Settings, var reporter: Reporter) show("could not invalidate system packages", failed) } - /** Merges new classpath entries into the symbol table + /** + * Merges new classpath entries into the symbol table * - * @param newEntries The new classpath entries - * @param root The root symbol to be resynced (a package class) - * @param allEntries Optionally, the corresponding package in the complete current classpath - * @param oldEntries Optionally, the corresponding package in the old classpath entries - * @param invalidated A listbuffer collecting the invalidated package classes - * @param failed A listbuffer collecting system package classes which could not be invalidated + * @param packageClass The ClassSymbol for the package being updated + * @param fullPackageName The full name of the package being updated + * @param oldEntries The classpath that was removed, it is no longer part of fullClasspath + * @param newEntries The classpath that was added, it is already part of fullClasspath + * @param fullClasspath The full classpath, equivalent to global.classPath + * @param invalidated A ListBuffer collecting the invalidated package classes + * @param failed A ListBuffer collecting system package classes which could not be invalidated * - * The merging strategy is determined by the absence or presence of classes and packages. + * If either oldEntries or newEntries contains classes in the current package, the package symbol + * is re-initialized to a fresh package loader, provided that a corresponding package exists in + * fullClasspath. Otherwise it is removed. * - * If either oldEntries or newEntries contains classes, root is invalidated provided that a corresponding package - * exists in allEntries. Otherwise it is removed. - * Otherwise, the action is determined by the following matrix, with columns: - * - * old sym action - * + + recurse into all child packages of newEntries - * - + invalidate root - * - - create and enter root - * - * Here, old means classpath, and sym means symboltable. + is presence of an entry in its column, - is absence. + * Otherwise, sub-packages in newEntries are looked up in the symbol table (created if + * non-existent) and the merge function is called recursively. */ - private def mergeNewEntries(newEntries: PlatformClassPath, root: ClassSymbol, - allEntries: OptClassPath, oldEntries: OptClassPath, - invalidated: mutable.ListBuffer[ClassSymbol], failed: mutable.ListBuffer[ClassSymbol]) { - ifDebug(informProgress(s"syncing $root, $oldEntries -> $newEntries")) - - val getName: ClassPath[AbstractFile] => String = (_.name) - def hasClasses(cp: OptClassPath) = cp.isDefined && cp.get.classes.nonEmpty - def invalidateOrRemove(root: ClassSymbol) = { - allEntries match { - case Some(cp) => root setInfo new loaders.PackageLoader(cp) - case None => root.owner.info.decls unlink root.sourceModule - } - invalidated += root + private def mergeNewEntries(packageClass: ClassSymbol, fullPackageName: String, + oldEntries: ClassPath, newEntries: ClassPath, fullClasspath: ClassPath, + invalidated: mutable.ListBuffer[ClassSymbol], failed: mutable.ListBuffer[ClassSymbol]): Unit = { + ifDebug(informProgress(s"syncing $packageClass, $oldEntries -> $newEntries")) + + def packageExists(cp: ClassPath): Boolean = { + val (parent, _) = PackageNameUtils.separatePkgAndClassNames(fullPackageName) + cp.packages(parent).exists(_.name == fullPackageName) } - def subPackage(cp: PlatformClassPath, name: String): OptClassPath = - cp.packages find (cp1 => getName(cp1) == name) - val classesFound = hasClasses(oldEntries) || newEntries.classes.nonEmpty - if (classesFound && !isSystemPackageClass(root)) { - invalidateOrRemove(root) - } else { - if (classesFound) { - if (root.isRoot) invalidateOrRemove(EmptyPackageClass) - else failed += root - } - if (!oldEntries.isDefined) invalidateOrRemove(root) + def invalidateOrRemove(pkg: ClassSymbol) = { + if (packageExists(fullClasspath)) + pkg setInfo new loaders.PackageLoader(fullPackageName, fullClasspath) else - for (pstr <- newEntries.packages.map(getName)) { - val pname = newTermName(pstr) - val pkg = (root.info decl pname) orElse { - // package does not exist in symbol table, create symbol to track it - assert(!subPackage(oldEntries.get, pstr).isDefined) - loaders.enterPackage(root, pstr, new loaders.PackageLoader(allEntries.get)) - } - mergeNewEntries(subPackage(newEntries, pstr).get, pkg.moduleClass.asClass, - subPackage(allEntries.get, pstr), subPackage(oldEntries.get, pstr), - invalidated, failed) + pkg.owner.info.decls unlink pkg.sourceModule + invalidated += pkg + } + + val classesFound = oldEntries.classes(fullPackageName).nonEmpty || newEntries.classes(fullPackageName).nonEmpty + if (classesFound) { + // if the package contains classes either in oldEntries or newEntries, the package is invalidated (or removed if there are no more classes in it) + if (!isSystemPackageClass(packageClass)) invalidateOrRemove(packageClass) + else if (packageClass.isRoot) invalidateOrRemove(EmptyPackageClass) + else failed += packageClass + } else { + // no new or removed classes in the current package + for (p <- newEntries.packages(fullPackageName)) { + val (_, subPackageName) = PackageNameUtils.separatePkgAndClassNames(p.name) + val subPackage = packageClass.info.decl(newTermName(subPackageName)) orElse { + // package does not exist in symbol table, create a new symbol + loaders.enterPackage(packageClass, subPackageName, new loaders.PackageLoader(p.name, fullClasspath)) } + mergeNewEntries( + subPackage.moduleClass.asClass, p.name, + oldEntries, newEntries, fullClasspath, + invalidated, failed) + } } } @@ -1063,10 +966,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter) definitions.isDefinitionsInitialized && rootMirror.isMirrorInitialized ) - override def isPastTyper = ( + override def isPastTyper = isPast(currentRun.typerPhase) + def isPast(phase: Phase) = ( (curRun ne null) && isGlobalInitialized // defense against init order issues - && (globalPhase.id > currentRun.typerPhase.id) + && (globalPhase.id > phase.id) ) // TODO - trim these to the absolute minimum. @@ -1084,9 +988,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) @inline final def enteringErasure[T](op: => T): T = enteringPhase(currentRun.erasurePhase)(op) @inline final def enteringExplicitOuter[T](op: => T): T = enteringPhase(currentRun.explicitouterPhase)(op) @inline final def enteringFlatten[T](op: => T): T = enteringPhase(currentRun.flattenPhase)(op) - @inline final def enteringIcode[T](op: => T): T = enteringPhase(currentRun.icodePhase)(op) @inline final def enteringMixin[T](op: => T): T = enteringPhase(currentRun.mixinPhase)(op) @inline final def enteringDelambdafy[T](op: => T): T = enteringPhase(currentRun.delambdafyPhase)(op) + @inline final def enteringJVM[T](op: => T): T = enteringPhase(currentRun.jvmPhase)(op) @inline final def enteringPickler[T](op: => T): T = enteringPhase(currentRun.picklerPhase)(op) @inline final def enteringSpecialize[T](op: => T): T = enteringPhase(currentRun.specializePhase)(op) @inline final def enteringTyper[T](op: => T): T = enteringPhase(currentRun.typerPhase)(op) @@ -1099,7 +1003,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) ) private def formatExplain(pairs: (String, Any)*): String = ( - pairs.toList collect { case (k, v) if v != null => "%20s: %s".format(k, v) } mkString "\n" + pairs collect { case (k, v) if v != null => f"$k%20s: $v" } mkString "\n" ) /** Don't want to introduce new errors trying to report errors, @@ -1112,9 +1016,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val site = lastSeenContext.enclClassOrMethod.owner val pos_s = if (tree.pos.isDefined) s"line ${tree.pos.line} of ${tree.pos.source.file}" else "<unknown>" val context_s = try { + import scala.reflect.io.{File => SFile} // Taking 3 before, 3 after the fingered line. - val start = 0 max (tree.pos.line - 3) - val xs = scala.reflect.io.File(tree.pos.source.file.file).lines drop start take 7 + val start = 1 max (tree.pos.line - 3) + val xs = SFile(tree.pos.source.file.file).lines.drop(start-1).take(7) val strs = xs.zipWithIndex map { case (line, idx) => f"${start + idx}%6d $line" } strs.mkString("== Source file context for tree position ==\n\n", "\n", "") } @@ -1166,6 +1071,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def newUnitParser(code: String, filename: String = "<console>"): UnitParser = newUnitParser(newCompilationUnit(code, filename)) + def newJavaUnitParser(unit: CompilationUnit): JavaUnitParser = new JavaUnitParser(unit) + /** A Run is a single execution of the compiler on a set of units. */ class Run extends RunContextApi with RunReporting with RunParsing { @@ -1178,9 +1085,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) var currentUnit: CompilationUnit = NoCompilationUnit // used in sbt - def uncheckedWarnings: List[(Position, String)] = reporting.uncheckedWarnings + def uncheckedWarnings: List[(Position, String)] = reporting.uncheckedWarnings.map{case (pos, (msg, since)) => (pos, msg)} // used in sbt - def deprecationWarnings: List[(Position, String)] = reporting.deprecationWarnings + def deprecationWarnings: List[(Position, String)] = reporting.deprecationWarnings.map{case (pos, (msg, since)) => (pos, msg)} private class SyncedCompilationBuffer { self => private val underlying = new mutable.ArrayBuffer[CompilationUnit] @@ -1305,7 +1212,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) first } - // --------------- Miscellania ------------------------------- + // --------------- Miscellanea ------------------------------- /** Progress tracking. Measured in "progress units" which are 1 per * compilation unit per phase completed. @@ -1360,35 +1267,23 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // val superaccessorsPhase = phaseNamed("superaccessors") val picklerPhase = phaseNamed("pickler") val refchecksPhase = phaseNamed("refchecks") - // val selectiveanfPhase = phaseNamed("selectiveanf") - // val selectivecpsPhase = phaseNamed("selectivecps") val uncurryPhase = phaseNamed("uncurry") + // val fieldsPhase = phaseNamed("fields") // val tailcallsPhase = phaseNamed("tailcalls") val specializePhase = phaseNamed("specialize") val explicitouterPhase = phaseNamed("explicitouter") val erasurePhase = phaseNamed("erasure") val posterasurePhase = phaseNamed("posterasure") - // val lazyvalsPhase = phaseNamed("lazyvals") val lambdaliftPhase = phaseNamed("lambdalift") // val constructorsPhase = phaseNamed("constructors") val flattenPhase = phaseNamed("flatten") val mixinPhase = phaseNamed("mixin") val delambdafyPhase = phaseNamed("delambdafy") val cleanupPhase = phaseNamed("cleanup") - val icodePhase = phaseNamed("icode") - val inlinerPhase = phaseNamed("inliner") - val inlineExceptionHandlersPhase = phaseNamed("inlinehandlers") - val closelimPhase = phaseNamed("closelim") - val dcePhase = phaseNamed("dce") - // val jvmPhase = phaseNamed("jvm") + val jvmPhase = phaseNamed("jvm") def runIsAt(ph: Phase) = globalPhase.id == ph.id - def runIsAtOptimiz = { - runIsAt(inlinerPhase) || // listing phases in full for robustness when -Ystop-after has been given. - runIsAt(inlineExceptionHandlersPhase) || - runIsAt(closelimPhase) || - runIsAt(dcePhase) - } + def runIsAtOptimiz = runIsAt(jvmPhase) isDefined = true @@ -1400,13 +1295,17 @@ class Global(var currentSettings: Settings, var reporter: Reporter) unitbuf += unit compiledFiles += unit.source.file.path } - private def checkDeprecatedSettings(unit: CompilationUnit) { + private def warnDeprecatedAndConflictingSettings(unit: CompilationUnit) { // issue warnings for any usage of deprecated settings settings.userSetSettings filter (_.isDeprecated) foreach { s => - currentRun.reporting.deprecationWarning(NoPosition, s.name + " is deprecated: " + s.deprecationMessage.get) + currentRun.reporting.deprecationWarning(NoPosition, s.name + " is deprecated: " + s.deprecationMessage.get, "") + } + val supportedTarget = "jvm-1.8" + if (settings.target.value != supportedTarget) { + currentRun.reporting.deprecationWarning(NoPosition, settings.target.name + ":" + settings.target.value + " is deprecated and has no effect, setting to " + supportedTarget, "2.12.0") + settings.target.value = supportedTarget } - if (settings.target.value.contains("jvm-1.5")) - currentRun.reporting.deprecationWarning(NoPosition, settings.target.name + ":" + settings.target.value + " is deprecated: use target for Java 1.6 or above.") + settings.conflictWarning.foreach(reporter.warning(NoPosition, _)) } /* An iterator returning all the units being compiled in this run */ @@ -1421,7 +1320,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** does this run compile given class, module, or case factory? */ // NOTE: Early initialized members temporarily typechecked before the enclosing class, see typedPrimaryConstrBody! - // Here we work around that wrinkle by claiming that a early-initialized member is compiled in + // Here we work around that wrinkle by claiming that a pre-initialized member is compiled in // *every* run. This approximation works because this method is exclusively called with `this` == `currentRun`. def compiles(sym: Symbol): Boolean = if (sym == NoSymbol) false @@ -1447,8 +1346,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) if (canCheck) { phase = globalPhase - if (globalPhase.id >= icodePhase.id) icodeChecker.checkICodes() - else treeChecker.checkTrees() + if (globalPhase.id <= cleanupPhase.id) + treeChecker.checkTrees() } } @@ -1497,7 +1396,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def compileSources(sources: List[SourceFile]) = if (!reporter.hasErrors) { def checkDeprecations() = { - checkDeprecatedSettings(newCompilationUnit("")) + warnDeprecatedAndConflictingSettings(newCompilationUnit("")) reporting.summarizeErrors() } @@ -1519,7 +1418,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val startTime = currentTime reporter.reset() - checkDeprecatedSettings(unitbuf.head) + warnDeprecatedAndConflictingSettings(unitbuf.head) globalPhase = fromPhase while (globalPhase.hasNext && !reporter.hasErrors) { @@ -1529,14 +1428,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // progress update informTime(globalPhase.description, startTime) - val shouldWriteIcode = ( - (settings.writeICode.isSetByUser && (settings.writeICode containsPhase globalPhase)) - || (!settings.Xprint.doAllPhases && (settings.Xprint containsPhase globalPhase) && runIsAtOptimiz) - ) - if (shouldWriteIcode) { - // Write *.icode files when -Xprint-icode or -Xprint:<some-optimiz-phase> was given. - writeICode() - } else if ((settings.Xprint containsPhase globalPhase) || settings.printLate && runIsAt(cleanupPhase)) { + if ((settings.Xprint containsPhase globalPhase) || settings.printLate && runIsAt(cleanupPhase)) { // print trees if (settings.Xshowtrees || settings.XshowtreesCompact || settings.XshowtreesStringified) nodePrinters.printAll() else printAllUnits() @@ -1557,7 +1449,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // move the pointer globalPhase = globalPhase.next - // run tree/icode checkers + // run tree checkers if (settings.check containsPhase globalPhase.prev) runCheckers() @@ -1701,33 +1593,17 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Returns the file with the given suffix for the given class. Used for icode writing. */ def getFile(clazz: Symbol, suffix: String): File = getFile(clazz.sourceFile, clazz.fullName split '.', suffix) - private def writeICode() { - val printer = new icodes.TextPrinter(writer = null, icodes.linearizer) - icodes.classes.values foreach { cls => - val file = { - val module = if (cls.symbol.hasModuleFlag) "$" else "" - val faze = if (settings.debug) phase.name else f"${phase.id}%02d" // avoid breaking windows build with long filename - getFile(cls.symbol, s"$module-$faze.icode") - } - - try { - val stream = new FileOutputStream(file) - printer.setWriter(new PrintWriter(stream, true)) - try - printer.printClass(cls) - finally - stream.close() - informProgress(s"wrote $file") - } catch { - case e: IOException => - if (settings.debug) e.printStackTrace() - globalError(s"could not write file $file") - } - } - } def createJavadoc = false } object Global { def apply(settings: Settings, reporter: Reporter): Global = new Global(settings, reporter) + + def apply(settings: Settings): Global = new Global(settings, reporter(settings)) + + private def reporter(settings: Settings): Reporter = { + //val loader = ScalaClassLoader(getClass.getClassLoader) // apply does not make delegate + val loader = new ClassLoader(getClass.getClassLoader) with ScalaClassLoader + loader.create[Reporter](settings.reporter.value, settings.errorFn)(settings) + } } |