diff options
author | Martin Odersky <odersky@gmail.com> | 2011-01-25 14:53:45 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2011-01-25 14:53:45 +0000 |
commit | be85330d5b3545ebfa12436c1030558f56d5fbd0 (patch) | |
tree | e44e91fa46aaa3dda6199a1649e7c0292534487f /src/compiler | |
parent | 3235722859a6ff30f186c948c78b1ee37ff36329 (diff) | |
download | scala-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.
Diffstat (limited to 'src/compiler')
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() +} |