diff options
author | Martin Odersky <odersky@gmail.com> | 2011-01-21 15:02:37 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2011-01-21 15:02:37 +0000 |
commit | 7cb70a411a8005cf9230f24b890330bce3894f9c (patch) | |
tree | 8422421e595f3399d161291c88c303cb85a089e6 | |
parent | e6167d9350b8f268379d0b4683400fb7950f0d13 (diff) | |
download | scala-7cb70a411a8005cf9230f24b890330bce3894f9c.tar.gz scala-7cb70a411a8005cf9230f24b890330bce3894f9c.tar.bz2 scala-7cb70a411a8005cf9230f24b890330bce3894f9c.zip |
Better replays.
4 files changed, 80 insertions, 44 deletions
diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala index f993d7c168..4a14bef96a 100644 --- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala +++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala @@ -48,7 +48,7 @@ trait CompilerControl { self: Global => override def toString = "dofirst "+source } - class AskLinkPosItem(sym: Symbol, val source: SourceFile, response: Response[Position]) extends WorkItem { + class AskLinkPosItem(val sym: Symbol, val source: SourceFile, response: Response[Position]) extends WorkItem { def apply() = self.getLinkPos(sym, source, response) override def toString = "linkpos "+sym+" in "+source } @@ -82,7 +82,7 @@ trait CompilerControl { self: Global => protected[interactive] val scheduler = new WorkScheduler /** The compilation unit corresponding to a source file - * if it does not yet exist creat a new one atomically + * if it does not yet exist create a new one atomically */ def unitOf(s: SourceFile): RichCompilationUnit = unitOfFile.synchronized { unitOfFile get s.file match { diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 6efe96861e..a0020272c4 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -60,10 +60,6 @@ self => */ var allSources: List[SourceFile] = List() - /** The currently checked source file - */ - var currentlyChecked: Option[RichCompilationUnit] = None - /** The currently active typer run */ private var currentTyperRun: TyperRun = _ newTyperRun() @@ -115,21 +111,15 @@ self => try { try { pollForWork(old.pos) - } catch { + } catch { case ex : Throwable => - if (context.unit != null) integrateNew() + if (context.unit != null) integrateNew() log.flush() throw ex - } + } if (typerRun == currentTyperRun) return - // @Martin - // Guard against NPEs in integrateNew if context.unit == null here. - // But why are we doing this at all? If it was non-null previously - // integrateNew will already have been called. If it was null previously - // it will still be null now? - // if (context.unit != null) integrateNew() throw FreshRunReq } catch { @@ -168,6 +158,8 @@ self => // ----------------- Polling --------------------------------------- + case class WorkEvent(atNode: Int, atMillis: Long) + var moreWorkAtNode: Int = -1 var nodesSeen = 0 var noWorkFoundAtNode: Int = -1 @@ -178,25 +170,13 @@ self => * Then, poll for work reload/typedTreeAt/doFirst commands during background checking. */ def pollForWork(pos: Position) { - scheduler.pollInterrupt() match { - case Some(ir) => - try { - activeLocks += 1 - ir.execute() - } finally { - activeLocks -= 1 - } - pollForWork(pos) - case _ => - } - - def nodeWithWork(): Option[Int] = { - if (scheduler.moreWork || pendingResponse.isCancelled) Some(nodesSeen) else None - } + def nodeWithWork(): Option[WorkEvent] = + if (scheduler.moreWork || pendingResponse.isCancelled) Some(new WorkEvent(nodesSeen, System.currentTimeMillis)) + else None nodesSeen += 1 logreplay("atnode", nodeWithWork()) match { - case Some(id) => + case Some(WorkEvent(id, _)) => debugLog("some work at node "+id+" current = "+nodesSeen) // assert(id >= nodesSeen) moreWorkAtNode = id @@ -204,6 +184,19 @@ self => } if (nodesSeen >= moreWorkAtNode) { + + logreplay("asked", scheduler.pollInterrupt()) match { + case Some(ir) => + try { + activeLocks += 1 + ir.execute() + } finally { + activeLocks -= 1 + } + pollForWork(pos) + case _ => + } + if (logreplay("cancelled", pendingResponse.isCancelled)) { throw CancelException } @@ -296,6 +289,7 @@ self => for (s <- allSources) { val unit = unitOf(s) + if (!unit.isUpToDate && unit.status != JustParsed) reset(unit) // reparse previously typechecked units. if (unit.status == NotLoaded) parse(unit) } @@ -307,7 +301,7 @@ self => informIDE("Everything is now up to date") } - /** Reset unit to just-parsed state */ + /** Reset unit to unloaded state */ def reset(unit: RichCompilationUnit): Unit = { unit.depends.clear() unit.defined.clear() @@ -328,15 +322,13 @@ self => unit.status = JustParsed } - /** Make sure unit is typechecked with new typer run + /** Make sure unit is typechecked */ def typeCheck(unit: RichCompilationUnit) { debugLog("type checking: "+unit) - //if (currentlyChecked == Some(unit) || unit.status > JustParsed) reset(unit) // not deeded for idempotent type checker phase if (unit.status == NotLoaded) parse(unit) - currentlyChecked = Some(unit) + unit.status = PartiallyChecked currentTyperRun.typeCheck(unit) - currentlyChecked = None unit.lastBody = unit.body unit.status = currentRunId } @@ -361,7 +353,7 @@ self => /** Move list of files to front of allSources */ def moveToFront(fs: List[SourceFile]) { - allSources = fs ++ (allSources diff fs) + allSources = fs ::: (allSources diff fs) } // ----------------- Implementations of client commands ----------------------- @@ -453,7 +445,7 @@ self => informIDE("typedTree" + source + " forceReload: " + forceReload) val unit = unitOf(source) if (forceReload) reset(unit) - if (unit.status <= JustParsed) { + if (unit.status <= PartiallyChecked) { //newTyperRun() // not deeded for idempotent type checker phase typeCheck(unit) } @@ -478,7 +470,7 @@ self => informIDE("getLastTyped" + source) respond(response) { val unit = unitOf(source) - if (unit.status > JustParsed) unit.body + if (unit.status > PartiallyChecked) unit.body else if (unit.lastBody ne EmptyTree) unit.lastBody else typedTree(source, false) } diff --git a/src/compiler/scala/tools/nsc/interactive/Picklers.scala b/src/compiler/scala/tools/nsc/interactive/Picklers.scala index e9f9a98eff..1239ec5003 100644 --- a/src/compiler/scala/tools/nsc/interactive/Picklers.scala +++ b/src/compiler/scala/tools/nsc/interactive/Picklers.scala @@ -1,13 +1,14 @@ package scala.tools.nsc package interactive -import util.{SourceFile, BatchSourceFile} +import util.{SourceFile, BatchSourceFile, InterruptReq} import io.{AbstractFile, PlainFile} import util.{Position, RangePosition, NoPosition, OffsetPosition, TransparentPosition, EmptyAction} import io.{Pickler, CondPickler} import io.Pickler._ import collection.mutable +import mutable.ListBuffer trait Picklers { self: Global => @@ -74,6 +75,50 @@ trait Picklers { self: Global => implicit lazy val position: Pickler[Position] = transparentPosition | rangePosition | offsetPosition | noPosition + implicit lazy val namePickler: Pickler[Name] = + pkl[String] .wrapped { + str => if ((str.length > 1) && (str endsWith "!")) newTypeName(str.init) else newTermName(str) + } { + name => if (name.isTypeName) name.toString+"!" else name.toString + } + + implicit lazy val symPickler: Pickler[Symbol] = { + def ownerNames(sym: Symbol, buf: ListBuffer[Name]): ListBuffer[Name] = { + if (!sym.isRoot) { + ownerNames(sym.owner, buf) + buf += (if (sym.isModuleClass) sym.sourceModule else sym).name + if (!sym.isType && !sym.isStable) { + val sym1 = sym.owner.info.decl(sym.name) + if (sym1.isOverloaded) { + val index = sym1.alternatives.indexOf(sym) + assert(index >= 0, sym1+" not found in alternatives "+sym1.alternatives) + buf += index.toString + } + } + } + buf + } + def makeSymbol(root: Symbol, names: List[Name]): Symbol = names match { + case List() => + root + case name :: rest => + val sym = root.info.decl(name) + if (sym.isOverloaded) makeSymbol(sym.alternatives(rest.head.toString.toInt), rest.tail) + else makeSymbol(sym, rest) + } + pkl[List[Name]] .wrapped { makeSymbol(definitions.RootClass, _) } { ownerNames(_, new ListBuffer).toList } + } + + implicit def workEvent: Pickler[WorkEvent] = { + (pkl[Int] ~ pkl[Long]) + .wrapped { case id ~ ms => WorkEvent(id, ms) } { w => w.atNode ~ w.atMillis } + } + + implicit def interruptReq: Pickler[InterruptReq] = { + val emptyIR: InterruptReq = new InterruptReq { type R = Unit; val todo = () => () } + pkl[Unit] .wrapped { _ => emptyIR } { _ => () } + } + implicit def reloadItem: CondPickler[ReloadItem] = pkl[List[SourceFile]] .wrapped { ReloadItem(_, new Response) } { _.sources } @@ -109,10 +154,9 @@ trait Picklers { self: Global => .wrapped { new AskToDoFirstItem(_) } { _.source } .asClass (classOf[AskToDoFirstItem]) - /** We cannot pickle symbols, so we return always RootClass */ implicit def askLinkPosItem: CondPickler[AskLinkPosItem] = - pkl[SourceFile] - .wrapped { new AskLinkPosItem(definitions.RootClass, _, new Response) } { _.source } + (pkl[Symbol] ~ pkl[SourceFile]) + .wrapped { case sym ~ source => new AskLinkPosItem(sym, source, new Response) } { item => item.sym ~ item.source } .asClass (classOf[AskLinkPosItem]) implicit def emptyAction: CondPickler[EmptyAction] = diff --git a/src/compiler/scala/tools/nsc/util/InterruptReq.scala b/src/compiler/scala/tools/nsc/util/InterruptReq.scala index b9efb1f406..342284fb70 100644 --- a/src/compiler/scala/tools/nsc/util/InterruptReq.scala +++ b/src/compiler/scala/tools/nsc/util/InterruptReq.scala @@ -28,7 +28,7 @@ abstract class InterruptReq { def getResult(): R = synchronized { while (result.isEmpty) { try { - wait() + wait() } catch { case _ : InterruptedException => () } } |