diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/Global.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/Global.scala | 139 |
1 files changed, 104 insertions, 35 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 9089be5cc0..ceb75b575d 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -12,7 +12,7 @@ import scala.tools.util.PathResolver import scala.collection.{ mutable, immutable } import io.{ SourceReader, AbstractFile, Path } import reporters.{ Reporter, ConsoleReporter } -import util.{ NoPosition, Exceptional, ClassPath, SourceFile, NoSourceFile, Statistics, StatisticsInfo, BatchSourceFile, ScriptSourceFile, ScalaClassLoader, returning } +import util.{ NoPosition, Exceptional, ClassPath, MergedClassPath, SourceFile, NoSourceFile, Statistics, StatisticsInfo, BatchSourceFile, ScriptSourceFile, ScalaClassLoader, returning } import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat } import settings.{ AestheticSettings } import symtab.{ Flags, SymbolTable, SymbolLoaders, SymbolTrackers } @@ -73,6 +73,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb else new { val global: Global.this.type = Global.this } with JavaPlatform type PlatformClassPath = ClassPath[platform.BinaryRepr] + type OptClassPath = Option[PlatformClassPath] def classPath: PlatformClassPath = platform.classPath @@ -879,20 +880,38 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb * - a list of of packages that should have been invalidated but were not because * they are system packages. */ - def invalidateClassPathEntry(path: String): (List[Symbol], List[Symbol]) = { - val invalidated, failed = new mutable.ListBuffer[Symbol] + def invalidateClassPathEntries(paths: String*): (List[ClassSymbol], List[ClassSymbol]) = { + val invalidated, failed = new mutable.ListBuffer[ClassSymbol] classPath match { - case cp: util.MergedClassPath[_] => - val dir = AbstractFile getDirectory path - val canonical = Some(dir.canonicalPath) - cp.entries find (_.origin == canonical) match { - case Some(oldEntry) => - val newEntry = cp.context.newClassPath(dir) - platform.updateClassPath(oldEntry, newEntry) - informProgress(s"classpath updated to $classPath") - reSync(definitions.RootClass, classPath, oldEntry, newEntry, invalidated, failed) - case None => - error(s"cannot invalidate: no entry named $path in classpath $classPath") + 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 => + println(s"canonical = $canonical, origins = ${cp.entries map (_.origin)}") + error(s"cannot invalidate: no entry named $path in classpath $classPath") + List() + } + } + val subst = Map(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, classPath.context) + val oldEntries = mkClassPath(subst.keys) + val newEntries = mkClassPath(subst.values) + reSync(definitions.RootClass, Some(classPath), Some(oldEntries), Some(newEntries), invalidated, failed) } } def show(msg: String, syms: collection.Traversable[Symbol]) = @@ -903,27 +922,76 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb (invalidated.toList, failed.toList) } - private def reSync(root: Symbol, all: PlatformClassPath, - oldEntry: PlatformClassPath, newEntry: PlatformClassPath, - invalidated: mutable.ListBuffer[Symbol], failed: mutable.ListBuffer[Symbol]) { - ifDebug(informProgress(s"syncing $root, $oldEntry -> $newEntry")) + /** Re-syncs symbol table with classpath + * @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 newEntries Optionally, the corresponding package in the new classPath entries + * @param invalidated A listbuffer collecting the invalidated package classes + * @param failed A listbuffer collecting system package classes which could not be invalidated + * The resyncing strategy is determined by the absence or presence of classes and packages. + * If either oldEntries or newEntries contains classes, root is invalidated, provided a corresponding package + * exists in allEntries, or otherwise is removed. + * Otherwise, the action is determined by the following matrix, with columns: + * + * old new all sym action + * + + + + recurse into all child packages of old ++ new + * + - + + invalidate root + * + - - + remove root from its scope + * - + + + invalidate root + * - + + - create and enter root + * - - * * no action + * + * Here, old, new, all mean classpaths and sym means symboltable. + is presence of an + * entry in its column, - is absence, * is don't care. + * + * Note that new <= all and old <= sym, so the matrix above covers all possibilities. + */ + private def reSync(root: ClassSymbol, + allEntries: OptClassPath, oldEntries: OptClassPath, newEntries: OptClassPath, + invalidated: mutable.ListBuffer[ClassSymbol], failed: mutable.ListBuffer[ClassSymbol]) { + ifDebug(informProgress(s"syncing $root, $oldEntries -> $newEntries")) + val getName: ClassPath[platform.BinaryRepr] => String = (_.name) - val oldPackages = oldEntry.packages sortBy getName - val newPackages = newEntry.packages sortBy getName - val hasChanged = - oldEntry.classes.nonEmpty || - newEntry.classes.nonEmpty || - (oldPackages map getName) != (newPackages map getName) - if (hasChanged && !isSystemPackageClass(root)) { - root setInfo new loaders.PackageLoader(all) + 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 + } + def packageNames(cp: PlatformClassPath): Set[String] = cp.packages.toSet map getName + def subPackage(cp: PlatformClassPath, name: String): OptClassPath = + cp.packages find (cp1 => getName(cp1) == name) + + val classesFound = hasClasses(oldEntries) || hasClasses(newEntries) + if (classesFound && !isSystemPackageClass(root)) { + invalidateOrRemove(root) } else { - if (hasChanged) failed += root - for ((oldNested, newNested) <- oldPackages zip newPackages) { - val pkgname = newNested.name - val pkg = root.info decl newTermName(pkgname) - val allNested = (all.packages find (_.name == pkgname)).get - reSync(pkg.moduleClass, allNested, oldNested, newNested, invalidated, failed) + if (classesFound) { + if (root.isRoot) invalidateOrRemove(definitions.EmptyPackageClass) + else failed += root + } + (oldEntries, newEntries) match { + case (Some(oldcp) , Some(newcp)) => + for (pstr <- packageNames(oldcp) ++ packageNames(newcp)) { + val pname = newTermName(pstr) + val pkg = (root.info decl pname) orElse { + // package was created by external agent, create symbol to track it + assert(!subPackage(oldcp, pstr).isDefined) + loaders.enterPackage(root, pstr, new loaders.PackageLoader(allEntries.get)) + } + reSync( + pkg.moduleClass.asInstanceOf[ClassSymbol], + subPackage(allEntries.get, pstr), subPackage(oldcp, pstr), subPackage(newcp, pstr), + invalidated, failed) + } + case (Some(oldcp), None) => + invalidateOrRemove(root) + case (None, Some(newcp)) => + invalidateOrRemove(root) + case (None, None) => } } } @@ -931,7 +999,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** Invalidate contents of setting -Yinvalidate */ def doInvalidation() = settings.Yinvalidate.value match { case "" => - case entry => invalidateClassPathEntry(entry) + case entry => invalidateClassPathEntries(entry) } // ----------- Runs --------------------------------------- @@ -953,7 +1021,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb * * @param sym A class symbol, object symbol, package, or package class. */ - @deprecated("use invalidateClassPathEntry instead") + @deprecated("use invalidateClassPathEntries instead") def clearOnNextRun(sym: Symbol) = false /* To try out clearOnNext run on the scala.tools.nsc project itself * replace `false` above with the following code @@ -1210,7 +1278,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** Reset all classes contained in current project, as determined by * the clearOnNextRun hook */ - @deprecated("use invalidateClassPathEntry instead") + @deprecated("use invalidateClassPathEntries instead") def resetProjectClasses(root: Symbol): Unit = try { def unlink(sym: Symbol) = if (sym != NoSymbol) root.info.decls.unlink(sym) @@ -1443,6 +1511,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb def compileUnits(units: List[CompilationUnit], fromPhase: Phase) { try compileUnitsInternal(units, fromPhase) catch { case ex => + // ex.printStackTrace(Console.out) // DEBUG for fsc, note that error stacktraces do not print in fsc globalError(supplementErrorMessage("uncaught exception during compilation: " + ex.getClass.getName)) throw ex } |