From b5c20527352085b01457e9104137de7b28eafdff Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 7 Sep 2010 15:36:13 +0000 Subject: Changes to presentation compiler. --- src/compiler/scala/tools/nsc/Global.scala | 4 + .../tools/nsc/interactive/CompilerControl.scala | 26 +++-- .../scala/tools/nsc/interactive/ContextTrees.scala | 6 +- .../scala/tools/nsc/interactive/Global.scala | 110 ++++++++++++++------- .../scala/tools/nsc/typechecker/Namers.scala | 4 +- 5 files changed, 106 insertions(+), 44 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 1f2aad5ddd..3eb7193530 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -132,6 +132,10 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable */ def registerContext(c: analyzer.Context) {} + /** Register top level class (called on entering the class) + */ + def registerTopLevelSym(sym: Symbol) {} + // ------------------ Reporting ------------------------------------- def error(msg: String) = reporter.error(NoPosition, msg) diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala index d2706b8878..1772f6f722 100644 --- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala +++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala @@ -42,14 +42,17 @@ trait CompilerControl { self: Global => protected val scheduler = new WorkScheduler /** The compilation unit corresponding to a source file + * if it does not yet exist creat a new one atomically */ - def unitOf(s: SourceFile): RichCompilationUnit = unitOfFile get s.file match { - case Some(unit) => - unit - case None => - val unit = new RichCompilationUnit(s) - unitOfFile(s.file) = unit - unit + def unitOf(s: SourceFile): RichCompilationUnit = unitOfFile.synchronized { + unitOfFile get s.file match { + case Some(unit) => + unit + case None => + val unit = new RichCompilationUnit(s) + unitOfFile(s.file) = unit + unit + } } /** The compilation unit corresponding to a position */ @@ -60,6 +63,15 @@ trait CompilerControl { self: Global => */ def removeUnitOf(s: SourceFile) = unitOfFile remove s.file + /* returns the top level classes and objects that were deleted + * in the editor since last time recentlyDeleted() was called. + */ + def recentlyDeleted(): List[Symbol] = deletedTopLevelSyms.synchronized { + val result = deletedTopLevelSyms + deletedTopLevelSyms.clear() + result.toList + } + /** Locate smallest tree that encloses position */ def locateTree(pos: Position): Tree = diff --git a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala index 8fa4b86219..11aafea71a 100644 --- a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala +++ b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala @@ -27,7 +27,7 @@ trait ContextTrees { self: Global => /** Optionally returns the smallest context that contains given `pos`, or None if none exists. */ - def locateContext(contexts: Contexts, pos: Position): Option[Context] = { + def locateContext(contexts: Contexts, pos: Position): Option[Context] = synchronized { def locateNearestContextTree(contexts: Contexts, pos: Position, recent: Array[ContextTree]): Option[ContextTree] = { locateContextTree(contexts, pos) match { case Some(x) => @@ -70,7 +70,7 @@ trait ContextTrees { self: Global => * If the `context` has a transparent position, add it multiple times * at the positions of all its solid descendant trees. */ - def addContext(contexts: Contexts, context: Context) { + def addContext(contexts: Contexts, context: Context): Unit = { val cpos = context.tree.pos if (cpos.isTransparent) for (t <- context.tree.children flatMap solidDescendants) @@ -82,7 +82,7 @@ trait ContextTrees { self: Global => /** Insert a context with non-transparent position `cpos` * at correct position into a buffer of context trees. */ - def addContext(contexts: Contexts, context: Context, cpos: Position) { + def addContext(contexts: Contexts, context: Context, cpos: Position): Unit = synchronized { try { if (!cpos.isRange) {} else if (contexts.isEmpty) contexts += new ContextTree(cpos, context) diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index bf901b321a..1f90d6c061 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -3,7 +3,8 @@ package interactive import java.io.{ PrintWriter, StringWriter } -import scala.collection.mutable.{LinkedHashMap, SynchronizedMap} +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 @@ -85,7 +86,13 @@ self => while(true) try { - pollForWork() + try { + pollForWork() + } catch { + case ex : Throwable => + if (context.unit != null) integrateNew() + throw ex + } if (typerRun == currentTyperRun) return @@ -112,6 +119,18 @@ self => case _ => } + /** The top level classes and objects currently seen in the presentation compiler + */ + private val currentTopLevelSyms = new mutable.LinkedHashSet[Symbol] + + /** The top level classes and objects no longer seen in the presentation compiler + */ + val deletedTopLevelSyms = new mutable.LinkedHashSet[Symbol] with mutable.SynchronizedSet[Symbol] + + /** Called from typechecker every time a top-level class or object is entered. + */ + override def registerTopLevelSym(sym: Symbol) { currentTopLevelSyms += sym } + // ----------------- Polling --------------------------------------- /** Called from runner thread and signalDone: @@ -276,8 +295,18 @@ self => activeLocks = 0 currentTyperRun.typeCheck(unit) unit.status = currentRunId - // todo: garbage collect any tyop-level symbols whose types are no longer valid for - // currentRunId + syncTopLevelSyms(unit) + } + } + + def syncTopLevelSyms(unit: RichCompilationUnit) { + val deleted = currentTopLevelSyms filter { sym => + sym.sourceFile == unit.source.file && runId(sym.validTo) < currentRunId + } + for (d <- deleted) { + d.owner.info.decls unlink d + deletedTopLevelSyms += d + currentTopLevelSyms -= d } } @@ -288,19 +317,30 @@ self => // ----------------- Implementations of client commands ----------------------- - def respond[T](result: Response[T])(op: => T): Unit = { + def respond[T](result: Response[T])(op: => T): Unit = + respondGradually(result)(Stream(op)) + + def respondGradually[T](response: Response[T])(op: => Stream[T]): Unit = { val prevResponse = pendingResponse try { - pendingResponse = result - if (!result.isCancelled) result set op + pendingResponse = response + if (!response.isCancelled) { + var results = op + while (!response.isCancelled && results.nonEmpty) { + val result = results.head + results = results.tail + if (results.isEmpty) response set result + else response setProvisionally result + } + } } catch { case CancelException => ; case ex @ FreshRunReq => - scheduler.postWorkItem(() => respond(result)(op)) + scheduler.postWorkItem(() => respondGradually(response)(op)) throw ex case ex => - result raise ex + response raise ex throw ex } finally { pendingResponse = prevResponse @@ -319,8 +359,8 @@ self => } /** Make sure a set of compilation units is loaded and parsed */ - def reload(sources: List[SourceFile], result: Response[Unit]) { - respond(result)(reloadSources(sources)) + def reload(sources: List[SourceFile], response: Response[Unit]) { + respond(response)(reloadSources(sources)) if (outOfDate) throw FreshRunReq // cancel background compile else outOfDate = true // proceed normally and enable new background compile } @@ -344,14 +384,14 @@ self => currentTyperRun.typedTree(unitOf(source)) } - /** Set sync var `result` to a fully attributed tree located at position `pos` */ - def getTypedTreeAt(pos: Position, result: Response[Tree]) { - respond(result)(typedTreeAt(pos)) + /** Set sync var `response` to a fully attributed tree located at position `pos` */ + def getTypedTreeAt(pos: Position, response: Response[Tree]) { + respond(response)(typedTreeAt(pos)) } - /** Set sync var `result` to a fully attributed tree corresponding to the entire compilation unit */ - def getTypedTree(source : SourceFile, forceReload: Boolean, result: Response[Tree]) { - respond(result)(typedTree(source, forceReload)) + /** Set sync var `response` to a fully attributed tree corresponding to the entire compilation unit */ + def getTypedTree(source : SourceFile, forceReload: Boolean, response: Response[Tree]) { + respond(response)(typedTree(source, forceReload)) } def stabilizedType(tree: Tree): Type = tree match { @@ -372,8 +412,8 @@ self => import analyzer.{SearchResult, ImplicitSearch} - def getScopeCompletion(pos: Position, result: Response[List[Member]]) { - respond(result) { scopeMembers(pos) } + def getScopeCompletion(pos: Position, response: Response[List[Member]]) { + respond(response) { scopeMembers(pos) } } val Dollar = newTermName("$") @@ -415,12 +455,12 @@ self => result } - def getTypeCompletion(pos: Position, result: Response[List[Member]]) { - respond(result) { typeMembers(pos) } + def getTypeCompletion(pos: Position, response: Response[List[Member]]) { + respondGradually(response) { typeMembers(pos) } if (debugIDE) scopeMembers(pos) } - def typeMembers(pos: Position): List[TypeMember] = { + def typeMembers(pos: Position): Stream[List[TypeMember]] = { var tree = typedTreeAt(pos) // Let's say you have something like val x: List[Int] and ypu want to get completion after List @@ -478,19 +518,23 @@ self => for (sym <- ownerTpe.decls) addTypeMember(sym, pre, false, NoSymbol) - for (sym <- ownerTpe.members) - addTypeMember(sym, pre, true, NoSymbol) - val applicableViews: List[SearchResult] = - new ImplicitSearch(tree, functionType(List(ownerTpe), AnyClass.tpe), isView = true, context.makeImplicit(reportAmbiguousErrors = false)) - .allImplicits - for (view <- applicableViews) { - val vtree = viewApply(view) - val vpre = stabilizedType(vtree) - for (sym <- vtree.tpe.members) { - addTypeMember(sym, vpre, false, view.tree.symbol) + members.values.toList #:: { + for (sym <- ownerTpe.members) + addTypeMember(sym, pre, true, NoSymbol) + members.values.toList #:: { + val applicableViews: List[SearchResult] = + new ImplicitSearch(tree, functionType(List(ownerTpe), AnyClass.tpe), isView = true, context.makeImplicit(reportAmbiguousErrors = false)) + .allImplicits + for (view <- applicableViews) { + val vtree = viewApply(view) + val vpre = stabilizedType(vtree) + for (sym <- vtree.tpe.members) { + addTypeMember(sym, vpre, false, view.tree.symbol) + } + } + Stream(members.values.toList) } } - members.values.toList } // ---------------- Helper classes --------------------------- diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 235f067c95..b5d3a939e1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -216,6 +216,7 @@ trait Namers { self: Analyzer => assert(currentRun.canRedefine(clazz) || clazz.sourceFile == currentRun.symSource(c)); currentRun.symSource(c) = clazz.sourceFile } + registerTopLevelSym(clazz) } assert(c.name.toString.indexOf('(') == -1) c @@ -244,9 +245,10 @@ trait Namers { self: Analyzer => m.moduleClass.setFlag(moduleClassFlags(moduleFlags)) setPrivateWithin(tree, m.moduleClass, tree.mods) } - if (m.owner.isPackageClass) { + if (m.owner.isPackageClass && !m.isPackage) { m.moduleClass.sourceFile = context.unit.source.file currentRun.symSource(m) = m.moduleClass.sourceFile + registerTopLevelSym(m) } m } -- cgit v1.2.3