diff options
author | Som Snytt <som.snytt@gmail.com> | 2013-08-24 04:20:44 -0700 |
---|---|---|
committer | Som Snytt <som.snytt@gmail.com> | 2013-09-02 18:59:15 -0700 |
commit | 2fc528e0887b46a8dea403c1f8620ca8967c4b42 (patch) | |
tree | 578b50bc3cb03aeeae6d1359074f3d26b4af44cf /src/compiler/scala/tools/nsc/util | |
parent | a8c05274f738943ae58ecefda4b012b9daf5d8dc (diff) | |
download | scala-2fc528e0887b46a8dea403c1f8620ca8967c4b42.tar.gz scala-2fc528e0887b46a8dea403c1f8620ca8967c4b42.tar.bz2 scala-2fc528e0887b46a8dea403c1f8620ca8967c4b42.zip |
SI-7781 REPL stack trunc shows cause
The handy stack trace truncation in REPL doesn't
show cause like a regular trace.
This commit fixes that and also adds the usual
indicator for truncation, viz, "... 33 more".
The example from the ticket produces:
```
scala> rewrapperer
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: Point of failure
at .rewrapper(<console>:9)
at .rewrapperer(<console>:10)
... 32 elided
Caused by: java.lang.RuntimeException: java.lang.RuntimeException: Point of failure
at .wrapper(<console>:8)
... 34 more
Caused by: java.lang.RuntimeException: Point of failure
at .sample(<console>:7)
... 35 more
```
Suppressed exceptions on Java 7 are handled reflectively.
```
java.lang.RuntimeException: My problem
at scala.tools.nsc.util.StackTraceTest.repressed(StackTraceTest.scala:56)
... 27 elided
Suppressed: java.lang.RuntimeException: Point of failure
at scala.tools.nsc.util.StackTraceTest.sample(StackTraceTest.scala:29)
at scala.tools.nsc.util.StackTraceTest.repressed(StackTraceTest.scala:54)
... 27 more
```
Diffstat (limited to 'src/compiler/scala/tools/nsc/util')
-rw-r--r-- | src/compiler/scala/tools/nsc/util/StackTracing.scala | 76 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/util/package.scala | 18 |
2 files changed, 87 insertions, 7 deletions
diff --git a/src/compiler/scala/tools/nsc/util/StackTracing.scala b/src/compiler/scala/tools/nsc/util/StackTracing.scala new file mode 100644 index 0000000000..fa4fe29f28 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/StackTracing.scala @@ -0,0 +1,76 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + */ + +package scala.tools.nsc.util + +private[util] trait StackTracing extends Any { + + /** Format a stack trace, returning the prefix consisting of frames that satisfy + * a given predicate. + * The format is similar to the typical case described in the JavaDoc + * for [[java.lang.Throwable#printStackTrace]]. + * If a stack trace is truncated, it will be followed by a line of the form + * `... 3 elided`, by analogy to the lines `... 3 more` which indicate + * shared stack trace segments. + * @param e the exception + * @param p the predicate to select the prefix + */ + def stackTracePrefixString(e: Throwable)(p: StackTraceElement => Boolean): String = { + import collection.mutable.{ ArrayBuffer, ListBuffer } + import compat.Platform.EOL + import util.Properties.isJavaAtLeast + + val sb = ListBuffer.empty[String] + + type TraceRelation = String + val Self = new TraceRelation("") + val CausedBy = new TraceRelation("Caused by: ") + val Suppressed = new TraceRelation("Suppressed: ") + + val suppressable = isJavaAtLeast("1.7") + + def clazz(e: Throwable) = e.getClass.getName + def because(e: Throwable): String = e.getCause match { case null => null ; case c => header(c) } + def msg(e: Throwable): String = e.getMessage match { case null => because(e) ; case s => s } + def txt(e: Throwable): String = msg(e) match { case null => "" ; case s => s": $s" } + def header(e: Throwable): String = s"${clazz(e)}${txt(e)}" + + val indent = "\u0020\u0020" + + val seen = new ArrayBuffer[Throwable](16) + def unseen(t: Throwable) = { + def inSeen = seen exists (_ eq t) + val interesting = (t != null) && !inSeen + if (interesting) seen += t + interesting + } + + def print(e: Throwable, r: TraceRelation, share: Array[StackTraceElement], indents: Int): Unit = if (unseen(e)) { + val trace = e.getStackTrace + val frames = ( + if (share.nonEmpty) { + val spare = share.reverseIterator + val trimmed = trace.reverse dropWhile (spare.hasNext && spare.next == _) + trimmed.reverse + } else trace + ) + val prefix = frames takeWhile p + val margin = indent * indents + val indented = margin + indent + sb append s"${margin}${r}${header(e)}" + prefix foreach (f => sb append s"${indented}at $f") + if (frames.size < trace.size) sb append s"$indented... ${trace.size - frames.size} more" + if (r == Self && prefix.size < frames.size) sb append s"$indented... ${frames.size - prefix.size} elided" + print(e.getCause, CausedBy, trace, indents) + if (suppressable) { + import scala.language.reflectiveCalls + type Suppressing = { def getSuppressed(): Array[Throwable] } + for (s <- e.asInstanceOf[Suppressing].getSuppressed) print(s, Suppressed, frames, indents + 1) + } + } + print(e, Self, share = Array.empty, indents = 0) + + sb mkString EOL + } +} diff --git a/src/compiler/scala/tools/nsc/util/package.scala b/src/compiler/scala/tools/nsc/util/package.scala index ea3c9d8dde..72a4bbf5c0 100644 --- a/src/compiler/scala/tools/nsc/util/package.scala +++ b/src/compiler/scala/tools/nsc/util/package.scala @@ -8,7 +8,6 @@ package tools package nsc import java.io.{ OutputStream, PrintStream, ByteArrayOutputStream, PrintWriter, StringWriter } -import scala.compat.Platform.EOL package object util { @@ -79,12 +78,17 @@ package object util { s"$clazz$msg @ $frame" } - def stackTracePrefixString(ex: Throwable)(p: StackTraceElement => Boolean): String = { - val frames = ex.getStackTrace takeWhile p map (" at " + _) - val msg = ex.getMessage match { case null => "" ; case s => s": $s" } - val clazz = ex.getClass.getName - - s"$clazz$msg" +: frames mkString EOL + implicit class StackTraceOps(val e: Throwable) extends AnyVal with StackTracing { + /** Format the stack trace, returning the prefix consisting of frames that satisfy + * a given predicate. + * The format is similar to the typical case described in the JavaDoc + * for [[java.lang.Throwable#printStackTrace]]. + * If a stack trace is truncated, it will be followed by a line of the form + * `... 3 elided`, by analogy to the lines `... 3 more` which indicate + * shared stack trace segments. + * @param p the predicate to select the prefix + */ + def stackTracePrefixString(p: StackTraceElement => Boolean): String = stackTracePrefixString(e)(p) } lazy val trace = new SimpleTracer(System.out) |