diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/util/Exceptional.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/util/Exceptional.scala | 123 |
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 + } +} |