Skip to content

Commit

Permalink
Unwrap invocation exception in runtime evaluation
Browse files Browse the repository at this point in the history
  • Loading branch information
adpi2 committed Feb 7, 2024
1 parent f1d864f commit 2f4c38f
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ private[internal] class EvaluationProvider(
}

private def evaluate(expression: PreparedExpression, frame: JdiFrame): Try[Value] = evaluationBlock {
expression match {
val result = expression match {
case logMessage: PlainLogMessage => MessageLogger.log(logMessage, frame)
case expr: RuntimeExpression => runtimeEvaluator.evaluate(expr, frame)
case expr: CompiledExpression =>
Expand All @@ -179,6 +179,8 @@ private[internal] class EvaluationProvider(
compiledExpression <- scalaEvaluator.evaluate(expr)
} yield compiledExpression
}
// if evaluation throws an exception, we return that exception as the result
result.recover { case MethodInvocationFailed(_, Some(exception)) => exception.value }
}

private def completeFuture[T](result: Try[T], thread: ThreadReference): CompletableFuture[T] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ package ch.epfl.scala.debugadapter.internal.evaluator
private[internal] case class MethodInvocationFailed(
message: String,
remoteException: Option[JdiObject]
) extends Exception(message)
) extends Exception(message) {
override def toString: String = s"MethodInvocationFailed: $message"
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,50 +132,44 @@ object RuntimePrimitiveOps {
fractional: Fractional[T]
): Safe[JdiValue] = {
import Fractional.Implicits._
val result: Option[AnyVal] = this match {
case Plus => Some(x + y)
case Minus => Some(x - y)
case Times => Some(x * y)
case Div if y != 0 => Some(x / y)
case Modulo if y != 0 =>
x match {
case d: Double => Some(d % y.asInstanceOf[Double])
case f: Float => Some(f % y.asInstanceOf[Float])
}
case Div | Modulo => None
case Less => Some(fractional.lt(x, y))
case LessOrEqual => Some(fractional.lteq(x, y))
case Greater => Some(fractional.gt(x, y))
case GreaterOrEqual => Some(fractional.gteq(x, y))
}

result match {
case Some(value) => Safe.successful(clsLoader.mirrorOfAnyVal(value))
case None => Safe.failed(MethodInvocationFailed("Division by zero", None))
}
Safe(this)
.map {
case Plus => x + y
case Minus => x - y
case Times => x * y
case Div => x / y
case Modulo =>
x match {
case d: Double => d % y.asInstanceOf[Double]
case f: Float => f % y.asInstanceOf[Float]
}
case Less => fractional.lt(x, y)
case LessOrEqual => fractional.lteq(x, y)
case Greater => fractional.gt(x, y)
case GreaterOrEqual => fractional.gteq(x, y)
}
.map(clsLoader.mirrorOfAnyVal)
.recoverWith { case e: ArithmeticException => Safe.failed(MethodInvocationFailed(e.getMessage, None)) }
}

private def computeIntegral[T <: AnyVal](x: T, y: T, clsLoader: JdiClassLoader)(implicit
integral: Integral[T]
): Safe[JdiValue] = {
import Integral.Implicits._
val result: Option[AnyVal] = this match {
case Plus => Some(x + y)
case Minus => Some(x - y)
case Times => Some(x * y)
case Div if y != 0 => Some(x / y)
case Modulo if y != 0 => Some(x % y)
case Div | Modulo => None
case Less => Some(integral.lt(x, y))
case LessOrEqual => Some(integral.lteq(x, y))
case Greater => Some(integral.gt(x, y))
case GreaterOrEqual => Some(integral.gteq(x, y))
}

result match {
case Some(value) => Safe.successful(clsLoader.mirrorOfAnyVal(value))
case None => Safe.failed(MethodInvocationFailed("Division by zero", None))
}
Safe(this)
.map {
case Plus => x + y
case Minus => x - y
case Times => x * y
case Div => x / y
case Modulo => x % y
case Less => integral.lt(x, y)
case LessOrEqual => integral.lteq(x, y)
case Greater => integral.gt(x, y)
case GreaterOrEqual => integral.gteq(x, y)
}
.map(clsLoader.mirrorOfAnyVal)
.recoverWith { case e: ArithmeticException => Safe.failed(MethodInvocationFailed(e.getMessage, None)) }
}

def evaluate(lhs: JdiValue, rhs: JdiValue, loader: JdiClassLoader) = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,8 @@ private[internal] class ScalaEvaluator(
evaluatedValue.getResult
}

private def evaluateExpression(expressionInstance: JdiObject): Safe[JdiValue] = {
expressionInstance
.invoke("evaluate", List())
.recover {
// if evaluation throws an exception, we return that exception as the result
case MethodInvocationFailed(msg, Some(exception)) => exception
}
}
private def evaluateExpression(expressionInstance: JdiObject): Safe[JdiValue] =
expressionInstance.invoke("evaluate", List())

/**
* In order to load the previously compiled Expression class, we need to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,7 @@ abstract class RuntimeEvaluatorTests(val scalaVersion: ScalaVersion) extends Deb
Evaluation.success("f1.foo_v2_apply.bar(42)", "hello 42"),
Evaluation.success("inner(42).x", 42),
Evaluation.success("list(0).toString()", "0"),
Evaluation.failed("list(1)", "java.lang.IndexOutOfBoundsException: 1")
Evaluation.success("list(1)", "java.lang.IndexOutOfBoundsException: 1")
)
)
}
Expand Down Expand Up @@ -939,7 +939,7 @@ abstract class RuntimeEvaluatorTests(val scalaVersion: ScalaVersion) extends Deb
Evaluation.success("list(2).toString", "3"),
Evaluation.success("list(new Integer(2)).toString", "3"),
Evaluation.successOrIgnore("list(new Character('\u0000')).toString", "1", true),
Evaluation.failed("list(3)", "java.lang.IndexOutOfBoundsException"),
Evaluation.success("list(3)", "java.lang.IndexOutOfBoundsException"),
Evaluation.success("map(1)", "one"),
Evaluation.success("map(2)", "two"),
Evaluation.success("map(new Integer(2))", "two"),
Expand Down

0 comments on commit 2f4c38f

Please sign in to comment.