Skip to content

Commit

Permalink
decode variables
Browse files Browse the repository at this point in the history
  • Loading branch information
SaadAissa committed Jul 9, 2024
1 parent 542b2e9 commit f850011
Show file tree
Hide file tree
Showing 13 changed files with 129 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import com.microsoft.java.debug.core.adapter.{StackTraceProvider => JavaStackTra
import com.microsoft.java.debug.core.protocol.Requests.StepFilters
import com.sun.jdi.Location
import com.sun.jdi.Method
import com.sun.jdi.LocalVariable
import com.microsoft.java.debug.core.adapter.stacktrace.DecodedMethod
import ch.epfl.scala.debugadapter.DebugConfig
import com.microsoft.java.debug.core.adapter.stacktrace.DecodedVariable

class StackTraceProvider(
stepFilters: Seq[StepFilter],
Expand All @@ -22,14 +24,17 @@ class StackTraceProvider(
override def decode(method: Method): DecodedMethod =
decoder.map(_.decode(method)).getOrElse(JavaMethod(method, isGenerated = false))

override def decode(variable: LocalVariable): DecodedVariable =
decoder.map(_.decode(variable)).getOrElse(JavaVariable(variable))

override def skipOver(method: Method, filters: StepFilters): Boolean = {
try {
val skipOver = super.skipOver(method, filters) || stepFilters.exists(_.skipOver(method))
if (skipOver) logger.debug(s"Skipping over $method")
skipOver
} catch {
case cause: Throwable =>
throwOrWarn(s"Failed to determine if $method should be skipped over: ${cause.getMessage}")
throwOrWarn(s"Failed to determine if $method should be skipped over", cause)
false
}
}
Expand All @@ -43,7 +48,7 @@ class StackTraceProvider(
skipOut
} catch {
case cause: Throwable =>
throwOrWarn(s"Failed to determine if $method should be skipped out: ${cause.getMessage}")
throwOrWarn(s"Failed to determine if $method should be skipped out", cause)
false
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ch.epfl.scala.debugadapter.internal.stacktrace

import ch.epfl.scala.debugadapter.internal.Errors
import com.microsoft.java.debug.core.adapter.stacktrace.DecodedVariable

import java.lang.reflect.InvocationTargetException

class DecodedVariableBridge(instance: Any) extends DecodedVariable {

override def format(): String = invoke[String]("format")

private def invoke[T](variableName: String): T =
try instance.getClass().getField(variableName).get(instance).asInstanceOf[T]
catch {
case e: InvocationTargetException =>
throw Errors.frameDecodingFailure(e.getCause)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ch.epfl.scala.debugadapter.internal.stacktrace

import com.sun.jdi
import com.microsoft.java.debug.core.adapter.stacktrace.DecodedVariable

final case class JavaVariable(variable: jdi.LocalVariable) extends DecodedVariable {
def format: String = {
variable.name()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import ch.epfl.scala.debugadapter.internal.scalasig.ScalaSigPrinter
import ch.epfl.scala.debugadapter.internal.scalasig.*
import ch.epfl.scala.debugadapter.internal.stacktrace.JdiExtensions.*
import com.microsoft.java.debug.core.adapter.stacktrace.DecodedMethod
import com.microsoft.java.debug.core.adapter.stacktrace.DecodedVariable

import com.sun.jdi

import scala.jdk.CollectionConverters.*
Expand Down Expand Up @@ -76,6 +78,9 @@ class Scala2Decoder(
override def decode(method: jdi.Method): DecodedMethod =
JavaMethod(method, isGenerated = skipOver(method))

override def decode(variable: jdi.LocalVariable): DecodedVariable =
JavaVariable(variable)

private def containsLazyField(interface: jdi.InterfaceType, fieldName: String): Boolean = {
val fqcn = interface.name
sourceLookUp.getScalaSig(fqcn).exists(containsLazyField(_, fieldName))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ch.epfl.scala.debugadapter.internal.ByteCode
import ch.epfl.scala.debugadapter.internal.ThrowOrWarn
import ch.epfl.scala.debugadapter.internal.stacktrace.JdiExtensions.*
import com.microsoft.java.debug.core.adapter.stacktrace.DecodedMethod
import com.microsoft.java.debug.core.adapter.stacktrace.DecodedVariable
import com.sun.jdi

import scala.util.control.NonFatal
Expand Down Expand Up @@ -43,6 +44,14 @@ class Scala3Decoder(
JavaMethod(method, isGenerated = method.isBridge)
}

override def decode(variable: jdi.LocalVariable): DecodedVariable =
try bridge.decode(variable)
catch {
case NonFatal(e) =>
throwOrWarn(e)
JavaVariable(variable)
}

override def reload(): Unit =
bridge = Scala3DecoderBridge(debuggee, classLoader, logger, testMode)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,33 @@ import ch.epfl.scala.debugadapter.Java9OrAbove
import ch.epfl.scala.debugadapter.Logger
import ch.epfl.scala.debugadapter.internal.Errors
import com.microsoft.java.debug.core.adapter.stacktrace.DecodedMethod
import com.microsoft.java.debug.core.adapter.stacktrace.DecodedVariable
import com.sun.jdi

import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import java.lang.reflect.Field
import java.nio.file.Files
import java.nio.file.Path
import java.util.function.Consumer
import scala.jdk.CollectionConverters.*

private class Scala3DecoderBridge(
instance: Any,
decodeMethod: Method
decodeMethod: Method,
decodeVariable: Field
) {
def decode(method: jdi.Method): DecodedMethod =
try new DecodedMethodBridge(decodeMethod.invoke(instance, method))
catch {
case e: InvocationTargetException => throw Errors.frameDecodingFailure(e.getCause)
}

def decode(variable: jdi.LocalVariable): DecodedVariable =
try new DecodedVariableBridge(decodeMethod.invoke(instance, variable))
catch {
case e: InvocationTargetException => throw Errors.frameDecodingFailure(e.getCause)
}
}

private object Scala3DecoderBridge {
Expand All @@ -32,7 +41,8 @@ private object Scala3DecoderBridge {
val cls = classLoader.loadClass(className)
val instance = newInstance(debuggee, cls, logger, testMode)
val decodeMethod = cls.getMethod("decode", classOf[jdi.Method])
new Scala3DecoderBridge(instance, decodeMethod)
val decodeVariable = cls.getField("decode")
new Scala3DecoderBridge(instance, decodeMethod, decodeVariable)
}

private def newInstance(debuggee: Debuggee, decoderClass: Class[?], logger: Logger, testMode: Boolean) = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import ch.epfl.scala.debugadapter.internal.ScalaExtension.*
import com.sun.jdi
import com.microsoft.java.debug.core.adapter.stacktrace.DecodedMethod
import scala.util.Try
import com.microsoft.java.debug.core.adapter.stacktrace.DecodedVariable

trait ScalaDecoder extends StepFilter {
def decode(method: jdi.Method): DecodedMethod
def decode(variable: jdi.LocalVariable): DecodedVariable
def reload(): Unit
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ch.epfl.scala.debugadapter.internal

import ch.epfl.scala.decoder.DecodedVariable
import ch.epfl.scala.decoder.StackTraceFormatter
import tastyquery.Symbols.*
import tastyquery.Modifiers.*
import ch.epfl.scala.decoder.DecodedVariable.ValDef
import ch.epfl.scala.decoder.DecodedVariable.CapturedVariable
import ch.epfl.scala.decoder.DecodedVariable.This
import ch.epfl.scala.decoder.DecodedVariable.AnyValThis

class DecodedVariableBridge(variable: DecodedVariable):
def format: String = variable match
case v: ValDef => v.symbol.name.toString
case v: CapturedVariable => v.symbol.name.toString
case v: This => "this"
case v: AnyValThis => v.symbol.name.toString

// formatter.format(variable)
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import ch.epfl.scala.decoder.binary
import java.nio.file.Path
import java.util.function.Consumer
import ch.epfl.scala.decoder.*
import ch.epfl.scala.decoder.jdi.JdiMethod
import ch.epfl.scala.decoder.jdi.*

class Scala3DecoderBridge(
classEntries: Array[Path],
Expand All @@ -20,3 +20,8 @@ class Scala3DecoderBridge(

def decode(obj: com.sun.jdi.Method): DecodedMethodBridge =
new DecodedMethodBridge(decoder.decode(JdiMethod(obj)), formatter)

// TODO //
// methode qui decode des variables pr l'appeler dans le Scala3DecoderBridge apply
def decode(obj: com.sun.jdi.LocalVariable): DecodedVariableBridge =
new DecodedVariableBridge(decoder.decode(JdiVariable(obj)), formatter)
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,17 @@ object Evaluation {
}
}

final case class LocalVariable(name: String) extends DebugStep[Array[Variable]]
final case class LocalVariable(names: Seq[String]) extends DebugStep[Seq[Variable]]
object LocalVariable {
def inspect(variable: String)(p: Array[Variable] => Boolean)(implicit
def inspect(variables: String*)(p: Seq[Variable] => Boolean)(implicit
location: Location
): SingleStepAssert[Array[Variable]] =
new SingleStepAssert(new LocalVariable(variable), values => assert(p(values)))
): SingleStepAssert[Seq[Variable]] =
new SingleStepAssert(new LocalVariable(variables), values => assert(p(values)))

def apply(variables: String*)(expected: Seq[String])(implicit
location: Location
): SingleStepAssert[Seq[Variable]] =
new SingleStepAssert(new LocalVariable(variables), values => assertEquals(values.map(_.name).toSeq, expected))
}

object Outputed extends DebugStep[String] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,16 @@ trait DebugTest extends CommonUtils {
}
}

def inspect(variable: LocalVariable, assertion: Array[Variable] => Unit): Unit = {
def inspect(variable: LocalVariable, assertion: Seq[Variable] => Unit): Unit = {
val values = for {
localScopeRef <- client.scopes(state.topFrame.id).find(_.name == "Local").map(_.variablesReference)
variableRef <- client.variables(localScopeRef).find(_.name == variable.name).map(_.variablesReference)
} yield client.variables(variableRef)
assertion(values.getOrElse(throw new NoSuchElementException(variable.name)))
localScopeRef <- client.scopes(state.topFrame.id).find(_.name == "Local").map(_.variablesReference).toSeq
variableRefOpt: Option[Int] = variable.names.foldLeft(Option(localScopeRef))((ref, name) =>
ref.toSeq.flatMap(x => client.variables(x)).find(_.name == name).map(_.variablesReference)
)
variableRef <- variableRefOpt.toSeq
v <- client.variables(variableRef)
} yield v
assertion(values)
}

def stop(): Array[StackFrame] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,33 @@ class LocalVariableTests extends DebugTestSuite {
| }
|}""".stripMargin
implicit val debuggee: TestingDebuggee = TestingDebuggee.mainClass(source, "example.Main", scalaVersion)
val regexp = """array\(\d+\)""".r
check(
Breakpoint(6),
LocalVariable.inspect("array")(
_.forall(v => """array\(\d+\)""".r.unapplySeq(v.evaluateName).isDefined)
)
)
}

test("simple local variables") {
val source =
"""|package example
|
|object Main {
| def main(args: Array[String]): Unit = {
| val x = 1
| println(x)
| val y = "2"
| println(y)
| }
|}""".stripMargin
implicit val debuggee: TestingDebuggee = TestingDebuggee.mainClass(source, "example.Main", scalaVersion)
check(
Breakpoint(6),
LocalVariable()(Seq("args", "x", "this")),
Breakpoint(8),
LocalVariable()(Seq("args", "x", "y", "this")),
LocalVariable("y", "CASE_INSENSITIVE_ORDER")(Seq("serialVersionUID"))
)
}
}

0 comments on commit f850011

Please sign in to comment.