TOREE-341 Fixed exception stack traces getting lost.

This is a rather ugly fix to address the issue caused by
https://issues.scala-lang.org/browse/SI-8935

The crux of the issue is that valueOfTerm returns None -- always

This introduces a small class that is bound into the REPL scope so that we
can modify its internal state and use that to retrieve values.

We can extend this to a more general case later on if needed.


Project: http://git-wip-us.apache.org/repos/asf/incubator-toree/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-toree/commit/7e947b4e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-toree/tree/7e947b4e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-toree/diff/7e947b4e

Branch: refs/heads/master
Commit: 7e947b4ea8876cc6050acd5e968760fa4241de6c
Parents: 1ea7b56
Author: Marius Van Niekerk <mniek...@vm179.corp.maxpointinteractive.com>
Authored: Wed Sep 21 11:17:28 2016 -0400
Committer: Marius van Niekerk <marius.v.niek...@gmail.com>
Committed: Tue Sep 27 10:23:47 2016 -0400

----------------------------------------------------------------------
 .../scala/ScalaInterpreterSpecific.scala        | 54 +++++++++++++++++---
 1 file changed, 46 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/7e947b4e/scala-interpreter/src/main/scala-2.11/org/apache/toree/kernel/interpreter/scala/ScalaInterpreterSpecific.scala
----------------------------------------------------------------------
diff --git 
a/scala-interpreter/src/main/scala-2.11/org/apache/toree/kernel/interpreter/scala/ScalaInterpreterSpecific.scala
 
b/scala-interpreter/src/main/scala-2.11/org/apache/toree/kernel/interpreter/scala/ScalaInterpreterSpecific.scala
index be729e0..6a31a55 100644
--- 
a/scala-interpreter/src/main/scala-2.11/org/apache/toree/kernel/interpreter/scala/ScalaInterpreterSpecific.scala
+++ 
b/scala-interpreter/src/main/scala-2.11/org/apache/toree/kernel/interpreter/scala/ScalaInterpreterSpecific.scala
@@ -36,6 +36,7 @@ trait ScalaInterpreterSpecific extends SettingsProducerLike { 
this: ScalaInterpr
 
   private var iMain: IMain = _
   private var jLineCompleter: JLineCompletion = _
+  private val exceptionHack = new ExceptionHack()
 
   def _runtimeClassloader = {
     _thisClassloader
@@ -293,6 +294,9 @@ trait ScalaInterpreterSpecific extends SettingsProducerLike 
{ this: ScalaInterpr
       //   ADD IMPORTS generates too many classes, client is responsible for 
adding import
       logger.debug("Adding org.apache.spark.SparkContext._ to imports")
       iMain.interpret("import org.apache.spark.SparkContext._")
+
+      logger.debug("Adding the hack for the exception handling retrieval.")
+      iMain.bind("_exceptionHack", exceptionHack)
     }
 
     this
@@ -347,6 +351,20 @@ trait ScalaInterpreterSpecific extends 
SettingsProducerLike { this: ScalaInterpr
     }
   }
 
+  private def retrieveLastException: Throwable = {
+    iMain.interpret("_exceptionHack.lastException = lastException")
+    exceptionHack.lastException
+  }
+
+  private def clearLastException(): Unit = {
+    iMain.directBind(
+      ExecutionExceptionName,
+      classOf[Throwable].getName,
+      null
+    )
+    exceptionHack.lastException = null
+  }
+
   protected def interpretMapToResultAndExecuteInfo(
     future: Future[(Results.Result, String)]
   ): Future[(Results.Result, Either[ExecuteOutput, ExecuteFailure])] = {
@@ -356,11 +374,12 @@ trait ScalaInterpreterSpecific extends 
SettingsProducerLike { this: ScalaInterpr
       case (Results.Incomplete, output) => (Results.Incomplete, Left(output))
       case (Results.Aborted, output)    => (Results.Aborted, Right(null))
       case (Results.Error, output)      =>
+        val ex = Some(retrieveLastException)
         (
           Results.Error,
           Right(
             interpretConstructExecuteError(
-              read(ExecutionExceptionName),
+              ex,
               output
             )
           )
@@ -375,16 +394,24 @@ trait ScalaInterpreterSpecific extends 
SettingsProducerLike { this: ScalaInterpr
     // Runtime error
     case Some(e) if e != null =>
       val ex = e.asInstanceOf[Throwable]
-      // Clear runtime error message
-      iMain.directBind(
-        ExecutionExceptionName,
-        classOf[Throwable].getName,
-        null
-      )
+      clearLastException()
+
+      // The scala REPL does a pretty good job of returning us a stack trace 
that is free from all the bits that the
+      // interpreter uses before it.
+      //
+      // The REPL emits its message as something like this, so trim off the 
first and last element
+      //
+      //    java.lang.ArithmeticException: / by zero
+      //    at failure(<console>:17)
+      //    at call_failure(<console>:19)
+      //    ... 40 elided
+
+      val formattedException = output.split("\n")
+
       ExecuteError(
         ex.getClass.getName,
         ex.getLocalizedMessage,
-        ex.getStackTrace.map(_.toString).toList
+        formattedException.slice(1, formattedException.size - 1).toList
       )
     // Compile time error, need to check internal reporter
     case _ =>
@@ -399,3 +426,14 @@ trait ScalaInterpreterSpecific extends 
SettingsProducerLike { this: ScalaInterpr
         ExecuteError("Unknown", "Unable to retrieve error!", List())
   }
 }
+
+/**
+  * Due to a bug in the scala interpreter under scala 2.11 (SI-8935) with 
IMain.valueOfTerm we can hack around it by
+  * binding an instance of ExceptionHack into iMain and interpret the 
"_exceptionHack.lastException = lastException".
+  * This makes it possible to extract the exception.
+  *
+  * TODO: Revisit this once Scala 2.12 is released.
+  */
+class ExceptionHack {
+  var lastException: Throwable = _
+}
\ No newline at end of file

Reply via email to