Skip to content

Commit 51f660e

Browse files
committed
Improve handling of exceptions in script execution #12
1 parent 082beda commit 51f660e

File tree

1 file changed

+47
-16
lines changed

1 file changed

+47
-16
lines changed

scala-console/src/main/scala-3/dotty/tools/repl/SCIMain.scala

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import dotty.tools.dotc.core.StdNames.str
44

55
import java.io.PrintStream
66
import java.lang.reflect.Method
7+
import scala.annotation.tailrec
78
import scala.util.control.NonFatal
9+
import scala.util.{Failure, Success, Try}
810

911
/** Interprets Scala code, based on `dotty.tools.repl.ScriptEngine` */
1012
class SCIMain(out: PrintStream, loader: ClassLoader) {
13+
import SCIMain.*
1114

1215
private val driver =
1316
new ReplDriver(
@@ -21,32 +24,41 @@ class SCIMain(out: PrintStream, loader: ClassLoader) {
2124
out,
2225
Some(loader)
2326
)
24-
25-
private var state = driver.initialState
2627
private val rendering = new Rendering(Some(getClass.getClassLoader))
28+
private var state = driver.initialState
2729

2830
def bind(tup: (String, Any)): Unit =
2931
state = driver.bind(tup._1, tup._2)(using state)
3032

3133
def interpret(line: String): SCResults = {
32-
val methodOpt: Option[Method] =
33-
try {
34+
// Parse script
35+
val methodTry: Try[Option[Method]] =
36+
Try {
3437
evalToMethod(line)
35-
} catch {
36-
case NonFatal(ex) =>
37-
// ex.printStackTrace()
38-
// ex.printStackTrace(out)
39-
return SCResults.Error
4038
}
4139

42-
val valueOpt: Option[Any] = methodOpt.map(_.invoke(null))
40+
// Execute parsed script
41+
val result: Try[Unit] =
42+
for methodOpt <- methodTry yield {
43+
for method <- methodOpt do
44+
val value = method.invoke(null)
45+
if value != () then
46+
out.println(s"${method.getName}: $value")
47+
}
48+
49+
// Interpret script execution result
50+
result match
51+
case Success(_) =>
52+
SCResults.Success
53+
case Failure(ex) =>
54+
if wasCausedByImageJMacroAbort(ex) then
55+
out.println(s"WARNING: Detected ImageJ's \"$IMAGEJ_MACRO_CANCELED\" request. Stopping script execution.")
56+
SCResults.Success
57+
else
58+
// ex.printStackTrace()
59+
// ex.printStackTrace(out)
60+
SCResults.Error
4361

44-
val value = valueOpt.getOrElse(())
45-
val methodName = methodOpt.fold("")(_.getName)
46-
if (methodOpt.isDefined && valueOpt.isDefined && !valueOpt.contains(())) {
47-
out.println(s"$methodName: $value")
48-
}
49-
SCResults.Success
5062
}
5163

5264
private def evalToMethod(script: String): Option[Method] = {
@@ -59,4 +71,23 @@ class SCIMain(out: PrintStream, loader: ClassLoader) {
5971
.find(_.getName == s"${str.REPL_RES_PREFIX}$vid")
6072
}
6173

74+
/**
75+
* Check if exception has a signature of an exception thrown by ImageJ to indicate that macro cancellation.
76+
* @param t exception to test
77+
* @return `true` is the exception matches the ImageJ' macro abort exception.
78+
*/
79+
@tailrec
80+
private final def wasCausedByImageJMacroAbort(t: Throwable): Boolean = {
81+
if t == null then
82+
false
83+
else if t.isInstanceOf[RuntimeException] & t.getMessage == IMAGEJ_MACRO_CANCELED then
84+
true
85+
else
86+
wasCausedByImageJMacroAbort(t.getCause)
87+
}
88+
6289
}
90+
91+
object SCIMain:
92+
private val IMAGEJ_MACRO_CANCELED = "Macro canceled"
93+
end SCIMain

0 commit comments

Comments
 (0)