summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2011-11-13 17:36:47 +0000
committerMartin Odersky <odersky@gmail.com>2011-11-13 17:36:47 +0000
commit167309afd10f9b65b35e6874a30ea6340a1ddc44 (patch)
treebdbb25160c996601d3ef52e0b0c8006176a406c0
parent46050d6ec42bfb274a7638488efcc944e351451c (diff)
downloadscala-167309afd10f9b65b35e6874a30ea6340a1ddc44.tar.gz
scala-167309afd10f9b65b35e6874a30ea6340a1ddc44.tar.bz2
scala-167309afd10f9b65b35e6874a30ea6340a1ddc44.zip
Added functionality to clear project files from...
Added functionality to clear project files from a resident compiler.
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala73
-rw-r--r--src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala10
2 files changed, 78 insertions, 5 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 2bbf2f9ac7..11c0911337 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -716,6 +716,34 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
private var curRun: Run = null
private var curRunId = 0
+ /** A hook that lets subclasses of `Global` define whether a package or class should be kept loaded for the
+ * next compiler run. If the parameter `sym` is a class or object, and `clearOnNextRun(sym)` returns `true`,
+ * then the symbol is unloaded and reset to its state before the last compiler run. If the parameter `sym` is
+ * a package, and clearOnNextRun(sym)` returns `true`, the package is recursively searched for
+ * classes to drop.
+ *
+ * Example: Let's say I want a compiler that drops all classes corresponding to the current project
+ * between runs. Then `keepForNextRun` of a toplevel class or object should return `true` if the
+ * class or object does not form part of the current project, `false` otherwise. For a package,
+ * clearOnNextRun should return `true` if no class in that package forms part of the current project,
+ * `false` otherwise.
+ *
+ * @param sym A class symbol, object symbol, package, or package class.
+ */
+ def clearOnNextRun(sym: Symbol) = false
+ /* To try out clearOnNext run on the scala.tools.nsc project itself
+ * replace `false` above with the following code
+
+ settings.Xexperimental.value && { sym.isRoot || {
+ sym.fullName match {
+ case "scala" | "scala.tools" | "scala.tools.nsc" => true
+ case _ => sym.owner.fullName.startsWith("scala.tools.nsc")
+ }
+ }}
+
+ * Then, fsc -Xexperimental clears the nsc porject between successive runs of `fsc`.
+ */
+
/** Remove the current run when not needed anymore. Used by the build
* manager to save on the memory foot print. The current run holds on
* to all compilation units, which in turn hold on to trees.
@@ -796,6 +824,46 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
parserPhase
}
+ /** Reset all classes contained in current project, as determined by
+ * the clearOnNextRun hook
+ */
+ def resetProjectClasses(root: Symbol): Unit = try {
+ def unlink(sym: Symbol) =
+ if (sym != NoSymbol) root.info.decls.unlink(sym)
+ if (settings.verbose.value) inform("[reset] recursing in "+root)
+ val toReload = mutable.Set[String]()
+ for (sym <- root.info.decls) {
+ if (sym.isInitialized && clearOnNextRun(sym))
+ if (sym.isPackage) {
+ resetProjectClasses(sym.moduleClass)
+ openPackageModule(sym.moduleClass)
+ } else {
+ unlink(sym)
+ unlink(root.info.decls.lookup(
+ if (sym.isTerm) sym.name.toTypeName else sym.name.toTermName))
+ toReload += sym.fullName
+ // note: toReload could be set twice with the same name
+ // but reinit must happen only once per name. That's why
+ // the following classPath.findClass { ... } code cannot be moved here.
+ }
+ }
+ for (fullname <- toReload)
+ classPath.findClass(fullname) match {
+ case Some(classRep) =>
+ if (settings.verbose.value) inform("[reset] reinit "+fullname)
+ loaders.initializeFromClassPath(root, classRep)
+ case _ =>
+ }
+ } catch {
+ case ex: Throwable =>
+ // this handler should not be nessasary, but it seems that `fsc`
+ // eats exceptions if they appear here. Need to find out the cause for
+ // this and fix it.
+ inform("[reset] exception happened: "+ex);
+ ex.printStackTrace();
+ throw ex
+ }
+
// --------------- Miscellania -------------------------------
/** The currently compiled unit; set from GlobalPhase */
@@ -1103,6 +1171,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
// Clear any sets or maps created via perRunCaches.
perRunCaches.clearAll()
+
+ // Reset project
+ atPhase(namerPhase) {
+ resetProjectClasses(definitions.RootClass)
+ }
}
/** Compile list of abstract files. */
diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala
index da71628097..340e1d1d08 100644
--- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala
+++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala
@@ -77,18 +77,18 @@ abstract class SymbolLoaders {
enterClassAndModule(root, name, new SourcefileLoader(src))
}
- /** Initialize symbol `root` from class path representation `classRep`
+ /** Initialize toplevel class and module symbols in `owner` from class path representation `classRep`
*/
- def initializeFromClassPath(root: Symbol, classRep: ClassPath[platform.BinaryRepr]#ClassRep) {
+ def initializeFromClassPath(owner: Symbol, classRep: ClassPath[platform.BinaryRepr]#ClassRep) {
((classRep.binary, classRep.source) : @unchecked) match {
case (Some(bin), Some(src)) if platform.needCompile(bin, src) =>
if (settings.verbose.value) inform("[symloader] picked up newer source file for " + src.path)
- global.loaders.enterToplevelsFromSource(root, classRep.name, src)
+ global.loaders.enterToplevelsFromSource(owner, classRep.name, src)
case (None, Some(src)) =>
if (settings.verbose.value) inform("[symloader] no class, picked up source file for " + src.path)
- global.loaders.enterToplevelsFromSource(root, classRep.name, src)
+ global.loaders.enterToplevelsFromSource(owner, classRep.name, src)
case (Some(bin), _) =>
- global.loaders.enterClassAndModule(root, classRep.name, platform.newClassLoader(bin))
+ global.loaders.enterClassAndModule(owner, classRep.name, platform.newClassLoader(bin))
}
}