diff options
author | Paul Phillips <paulp@improving.org> | 2011-02-08 21:26:46 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2011-02-08 21:26:46 +0000 |
commit | 5b9b417ae019d3d79c63837895b8295be7a44ce1 (patch) | |
tree | fda7f7584964df59a648e2df9396969fb93783b8 | |
parent | ac17c71b2357cf37386dec9644b5bdbab1e2fa4f (diff) | |
download | scala-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.scala | 77 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/interpreter/ILoop.scala | 31 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/io/Sources.scala | 23 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/settings/AestheticSettings.scala | 37 |
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 +} |