summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2011-01-25 14:53:45 +0000
committerMartin Odersky <odersky@gmail.com>2011-01-25 14:53:45 +0000
commitbe85330d5b3545ebfa12436c1030558f56d5fbd0 (patch)
treee44e91fa46aaa3dda6199a1649e7c0292534487f
parent3235722859a6ff30f186c948c78b1ee37ff36329 (diff)
downloadscala-be85330d5b3545ebfa12436c1030558f56d5fbd0.tar.gz
scala-be85330d5b3545ebfa12436c1030558f56d5fbd0.tar.bz2
scala-be85330d5b3545ebfa12436c1030558f56d5fbd0.zip
Added methods askStructure and askLoadedTyped, ...
Added methods askStructure and askLoadedTyped, which should replace any calls to askType. Next: remove askType.
-rw-r--r--src/compiler/scala/tools/nsc/interactive/CompilerControl.scala35
-rw-r--r--src/compiler/scala/tools/nsc/interactive/Global.scala102
-rw-r--r--src/compiler/scala/tools/nsc/interactive/Picklers.scala13
-rw-r--r--src/compiler/scala/tools/nsc/util/MultiHashMap.scala10
4 files changed, 146 insertions, 14 deletions
diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
index a0104c2395..5a1cd0fdde 100644
--- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
+++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
@@ -157,6 +157,29 @@ trait CompilerControl { self: Global =>
def askToDoFirst(source: SourceFile) =
scheduler postWorkItem new AskToDoFirstItem(source)
+ /** If source is not yet loaded, loads it, and starts a new run, otherwise
+ * continues with current pass.
+ * Waits until source is fully type checked and returns body in response.
+ * @param source The source file that needs to be fully typed.
+ * @param response The response, which is set to the fully attributed tree of `source`.
+ * If the unit corresponding to `source` has been removed in the meantime
+ * the a NoSuchUnitError is raised in the response.
+ */
+ def askLoadedTyped(source: SourceFile, response: Response[Tree]) =
+ scheduler postWorkItem new AskLoadedTypedItem(source, response)
+
+ /** Build structure of source file. The structure consists of a list of top-level symbols
+ * in the source file, which might contain themselves nested symbols in their scopes.
+ * All reachable symbols are forced, i.e. their types are completed.
+ * @param source The source file to be analyzed
+ * @param keepLoaded If set to `true`, source file will be kept as a loaded unit afterwards.
+ * If keepLoaded is `false` the operation is run at low priority, only after
+ * everything is brought up to date in a regular type checker run.
+ * @param response The response, which is set to the list of toplevel symbols found in `source`
+ */
+ def askStructure(source: SourceFile, keepLoaded: Boolean, response: Response[List[Symbol]]) =
+ scheduler postWorkItem new AskStructureItem(source, keepLoaded, response)
+
/** Cancels current compiler run and start a fresh one where everything will be re-typechecked
* (but not re-loaded).
*/
@@ -232,6 +255,16 @@ trait CompilerControl { self: Global =>
def apply() = self.getLinkPos(sym, source, response)
override def toString = "linkpos "+sym+" in "+source
}
+
+ class AskLoadedTypedItem(val source: SourceFile, response: Response[Tree]) extends WorkItem {
+ def apply() = self.waitLoadedTyped(source, response)
+ override def toString = "wait loaded & typed "+source
+ }
+
+ class AskStructureItem(val source: SourceFile, val keepLoaded: Boolean, response: Response[List[Symbol]]) extends WorkItem {
+ def apply() = self.buildStructure(source, keepLoaded, response)
+ override def toString = "buildStructure "+source+", keepLoaded = "+keepLoaded
+ }
}
// ---------------- Interpreted exceptions -------------------
@@ -246,3 +279,5 @@ object FreshRunReq extends ControlThrowable
*/
object ShutdownReq extends ControlThrowable
+class NoSuchUnitError(file: AbstractFile) extends Exception("no unit found for file "+file)
+
diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala
index f41e543ea4..d43f676679 100644
--- a/src/compiler/scala/tools/nsc/interactive/Global.scala
+++ b/src/compiler/scala/tools/nsc/interactive/Global.scala
@@ -2,14 +2,14 @@ package scala.tools.nsc
package interactive
import java.io.{ PrintWriter, StringWriter, FileReader, FileWriter }
-import collection.mutable.{ArrayBuffer, SynchronizedBuffer}
+import collection.mutable.{ArrayBuffer, ListBuffer, SynchronizedBuffer, HashMap}
import scala.collection.mutable
import mutable.{LinkedHashMap, SynchronizedMap,LinkedHashSet, SynchronizedSet}
import scala.concurrent.SyncVar
import scala.util.control.ControlThrowable
import scala.tools.nsc.io.{ AbstractFile, LogReplay, Logger, NullLogger, Replayer }
-import scala.tools.nsc.util.{ SourceFile, BatchSourceFile, Position, RangePosition, NoPosition, WorkScheduler }
+import scala.tools.nsc.util.{ SourceFile, BatchSourceFile, Position, RangePosition, NoPosition, WorkScheduler, MultiHashMap }
import scala.tools.nsc.reporters._
import scala.tools.nsc.symtab._
import scala.tools.nsc.ast._
@@ -59,24 +59,27 @@ self =>
val unitOfFile = new LinkedHashMap[AbstractFile, RichCompilationUnit] with
SynchronizedMap[AbstractFile, RichCompilationUnit]
+ /** A list containing all those files that need to be removed
+ * Units are removed by getUnit, typically once a unit is finished compiled.
+ */
protected val toBeRemoved = new ArrayBuffer[AbstractFile] with SynchronizedBuffer[AbstractFile]
+ /** A map that associates with each abstract file the set of responses that are waiting
+ * (via waitLoadedTyped) for the unit associated with the abstract file to be loaded and completely typechecked.
+ */
+ protected val waitLoadedTypeResponses = new MultiHashMap[SourceFile, Response[Tree]]
+
+ /** A map that associates with each abstract file the set of responses that ware waiting
+ * (via buildStructure) for the unit associated with the abstract file to be structure-analyzed
+ */
+ protected var buildStructureResponses = new MultiHashMap[SourceFile, Response[List[Symbol]]]
+
/** The compilation unit corresponding to a source file
* if it does not yet exist create a new one atomically
* Note: We want to rmeove this.
*/
protected[interactive] def getOrCreateUnitOf(source: SourceFile): RichCompilationUnit =
- unitOfFile.synchronized {
- unitOfFile get source.file match {
- case Some(unit) =>
- unit
- case None =>
- println("*** precondition violated: executing operation on non-loaded file " + source)
- val unit = new RichCompilationUnit(source)
- unitOfFile(source.file) = unit
- unit
- }
- }
+ unitOfFile.getOrElse(source.file, { println("precondition violated: "+source+" is not loaded"); new Exception().printStackTrace(); new RichCompilationUnit(source) })
protected [interactive] def onUnitOf[T](source: SourceFile)(op: RichCompilationUnit => T): T =
op(unitOfFile.getOrElse(source.file, new RichCompilationUnit(source)))
@@ -329,9 +332,21 @@ self =>
for (s <- allSources; unit <- getUnit(s)) {
if (!unit.isUpToDate) typeCheck(unit)
+ for (r <- waitLoadedTypeResponses(unit.source))
+ r set unit.body
}
informIDE("Everything is now up to date")
+
+ for ((source, rs) <- waitLoadedTypeResponses; r <- rs) r raise new NoSuchUnitError(source.file)
+ waitLoadedTypeResponses.clear()
+
+ var atOldRun = true
+ for ((source, rs) <- buildStructureResponses; r <- rs) {
+ if (atOldRun) { newTyperRun(); atOldRun = false }
+ buildStructureNow(source, r)
+ }
+ buildStructureResponses.clear()
}
/** Reset unit to unloaded state */
@@ -511,6 +526,7 @@ self =>
}
}
+ /** Implements CompilerControl.askLinkPos */
def getLinkPos(sym: Symbol, source: SourceFile, response: Response[Position]) {
informIDE("getLinkPos "+sym+" "+source)
respond(response) {
@@ -703,6 +719,66 @@ self =>
}
}
+ /** Implements CompilerControl.askLoadedTyped */
+ protected def waitLoadedTyped(source: SourceFile, response: Response[Tree]) {
+ getUnit(source) match {
+ case Some(unit) =>
+ if (unit.isUpToDate) response set unit.body
+ else waitLoadedTypeResponses(source) += response
+ case None =>
+ reloadSources(List(source))
+ waitLoadedTyped(source, response)
+ }
+ }
+
+ /** Implements CompilerControl.askStructure */
+ protected def buildStructure(source: SourceFile, keepLoaded: Boolean, response: Response[List[Symbol]]) {
+ getUnit(source) match {
+ case Some(unit) =>
+ buildStructureNow(source, response)
+ case None =>
+ if (keepLoaded) {
+ reloadSources(List(source))
+ buildStructureNow(source, response)
+ } else {
+ buildStructureResponses(source) += response
+ }
+ }
+ }
+
+ /** Builds structure of given source file */
+ protected def buildStructureNow(source: SourceFile, response: Response[List[Symbol]]) {
+ def forceSym(sym: Symbol) {
+ sym.info.decls.iterator foreach forceSym
+ }
+ respond(response) {
+ onUnitOf(source) { unit =>
+ if (unit.status == NotLoaded) parse(unit)
+ structureTraverser.traverse(unit.body)
+ val topLevelSyms = structureTraverser.getResult
+ topLevelSyms foreach forceSym
+ topLevelSyms
+ }
+ }
+ }
+
+ object structureTraverser extends Traverser {
+ private var topLevelSyms = new ListBuffer[Symbol]
+ override def traverse(tree: Tree) = tree match {
+ case PackageDef(pkg, body) =>
+ body foreach traverse
+ case md: MemberDef =>
+ topLevelSyms += md.symbol
+ case _ =>
+ }
+ def getResult: List[Symbol] = {
+ val result = topLevelSyms.toList
+ topLevelSyms.clear()
+ result
+ }
+ }
+
+
// ---------------- Helper classes ---------------------------
/** A transformer that replaces tree `from` with tree `to` in a given tree */
diff --git a/src/compiler/scala/tools/nsc/interactive/Picklers.scala b/src/compiler/scala/tools/nsc/interactive/Picklers.scala
index 1239ec5003..176e6505af 100644
--- a/src/compiler/scala/tools/nsc/interactive/Picklers.scala
+++ b/src/compiler/scala/tools/nsc/interactive/Picklers.scala
@@ -159,11 +159,22 @@ trait Picklers { self: Global =>
.wrapped { case sym ~ source => new AskLinkPosItem(sym, source, new Response) } { item => item.sym ~ item.source }
.asClass (classOf[AskLinkPosItem])
+ implicit def askLoadedTypedItem: CondPickler[AskLoadedTypedItem] =
+ pkl[SourceFile]
+ .wrapped { new AskLoadedTypedItem(_, new Response) } { _.source }
+ .asClass (classOf[AskLoadedTypedItem])
+
+ implicit def askStructureItem: CondPickler[AskStructureItem] =
+ (pkl[SourceFile] ~ pkl[Boolean])
+ .wrapped { case source ~ keepLoaded => new AskStructureItem(source, keepLoaded, new Response) } { w => w.source ~ w.keepLoaded }
+ .asClass (classOf[AskStructureItem])
+
implicit def emptyAction: CondPickler[EmptyAction] =
pkl[Unit]
.wrapped { _ => new EmptyAction } { _ => () }
.asClass (classOf[EmptyAction])
implicit def action: Pickler[() => Unit] =
- reloadItem | askTypeAtItem | askTypeItem | askLastTypeItem | askTypeCompletionItem | askScopeCompletionItem | askToDoFirstItem | emptyAction
+ reloadItem | askTypeAtItem | askTypeItem | askLastTypeItem | askTypeCompletionItem | askScopeCompletionItem |
+ askToDoFirstItem | askLinkPosItem | askLoadedTypedItem | askStructureItem | emptyAction
}
diff --git a/src/compiler/scala/tools/nsc/util/MultiHashMap.scala b/src/compiler/scala/tools/nsc/util/MultiHashMap.scala
new file mode 100644
index 0000000000..719d18cd2e
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/util/MultiHashMap.scala
@@ -0,0 +1,10 @@
+package scala.tools.nsc.util
+
+import collection.mutable.HashMap
+import collection.immutable
+
+/** A hashmap with set-valued values, and an empty set as default value
+ */
+class MultiHashMap[K, V] extends HashMap[K, immutable.Set[V]] {
+ override def default(key: K): immutable.Set[V] = Set()
+}