summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-02-08 21:26:46 +0000
committerPaul Phillips <paulp@improving.org>2011-02-08 21:26:46 +0000
commit5b9b417ae019d3d79c63837895b8295be7a44ce1 (patch)
treefda7f7584964df59a648e2df9396969fb93783b8
parentac17c71b2357cf37386dec9644b5bdbab1e2fa4f (diff)
downloadscala-5b9b417ae019d3d79c63837895b8295be7a44ce1.tar.gz
scala-5b9b417ae019d3d79c63837895b8295be7a44ce1.tar.bz2
scala-5b9b417ae019d3d79c63837895b8295be7a44ce1.zip
Added compiler crash recovery to the repl.
up it will issue some apologies and then replay the session up to the crash line with a new compiler. If you combine this with -Yrich-exceptions then you can induce a crash, see the exact path through the code which led there, and then continue on your merry way as if nothing happened. // say ticket #4188 for example % scala -Yrich-exceptions scala> class A { | object Ding | class B { | (null: Any) match { case _: Ding.type => () } | } | } assertion failed: Trying to access the this of another class: tree.symbol = class $read$$iw$$iw$A, ctx.clazz.symbol = class $read$$iw$$iw$A$B compilation unit:<console> [searching for exception contexts...] [GenICode.scala$tools$nsc$backend$icode$GenICode$ICodePhase$$genLoad] 958: 959: case This(qual) => 960: assert(tree.symbol == ctx.clazz.symbol || tree.symbol.isModuleClass, *961: "Trying to access the this of another class: " + 962: "tree.symbol = " + tree.symbol + ", ctx.clazz.symbol = " + ctx.clazz.symbol + " compilation unit:"+unit) 963: if (tree.symbol.isModuleClass && tree.symbol != ctx.clazz.symbol) { 964: if (settings.debug.value) [GenICode.genLoadQualifier] 1166: tree match { 1167: case Select(qualifier, _) => 1168: genLoad(qualifier, ctx, toTypeKind(qualifier.tpe)) *1169: case _ => 1170: abort("Unknown qualifier " + tree) 1171: } 1172: [...] Attempting session recovery... scala> No review.
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala77
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ILoop.scala31
-rw-r--r--src/compiler/scala/tools/nsc/io/Sources.scala23
-rw-r--r--src/compiler/scala/tools/nsc/settings/AestheticSettings.scala37
4 files changed, 113 insertions, 55 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index b1490e411a..6e15906b97 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -15,6 +15,7 @@ import io.{ SourceReader, AbstractFile, Path }
import reporters.{ Reporter, ConsoleReporter }
import util.{ Exceptional, ClassPath, SourceFile, Statistics, BatchSourceFile, ScriptSourceFile, ShowPickled, returning }
import reflect.generic.{ PickleBuffer, PickleFormat }
+import settings.{ AestheticSettings }
import symtab.{ Flags, SymbolTable, SymbolLoaders, SymbolTrackers }
import symtab.classfile.Pickler
@@ -206,16 +207,13 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
informComplete("[search path for class files: " + classPath.asClasspathString + "]")
}
- /** Taking flag checking to a somewhat higher level. */
- object opt {
+ object opt extends AestheticSettings {
+ def settings = Global.this.settings
+
// protected implicit lazy val globalPhaseOrdering: Ordering[Phase] = Ordering[Int] on (_.id)
def isActive(ph: Settings#PhasesSetting) = ph containsPhase globalPhase
def wasActive(ph: Settings#PhasesSetting) = ph containsPhase globalPhase.prev
- // Some(value) if setting has been set by user, None otherwise.
- def optSetting[T](s: Settings#Setting): Option[T] =
- if (s.isDefault) None else Some(s.value.asInstanceOf[T])
-
// Allows for syntax like scalac -Xshow-class Random@erasure,typer
private def splitClassAndPhase(str: String, term: Boolean): Name = {
def mkName(s: String) = if (term) newTermName(s) else newTypeName(s)
@@ -228,52 +226,35 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
}
}
- val showClass = optSetting[String](settings.Xshowcls) map (x => splitClassAndPhase(x, false))
- val showObject = optSetting[String](settings.Xshowobj) map (x => splitClassAndPhase(x, true))
- def script = optSetting[String](settings.script)
- def encoding = optSetting[String](settings.encoding)
- def sourceReader = optSetting[String](settings.sourceReader)
+ // debugging
+ def checkPhase = wasActive(settings.check)
+ def logPhase = isActive(settings.log)
+ def typerDebug = settings.Ytyperdebug.value
+ def writeICode = settings.writeICode.value
- // XXX: short term, but I can't bear to add another option.
- // scalac -Dscala.timings will make this true.
- def timings = sys.props contains "scala.timings"
+ // showing/printing things
+ def browsePhase = isActive(settings.browse)
+ def echoFilenames = opt.debug && (opt.verbose || currentRun.size < 5)
+ def noShow = settings.Yshow.isDefault
+ def printLate = settings.printLate.value
+ def printPhase = isActive(settings.Xprint)
+ def showNames = List(showClass, showObject).flatten
+ def showPhase = isActive(settings.Yshow)
+ def showSymbols = settings.Yshowsyms.value
+ def showTrees = settings.Xshowtrees.value
+ val showClass = optSetting[String](settings.Xshowcls) map (x => splitClassAndPhase(x, false))
+ val showObject = optSetting[String](settings.Xshowobj) map (x => splitClassAndPhase(x, true))
- def debug = settings.debug.value
- def deprecation = settings.deprecation.value
- def experimental = settings.Xexperimental.value
- def fatalWarnings = settings.Xwarnfatal.value
- def logClasspath = settings.Ylogcp.value
- def printLate = settings.printLate.value
- def printStats = settings.Ystatistics.value
- def profileClass = settings.YprofileClass.value
- def profileMem = settings.YprofileMem.value
- def richExes = settings.YrichExes.value
- def showTrees = settings.Xshowtrees.value
- def showSymbols = settings.Yshowsyms.value
- def target = settings.target.value
- def typerDebug = settings.Ytyperdebug.value
- def unchecked = settings.unchecked.value
- def verbose = settings.verbose.value
- def writeICode = settings.writeICode.value
- def declsOnly = false
-
- /** Flags as applied to the current or previous phase */
- def browsePhase = isActive(settings.browse)
- def checkPhase = wasActive(settings.check)
- def logPhase = isActive(settings.log)
- def printPhase = isActive(settings.Xprint)
- def showPhase = isActive(settings.Yshow)
+ // profiling
def profCPUPhase = isActive(settings.Yprofile) && !profileAll
+ def profileAll = settings.Yprofile.doAllPhases
+ def profileAny = !settings.Yprofile.isDefault || !settings.YprofileMem.isDefault
+ def profileClass = settings.YprofileClass.value
+ def profileMem = settings.YprofileMem.value
- /** Derived values */
- def noShow = settings.Yshow.isDefault
- def showNames = List(showClass, showObject).flatten
- def profileAll = settings.Yprofile.doAllPhases
- def profileAny = !settings.Yprofile.isDefault || !settings.YprofileMem.isDefault
- def jvm = target startsWith "jvm"
- def msil = target == "msil"
- def verboseDebug = debug && verbose
- def echoFilenames = opt.debug && (opt.verbose || currentRun.size < 5)
+ // XXX: short term, but I can't bear to add another option.
+ // scalac -Dscala.timings will make this true.
+ def timings = sys.props contains "scala.timings"
}
// True if -Xscript has been set, indicating a script run.
diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
index f663bfd1be..90346c480f 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
@@ -17,9 +17,9 @@ import scala.annotation.tailrec
import scala.util.control.Exception.{ ignoring }
import scala.collection.mutable.ListBuffer
import scala.concurrent.ops
-import util.{ ClassPath, stringFromWriter, stringFromStream }
+import util.{ ClassPath, Exceptional, stringFromWriter, stringFromStream }
import interpreter._
-import io.File
+import io.{ File, Sources }
/** The Scala interactive shell. It provides a read-eval-print loop
* around the Interpreter class.
@@ -45,6 +45,9 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
var intp: IMain = _
var power: Power = _
+ // TODO
+ // object opt extends AestheticSettings
+
@deprecated("Use `intp` instead.")
def interpreter = intp
@@ -389,6 +392,25 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
else powerCommands
)
+ private val crashRecovery: PartialFunction[Throwable, Unit] = {
+ case ex: Throwable =>
+ if (settings.YrichExes.value) {
+ val sources = implicitly[Sources]
+ out.println("\n" + ex.getMessage)
+ out.println(
+ if (isReplDebug) "[searching " + sources.path + " for exception contexts...]"
+ else "[searching for exception contexts...]"
+ )
+ out.println(Exceptional(ex).force().context())
+ }
+ else {
+ out.println(util.stackTraceString(ex))
+ }
+ out.println("Attempting session recovery...")
+
+ replay()
+ }
+
/** The main read-eval-print loop for the repl. It calls
* command() for each line of input, and stops when
* command() returns false.
@@ -407,7 +429,10 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
case _ => true
}
- while (processLine(readOneLine)) { }
+ while (true) {
+ try if (!processLine(readOneLine)) return
+ catch crashRecovery
+ }
}
/** interpret all lines from a specified file */
diff --git a/src/compiler/scala/tools/nsc/io/Sources.scala b/src/compiler/scala/tools/nsc/io/Sources.scala
index a3f0e09bb2..317701b3c4 100644
--- a/src/compiler/scala/tools/nsc/io/Sources.scala
+++ b/src/compiler/scala/tools/nsc/io/Sources.scala
@@ -64,10 +64,25 @@ trait LowPrioritySourcesImplicits {
object Sources extends LowPrioritySourcesImplicits {
- val scalaSourceJars = List("scala-library-src.jar", "scala-compiler-src.jar")
- val sourcePathEnv = envOrElse("SOURCEPATH", "")
- val scalaLibraryJarPath = (scalaSourceJars map locateJarByName).flatten map (_.path)
- val defaultSources = apply(scalaLibraryJarPath :+ sourcePathEnv: _*)
+ // Examples of what libraryJar might be, each of which we'd like to find
+ // the source files automatically:
+ //
+ // /scala/trunk/build/pack/lib/scala-library.jar
+ // /scala/trunk/build/quick/classes/library
+ // /scala/inst/scala-2.9.0.r24213-b20110206233447/lib/scala-library.jar
+ private def libraryJar = Path.locateJarByClass(classOf[ScalaObject]) map (_.toAbsolute.path)
+ private def autoSourcePaths: List[String] = libraryJar.toList flatMap { lib =>
+ val markers = List("build/pack/lib", "build/quick/classes", "scala-library.jar")
+ markers filter (lib contains _) flatMap { m =>
+ val dir = Path(lib take lib.indexOf(m)) / "src"
+
+ if (dir.exists) ClassPath.expandDir(dir.path)
+ else Nil
+ }
+ }
+
+ val sourcePathEnv = envOrElse("SOURCEPATH", "")
+ val defaultSources = apply(autoSourcePaths :+ sourcePathEnv: _*)
def apply(paths: String*): Sources = new Sources(ClassPath.join(paths: _*))
}
diff --git a/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala b/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala
new file mode 100644
index 0000000000..d7bec764b3
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala
@@ -0,0 +1,37 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package settings
+
+/** Taking flag checking to a somewhat higher level. */
+trait AestheticSettings {
+ def settings: Settings
+
+ // Some(value) if setting has been set by user, None otherwise.
+ def optSetting[T](s: Settings#Setting): Option[T] =
+ if (s.isDefault) None else Some(s.value.asInstanceOf[T])
+
+ def script = optSetting[String](settings.script)
+ def encoding = optSetting[String](settings.encoding)
+ def sourceReader = optSetting[String](settings.sourceReader)
+
+ def debug = settings.debug.value
+ def declsOnly = false
+ def deprecation = settings.deprecation.value
+ def experimental = settings.Xexperimental.value
+ def fatalWarnings = settings.Xwarnfatal.value
+ def logClasspath = settings.Ylogcp.value
+ def printStats = settings.Ystatistics.value
+ def richExes = settings.YrichExes.value
+ def target = settings.target.value
+ def unchecked = settings.unchecked.value
+ def verbose = settings.verbose.value
+
+ /** Derived values */
+ def jvm = target startsWith "jvm"
+ def msil = target == "msil"
+ def verboseDebug = debug && verbose
+}