summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/util/Exceptional.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/util/Exceptional.scala')
-rw-r--r--src/compiler/scala/tools/nsc/util/Exceptional.scala123
1 files changed, 123 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/util/Exceptional.scala b/src/compiler/scala/tools/nsc/util/Exceptional.scala
new file mode 100644
index 0000000000..c5fc92443d
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/util/Exceptional.scala
@@ -0,0 +1,123 @@
+package scala.tools.nsc
+package util
+
+import java.lang.reflect.InvocationTargetException
+import io.{ Sources, Fileish }
+import scala.tools.util.StringOps._
+
+/** A simple throwable wrapper so it looks more like a parade of
+ * glittering frame-shaped beauties than the other thing.
+ */
+class Exceptional(val ex: Throwable)(implicit prefs: ScalaPrefs) {
+ val formatter = prefs.exceptionFormatter(ex)
+ val unwrapped = Exceptional.unwrap(ex)
+ val table = formatter.newTable(unwrapped)
+ def rawTrace() = unwrapped.printStackTrace()
+ def isScanDone = prefs.codeSources.isDone()
+
+ /** Block until the scanning is complete. */
+ def force(): this.type = {
+ prefs.codeSources.force()
+ this
+ }
+
+ /** Stack frame contexts are only shown as long as this is true. */
+ def spanFn(frame: JavaStackFrame): Boolean = true
+
+ /** The result of this will be printed before a context trace. */
+ def contextPrelude: String =
+ if (isScanDone) ""
+ else "/* Still scanning source path: there may be more momentarily. */\n"
+
+ /** Frames with surrounding context. */
+ private def contextFrames = toList takeWhile spanFn
+ def contextHead(): String = contextElems.headOption getOrElse ""
+ def contextElems() = contextFrames map formatter.inContext
+ def context(): String = context(length)
+ def context(num: Int): String = contextPrelude + ojoinOr(contextFrames take num map formatter.inContext, "\n", "No stack trace.")
+
+ /** Exceptional doesn't extend Seq because it turns out to be super
+ * annoying in the repl: tab-completion sees all the Seq methods.
+ */
+ def length = toList.length
+ def toList = table.toList
+ def iterator = table.iterator
+ def apply(index: Int) = table(index)
+
+ def causes = Exceptional.causes(ex)
+ def summary = unwrapped.toString + "\n at " + apply(0).shortNameString
+
+ def show(): Unit = println(context())
+ def show(num: Int): Unit = println(context(num))
+ def showCauses() = println((ex :: causes).mkString("", "\n caused by -> ", ""))
+ def showTable() = println(table)
+ def showSummary() = println(summary)
+
+ override def toString = summary
+}
+
+
+object Exceptional {
+ /** The Throwable => Exceptional implicit plus the associated factory. */
+ implicit def throwableToExceptional(ex: Throwable)(implicit prefs: ScalaPrefs): Exceptional = apply(ex)(prefs)
+ def apply(ex: Throwable)(implicit prefs: ScalaPrefs) = new Exceptional(ex)(prefs)
+
+ /** Some handy functions. */
+ def stack() = JavaStackFrame frames ((new Throwable).getStackTrace dropWhile isLocal)
+ def showme() = apply(new Throwable).show()
+
+ /** A frame formatter with more refined aesthetics than the default.
+ * Come, let us be civilized.
+ */
+ object ScalaFormat extends TableDef[JavaStackFrame] {
+ >> ("file" -> (_.fileName)) >+ ":"
+ << ("line" -> (_.line))
+ >> ("class" -> (_.shortestName)) >+ "."
+ << ("method" -> (_.methodName))
+ }
+
+ trait Calibrated {
+ def newTable(ex: Throwable): TableDef[JavaStackFrame]#Table
+ def inContext(frame: JavaStackFrame): String
+ }
+ trait Formatter extends (Throwable => Calibrated) {
+ def apply(ex: Throwable): Calibrated
+ }
+ object Formatter {
+ def apply(implicit prefs: ScalaPrefs): Formatter = new Formatter {
+ def apply(ex: Throwable) = new Calibrated {
+ def newTable(ex: Throwable) = new ScalaFormat.Table(JavaStackFrame frames ex)
+ def inContext(frame: JavaStackFrame) = new FrameContext(frame, prefs.codeSources) toString
+ }
+ }
+ }
+
+ /** Java stack traces have the interesting property of using only the name
+ * of the file, no paths. This makes it a bit of a gamble to try to associate
+ * a stack frame with a specific file. Witness the heuristic.
+ */
+ def locateSources(sources: Sources, frame: JavaStackFrame): List[Fileish] = {
+ // if only one has a matching path, that's fairly sure to be the one
+ val matches = sources(frame.fileName) filter (_.pkgFromPath endsWith frame.pkgName)
+ if (matches.isEmpty || matches.tail.isEmpty)
+ return matches
+
+ // otherwise we'll drink them in and look for a package name
+ matches filter (_.pkgFromSource endsWith frame.pkgName)
+ }
+
+ /** Right now this punts if more than one match and it accepts the first at random.
+ */
+ def locateSource(sources: Sources, frame: JavaStackFrame): Option[Fileish] =
+ locateSources(sources, frame).headOption
+
+ def isLocal(ste: StackTraceElement) = ste.getClassName startsWith this.getClass.getName
+ def causes(x: Throwable): List[Throwable] = x.getCause match {
+ case null => Nil
+ case ex => x :: causes(ex)
+ }
+ def unwrap(x: Throwable): Throwable = x match {
+ case _: InvocationTargetException | _: ExceptionInInitializerError if x.getCause != null => unwrap(x.getCause)
+ case _ => x
+ }
+}