summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@epfl.ch>2012-05-28 12:55:23 -0700
committerAdriaan Moors <adriaan.moors@epfl.ch>2012-05-28 12:55:23 -0700
commit765aab65614fd18b67b961f4b91047b536a554c3 (patch)
treee19d96f11d34f0a502bbcb939a0dce978c51632e /src/compiler
parent3f290baa0fdd5e8a34dba698ba6d0b642a71d75d (diff)
parentace051ff0abe112b767c3912f846eb4d50e52cf5 (diff)
downloadscala-765aab65614fd18b67b961f4b91047b536a554c3.tar.gz
scala-765aab65614fd18b67b961f4b91047b536a554c3.tar.bz2
scala-765aab65614fd18b67b961f4b91047b536a554c3.zip
Merge pull request #640 from odersky/topic/inkling
Implemented functionality to invalidate classpath entries
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala102
-rw-r--r--src/compiler/scala/tools/nsc/backend/JavaPlatform.scala14
-rw-r--r--src/compiler/scala/tools/nsc/backend/MSILPlatform.scala6
-rw-r--r--src/compiler/scala/tools/nsc/backend/Platform.scala3
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Members.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/util/ClassPath.scala5
7 files changed, 127 insertions, 6 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index fb5b9b7169..9089be5cc0 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -72,7 +72,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
if (forMSIL) new { val global: Global.this.type = Global.this } with MSILPlatform
else new { val global: Global.this.type = Global.this } with JavaPlatform
- def classPath: ClassPath[platform.BinaryRepr] = platform.classPath
+ type PlatformClassPath = ClassPath[platform.BinaryRepr]
+
+ def classPath: PlatformClassPath = platform.classPath
+
def rootLoader: LazyType = platform.rootLoader
// sub-components --------------------------------------------------
@@ -840,6 +843,97 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
def printAfterEachPhase[T](op: => T): Unit =
describeAfterEachPhase(op) foreach (m => println(" " + m))
+ // ------------ Invalidations ---------------------------------
+
+ /** Is given package class a system package class that cannot be invalidated?
+ */
+ private def isSystemPackageClass(pkg: Symbol) =
+ pkg == definitions.RootClass ||
+ pkg == definitions.ScalaPackageClass || {
+ val pkgname = pkg.fullName
+ (pkgname startsWith "scala.") && !(pkgname startsWith "scala.tools")
+ }
+
+ /** Invalidates packages that contain classes defined in a classpath entry, and
+ * rescans that entry.
+ * @param path A fully qualified name that refers to a directory or jar file that's
+ * an entry on the classpath.
+ * First, causes the classpath entry referred to by `path` to be rescanned, so that
+ * any new files or deleted files or changes in subpackages are picked up.
+ * Second, invalidates any packages for which one of the following considitions is met:
+
+ * - the classpath entry contained during the last compilation run classfiles
+ * that represent a member in the package
+ * - the classpath entry now contains classfiles
+ * that represent a member in the package
+ * - the set of subpackages has changed.
+ *
+ * The invalidated packages are reset in their entirety; all member classes and member packages
+ * are re-accessed using the new classpath.
+ * Not invalidated are system packages that the compiler needs to access as parts
+ * of standard definitions. The criterion what is a system package is currently:
+ * any package rooted in "scala", with the exception of packages rooted in "scala.tools".
+ * This can be refined later.
+ * @return A pair consisting of
+ * - a list of invalidated packages
+ * - 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]
+ 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")
+ }
+ }
+ def show(msg: String, syms: collection.Traversable[Symbol]) =
+ if (syms.nonEmpty)
+ informProgress(s"$msg: ${syms map (_.fullName) mkString ","}")
+ show("invalidated packages", invalidated)
+ show("could not invalidate system packages", failed)
+ (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"))
+ 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)
+ invalidated += 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)
+ }
+ }
+ }
+
+ /** Invalidate contents of setting -Yinvalidate */
+ def doInvalidation() = settings.Yinvalidate.value match {
+ case "" =>
+ case entry => invalidateClassPathEntry(entry)
+ }
+
// ----------- Runs ---------------------------------------
private var curRun: Run = null
@@ -859,6 +953,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")
def clearOnNextRun(sym: Symbol) = false
/* To try out clearOnNext run on the scala.tools.nsc project itself
* replace `false` above with the following code
@@ -870,7 +965,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
}
}}
- * Then, fsc -Xexperimental clears the nsc porject between successive runs of `fsc`.
+ * Then, fsc -Xexperimental clears the nsc project between successive runs of `fsc`.
*/
/** Remove the current run when not needed anymore. Used by the build
@@ -1115,6 +1210,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")
def resetProjectClasses(root: Symbol): Unit = try {
def unlink(sym: Symbol) =
if (sym != NoSymbol) root.info.decls.unlink(sym)
@@ -1353,6 +1449,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
}
private def compileUnitsInternal(units: List[CompilationUnit], fromPhase: Phase) {
+ doInvalidation()
+
units foreach addUnit
val startTime = currentTime
diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
index 7da7611ce2..e5e67e8811 100644
--- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
+++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
@@ -7,7 +7,7 @@ package scala.tools.nsc
package backend
import io.AbstractFile
-import util.{ClassPath,JavaClassPath}
+import util.{ClassPath,JavaClassPath,MergedClassPath,DeltaClassPath}
import util.ClassPath.{ JavaContext, DefaultJavaContext }
import scala.tools.util.PathResolver
@@ -17,7 +17,17 @@ trait JavaPlatform extends Platform {
type BinaryRepr = AbstractFile
- lazy val classPath = new PathResolver(settings).result
+ private var currentClassPath: Option[MergedClassPath[BinaryRepr]] = None
+
+ def classPath: ClassPath[BinaryRepr] = {
+ if (currentClassPath.isEmpty) currentClassPath = Some(new PathResolver(settings).result)
+ currentClassPath.get
+ }
+
+ /** Update classpath with a substituted subentry */
+ def updateClassPath(oldEntry: ClassPath[BinaryRepr], newEntry: ClassPath[BinaryRepr]) =
+ currentClassPath = Some(new DeltaClassPath(currentClassPath.get, oldEntry, newEntry))
+
def rootLoader = new loaders.PackageLoader(classPath.asInstanceOf[ClassPath[platform.BinaryRepr]])
// [Martin] Why do we need a cast here?
// The problem is that we cannot specify at this point that global.platform should be of type JavaPlatform.
diff --git a/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala b/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala
index 65b1fbc229..30447e1274 100644
--- a/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala
+++ b/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala
@@ -30,7 +30,11 @@ trait MSILPlatform extends Platform {
lazy val classPath = MsilClassPath.fromSettings(settings)
def rootLoader = new loaders.PackageLoader(classPath.asInstanceOf[ClassPath[platform.BinaryRepr]])
// See discussion in JavaPlatForm for why we need a cast here.
-
+
+ /** Update classpath with a substituted subentry */
+ def updateClassPath(oldEntry: ClassPath[BinaryRepr], newEntry: ClassPath[BinaryRepr]) =
+ throw new UnsupportedOperationException("classpath invalidations not supported on MSIL")
+
def platformPhases = List(
genMSIL // generate .msil files
)
diff --git a/src/compiler/scala/tools/nsc/backend/Platform.scala b/src/compiler/scala/tools/nsc/backend/Platform.scala
index 23592eeb61..f97d3e3d56 100644
--- a/src/compiler/scala/tools/nsc/backend/Platform.scala
+++ b/src/compiler/scala/tools/nsc/backend/Platform.scala
@@ -23,6 +23,9 @@ trait Platform {
/** The root symbol loader. */
def rootLoader: LazyType
+
+ /** Update classpath with a substituted subentry */
+ def updateClassPath(oldEntry: ClassPath[BinaryRepr], newEntry: ClassPath[BinaryRepr])
/** Any platform-specific phases. */
def platformPhases: List[SubComponent]
diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala
index 019e887c4e..efb4e7a199 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala
@@ -156,7 +156,7 @@ trait Members {
def newBlock() = code.newBlock
def startBlock = code.startBlock
- def lastBlock = blocks.last
+ def lastBlock = { assert(blocks.nonEmpty, symbol); blocks.last }
def blocks = code.blocksList
def linearizedBlocks(lin: Linearizer = self.linearizer): List[BasicBlock] = lin linearize this
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 4aa30038f6..efde2cee25 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -171,6 +171,7 @@ trait ScalaSettings extends AbsScalaSettings
val Ynotnull = BooleanSetting ("-Ynotnull", "Enable (experimental and incomplete) scala.NotNull.")
val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overriden methods.")
val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.")
+ val Yinvalidate = StringSetting ("-Yinvalidate", "classpath-entry", "Invalidate classpath entry before run", "")
val noSelfCheck = BooleanSetting ("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.")
val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes")
diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala
index 404490bd49..18b9c7dd01 100644
--- a/src/compiler/scala/tools/nsc/util/ClassPath.scala
+++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala
@@ -312,6 +312,11 @@ class DirectoryClassPath(val dir: AbstractFile, val context: ClassPathContext[Ab
override def toString() = "directory classpath: "+ origin.getOrElse("?")
}
+class DeltaClassPath[T](original: MergedClassPath[T], oldEntry: ClassPath[T], newEntry: ClassPath[T])
+extends MergedClassPath[T](original.entries map (e => if (e == oldEntry) newEntry else e), original.context) {
+ require(original.entries contains oldEntry)
+}
+
/**
* A classpath unifying multiple class- and sourcepath entries.
*/