From 35557c83031ffa6b427696020d516c6aea228779 Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Wed, 7 Feb 2024 14:09:17 +0100 Subject: [PATCH 1/3] Unwrap invocation exception in runtime evaluation --- .../internal/EvaluationProvider.scala | 4 +- .../evaluator/MethodInvocationFailed.scala | 4 +- .../evaluator/RuntimePrimitiveOps.scala | 70 +++++++++---------- .../internal/evaluator/ScalaEvaluator.scala | 10 +-- .../internal/RuntimeEvaluatorTests.scala | 10 +-- 5 files changed, 45 insertions(+), 53 deletions(-) diff --git a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/EvaluationProvider.scala b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/EvaluationProvider.scala index 7786e0c2b..a0e13c5be 100644 --- a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/EvaluationProvider.scala +++ b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/EvaluationProvider.scala @@ -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 => @@ -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] = { diff --git a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/MethodInvocationFailed.scala b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/MethodInvocationFailed.scala index 5ce0cf8ce..157e068d8 100644 --- a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/MethodInvocationFailed.scala +++ b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/MethodInvocationFailed.scala @@ -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" +} diff --git a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimePrimitiveOps.scala b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimePrimitiveOps.scala index bbf3c24fe..f58782475 100644 --- a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimePrimitiveOps.scala +++ b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimePrimitiveOps.scala @@ -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) = { diff --git a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/ScalaEvaluator.scala b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/ScalaEvaluator.scala index ee7e79d39..1d8e432a6 100644 --- a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/ScalaEvaluator.scala +++ b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/ScalaEvaluator.scala @@ -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 diff --git a/modules/tests/src/test/scala/ch/epfl/scala/debugadapter/internal/RuntimeEvaluatorTests.scala b/modules/tests/src/test/scala/ch/epfl/scala/debugadapter/internal/RuntimeEvaluatorTests.scala index 872a27206..85a69f091 100644 --- a/modules/tests/src/test/scala/ch/epfl/scala/debugadapter/internal/RuntimeEvaluatorTests.scala +++ b/modules/tests/src/test/scala/ch/epfl/scala/debugadapter/internal/RuntimeEvaluatorTests.scala @@ -3,6 +3,7 @@ package ch.epfl.scala.debugadapter.internal import ch.epfl.scala.debugadapter.testfmk.* import ch.epfl.scala.debugadapter.ScalaVersion import ch.epfl.scala.debugadapter.DebugConfig +import java.{util => ju} class Scala212RuntimeEvaluatorTests extends RuntimeEvaluatorTests(ScalaVersion.`2.12`) { test("Should access to wrapping 'object' methods") { @@ -887,7 +888,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)", new IndexOutOfBoundsException("1")) ) ) } @@ -939,13 +940,12 @@ 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)", new IndexOutOfBoundsException("3")), Evaluation.success("map(1)", "one"), Evaluation.success("map(2)", "two"), Evaluation.success("map(new Integer(2))", "two"), Evaluation.successOrIgnore("map(new Character('\u0000'))", "one", true), - // todo distinguish evaluation exception from MethodInvocationFailed - Evaluation.failed("map(3)", "key not found: 3"), + Evaluation.success("map(3)", new ju.NoSuchElementException("key not found: 3")), Evaluation.success("set(1)", true), Evaluation.success("set(2)", true), Evaluation.success("set(new Integer(2))", true), @@ -955,7 +955,7 @@ abstract class RuntimeEvaluatorTests(val scalaVersion: ScalaVersion) extends Deb Evaluation.success("seq(2).toString", "3"), Evaluation.success("seq(new Integer(2)).toString", "3"), Evaluation.successOrIgnore("seq(new Character('\u0000')).toString", "1", true), - Evaluation.failed("seq(3)", "java.lang.IndexOutOfBoundsException: 3"), + Evaluation.success("seq(3)", new IndexOutOfBoundsException("3")), Evaluation.success("vector(0).toString", "1"), Evaluation.success("vector(2).toString", "3"), Evaluation.success("vector(new Integer(2)).toString", "3"), From 039c04c52ad3f744b79351ca786e43d0d5652ea9 Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Wed, 7 Feb 2024 14:46:01 +0100 Subject: [PATCH 2/3] Rename MethodInvocationFailed to RuntimeException --- .../scala/debugadapter/internal/EvaluationProvider.scala | 4 ++-- .../scala/debugadapter/internal/evaluator/JdiArray.scala | 5 ++++- .../scala/debugadapter/internal/evaluator/JdiClass.scala | 4 ++-- .../debugadapter/internal/evaluator/JdiObject.scala | 9 ++++----- .../internal/evaluator/RuntimeEvaluation.scala | 3 ++- ...thodInvocationFailed.scala => RuntimeException.scala} | 4 ++-- .../internal/evaluator/RuntimePrimitiveOps.scala | 4 ++-- .../scala/tools/nsc/ExpressionCompilerBridge.scala | 2 +- .../scala-3/dotty/tools/dotc/ExpressionContext.scala | 4 ++-- .../dotty/tools/dotc/evaluation/ExtractExpression.scala | 3 ++- .../debugadapter/internal/RuntimeEvaluatorTests.scala | 2 +- 11 files changed, 24 insertions(+), 20 deletions(-) rename modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/{MethodInvocationFailed.scala => RuntimeException.scala} (54%) diff --git a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/EvaluationProvider.scala b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/EvaluationProvider.scala index a0e13c5be..4bac6867c 100644 --- a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/EvaluationProvider.scala +++ b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/EvaluationProvider.scala @@ -94,7 +94,7 @@ private[internal] class EvaluationProvider( .invoke(methodName, methodSignature, wrappedArgs) .recover { // if invocation throws an exception, we return that exception as the result - case MethodInvocationFailed(msg, Some(exception)) => exception + case RuntimeException(msg, Some(exception)) => exception } .map(_.value) } @@ -180,7 +180,7 @@ private[internal] class EvaluationProvider( } yield compiledExpression } // if evaluation throws an exception, we return that exception as the result - result.recover { case MethodInvocationFailed(_, Some(exception)) => exception.value } + result.recover { case RuntimeException(_, Some(exception)) => exception.value } } private def completeFuture[T](result: Try[T], thread: ThreadReference): CompletableFuture[T] = { diff --git a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiArray.scala b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiArray.scala index f62380c45..92cbfb19c 100644 --- a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiArray.scala +++ b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiArray.scala @@ -14,7 +14,10 @@ class JdiArray(arrayRef: ArrayReference, thread: ThreadReference) extends JdiObj def getValues: Seq[JdiValue] = arrayRef.getValues.asScala.toSeq.map(JdiValue(_, thread)) - def getValue(i: Int): JdiValue = JdiValue(arrayRef.getValue(i), thread) + def getValue(i: Int): Safe[JdiValue] = + Safe(JdiValue(arrayRef.getValue(i), thread)).recoverWith { case e: IndexOutOfBoundsException => + Safe.failed(RuntimeException(e.getMessage, None)) + } } object JdiArray { diff --git a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiClass.scala b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiClass.scala index 4ccd311db..0059bfaf9 100644 --- a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiClass.scala +++ b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiClass.scala @@ -28,7 +28,7 @@ private[internal] class JdiClass( for { _ <- prepareMethod(ctr) instance <- Safe(cls.newInstance(thread, ctr, args.map(_.value).asJava, ObjectReference.INVOKE_SINGLE_THREADED)) - .recoverWith(wrapInvocationException(thread)) + .recoverWith(wrapInvocationException) } yield JdiObject(instance, thread) // Load the argument types of the method to avoid ClassNotLoadedException @@ -64,7 +64,7 @@ private[internal] class JdiClass( def invokeStatic(method: Method, args: Seq[JdiValue]): Safe[JdiValue] = Safe(cls.invokeMethod(thread, method, args.map(_.value).asJava, ObjectReference.INVOKE_SINGLE_THREADED)) .map(JdiValue(_, thread)) - .recoverWith(wrapInvocationException(thread)) + .recoverWith(wrapInvocationException) } object JdiClass { diff --git a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiObject.scala b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiObject.scala index 707db29b8..998b86c0b 100644 --- a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiObject.scala +++ b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiObject.scala @@ -29,18 +29,17 @@ private[evaluator] class JdiObject( def classObject: JdiClass = JdiClass(reference.referenceType, thread) def classLoader: JdiClassLoader = JdiClassLoader(reference.referenceType.classLoader, thread) - def invoke(method: Method, args: Seq[JdiValue]): Safe[JdiValue] = { + def invoke(method: Method, args: Seq[JdiValue]): Safe[JdiValue] = Safe(reference.invokeMethod(thread, method, args.map(_.value).asJava, ObjectReference.INVOKE_SINGLE_THREADED)) .map(JdiValue(_, thread)) - .recoverWith(wrapInvocationException(thread)) - } + .recoverWith(wrapInvocationException) - protected def wrapInvocationException(thread: ThreadReference): PartialFunction[Throwable, Safe[Nothing]] = { + protected val wrapInvocationException: PartialFunction[Throwable, Safe[Nothing]] = { case invocationException: InvocationException => for { exception <- Safe(invocationException.exception).map(JdiObject(_, thread)) message <- exception.invoke("toString", List()).map(_.asString.stringValue).recover { case _ => "" } - } yield throw new MethodInvocationFailed(message, Some(exception)) + } yield throw new RuntimeException(message, Some(exception)) } // we use a Seq instead of a Map because the ScalaEvaluator rely on the order of the fields diff --git a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimeEvaluation.scala b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimeEvaluation.scala index 55639be82..54b847e0f 100644 --- a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimeEvaluation.scala +++ b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimeEvaluation.scala @@ -77,7 +77,8 @@ class RuntimeEvaluation(frame: JdiFrame, logger: Logger) { for { array <- eval(tree.array) index <- eval(tree.index).flatMap(_.unboxIfPrimitive).flatMap(_.toInt) - } yield array.asArray.getValue(index) + value <- array.asArray.getValue(index) + } yield value private def evaluateIf(tree: If): Safe[JdiValue] = for { diff --git a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/MethodInvocationFailed.scala b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimeException.scala similarity index 54% rename from modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/MethodInvocationFailed.scala rename to modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimeException.scala index 157e068d8..698d9874b 100644 --- a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/MethodInvocationFailed.scala +++ b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimeException.scala @@ -1,8 +1,8 @@ package ch.epfl.scala.debugadapter.internal.evaluator -private[internal] case class MethodInvocationFailed( +private[internal] case class RuntimeException( message: String, remoteException: Option[JdiObject] ) extends Exception(message) { - override def toString: String = s"MethodInvocationFailed: $message" + override def toString: String = s"RuntimeException: $message" } diff --git a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimePrimitiveOps.scala b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimePrimitiveOps.scala index f58782475..687f1cdd8 100644 --- a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimePrimitiveOps.scala +++ b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimePrimitiveOps.scala @@ -149,7 +149,7 @@ object RuntimePrimitiveOps { case GreaterOrEqual => fractional.gteq(x, y) } .map(clsLoader.mirrorOfAnyVal) - .recoverWith { case e: ArithmeticException => Safe.failed(MethodInvocationFailed(e.getMessage, None)) } + .recoverWith { case e: ArithmeticException => Safe.failed(RuntimeException(e.getMessage, None)) } } private def computeIntegral[T <: AnyVal](x: T, y: T, clsLoader: JdiClassLoader)(implicit @@ -169,7 +169,7 @@ object RuntimePrimitiveOps { case GreaterOrEqual => integral.gteq(x, y) } .map(clsLoader.mirrorOfAnyVal) - .recoverWith { case e: ArithmeticException => Safe.failed(MethodInvocationFailed(e.getMessage, None)) } + .recoverWith { case e: ArithmeticException => Safe.failed(RuntimeException(e.getMessage, None)) } } def evaluate(lhs: JdiValue, rhs: JdiValue, loader: JdiClassLoader) = { diff --git a/modules/expression-compiler/src/main/scala-2/scala/tools/nsc/ExpressionCompilerBridge.scala b/modules/expression-compiler/src/main/scala-2/scala/tools/nsc/ExpressionCompilerBridge.scala index 0ede2411b..a04fa5aa7 100644 --- a/modules/expression-compiler/src/main/scala-2/scala/tools/nsc/ExpressionCompilerBridge.scala +++ b/modules/expression-compiler/src/main/scala-2/scala/tools/nsc/ExpressionCompilerBridge.scala @@ -31,7 +31,7 @@ final class ExpressionCompilerBridge { ) ++ options :+ sourceFile.toString val command = new CompilerCommand(args, errorConsumer.accept(_)) - val reporter = new StoreReporter() // cannot fix because of Scala 2.10 + val reporter = new StoreReporter() // cannot fix because of Scala 2.12 val global = new ExpressionGlobal( command.settings, reporter, diff --git a/modules/expression-compiler/src/main/scala-3/dotty/tools/dotc/ExpressionContext.scala b/modules/expression-compiler/src/main/scala-3/dotty/tools/dotc/ExpressionContext.scala index fbac778c9..8067b2469 100644 --- a/modules/expression-compiler/src/main/scala-3/dotty/tools/dotc/ExpressionContext.scala +++ b/modules/expression-compiler/src/main/scala-3/dotty/tools/dotc/ExpressionContext.scala @@ -19,10 +19,10 @@ class ExpressionContext( val expressionTermName: TermName = termName(uniqueName.toLowerCase.toString) val expressionClassName: TypeName = typeName(uniqueName) - var expressionSymbol: TermSymbol = _ + var expressionSymbol: TermSymbol = null // all classes and def in the chain of owners of the expression from local to global // we store them to resolve the captured variables - var classOwners: Seq[ClassSymbol] = _ + var classOwners: Seq[ClassSymbol] = null var capturingMethod: Option[TermSymbol] = None def store(exprSym: Symbol)(using Context): Unit = diff --git a/modules/expression-compiler/src/main/scala-3/dotty/tools/dotc/evaluation/ExtractExpression.scala b/modules/expression-compiler/src/main/scala-3/dotty/tools/dotc/evaluation/ExtractExpression.scala index 850b0be55..224745701 100644 --- a/modules/expression-compiler/src/main/scala-3/dotty/tools/dotc/evaluation/ExtractExpression.scala +++ b/modules/expression-compiler/src/main/scala-3/dotty/tools/dotc/evaluation/ExtractExpression.scala @@ -16,6 +16,7 @@ import dotty.tools.dotc.transform.MacroTransform import dotty.tools.dotc.core.Phases.* import dotty.tools.dotc.report import dotty.tools.dotc.util.SrcPos +import scala.annotation.nowarn class ExtractExpression(using exprCtx: ExpressionContext) extends MacroTransform with DenotTransformer: override def phaseName: String = ExtractExpression.name @@ -39,7 +40,7 @@ class ExtractExpression(using exprCtx: ExpressionContext) extends MacroTransform override protected def newTransformer(using Context): Transformer = new Transformer: - var expressionTree: Tree = _ + var expressionTree: Tree = null override def transform(tree: Tree)(using Context): Tree = tree match case PackageDef(pid, stats) => diff --git a/modules/tests/src/test/scala/ch/epfl/scala/debugadapter/internal/RuntimeEvaluatorTests.scala b/modules/tests/src/test/scala/ch/epfl/scala/debugadapter/internal/RuntimeEvaluatorTests.scala index 85a69f091..3187c8a54 100644 --- a/modules/tests/src/test/scala/ch/epfl/scala/debugadapter/internal/RuntimeEvaluatorTests.scala +++ b/modules/tests/src/test/scala/ch/epfl/scala/debugadapter/internal/RuntimeEvaluatorTests.scala @@ -921,7 +921,7 @@ abstract class RuntimeEvaluatorTests(val scalaVersion: ScalaVersion) extends Deb Evaluation.success("arr(by)", 3), Evaluation.success("arr(new Integer(2))", 3), Evaluation.success("arr(new Character('\u0000'))", 1), - Evaluation.failed("arr(3)", "java.lang.IndexOutOfBoundsException"), + Evaluation.failed("arr(3)", "Invalid array range: 3 to 3."), Evaluation.success("test(arr)", "1,2,3"), Evaluation.failed( "test(Array(Test(1), Test(2), Test(3)))", From 2dbcaf8037d360efde1835dd705d50e62d11fa3d Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Wed, 7 Feb 2024 15:26:12 +0100 Subject: [PATCH 3/3] minor changes --- .../debugadapter/internal/evaluator/JdiObject.scala | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiObject.scala b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiObject.scala index 998b86c0b..6ac02a445 100644 --- a/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiObject.scala +++ b/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/JdiObject.scala @@ -8,13 +8,9 @@ private[evaluator] class JdiObject( val reference: ObjectReference, thread: ThreadReference ) extends JdiValue(reference, thread) { - def getField(field: Field): JdiValue = - JdiValue(reference.getValue(field), thread) + def getField(field: Field): JdiValue = JdiValue(reference.getValue(field), thread) - def getField(name: String): JdiValue = { - val field = reference.referenceType.fieldByName(name) - JdiValue(reference.getValue(field), thread) - } + def getField(name: String): JdiValue = getField(reference.referenceType.fieldByName(name)) def invoke(methodName: String, args: Seq[JdiValue]): Safe[JdiValue] = { val m = reference.referenceType.methodsByName(methodName).get(0) @@ -44,8 +40,7 @@ private[evaluator] class JdiObject( // we use a Seq instead of a Map because the ScalaEvaluator rely on the order of the fields def fields: Seq[(String, JdiValue)] = - reference.referenceType.fields.asScala.toSeq - .map(f => (f.name, JdiValue(reference.getValue(f), thread))) + reference.referenceType.fields.asScala.toSeq.map(f => (f.name, getField(f))) } private[internal] object JdiObject {