Skip to content

Commit

Permalink
fixes #496
Browse files Browse the repository at this point in the history
  • Loading branch information
iusildra committed Jul 12, 2023
1 parent fcd6297 commit d20bdbe
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ class RuntimeDefaultEvaluator(val frame: JdiFrame, implicit val logger: Logger)
case array: ArrayElemTree => evaluateArrayElement(array)
case branching: IfTree => evaluateIf(branching)
case staticMethod: StaticMethodTree => invokeStatic(staticMethod)
case outer: OuterTree => evaluateOuter(outer)
case UnitTree => Safe(JdiValue(frame.thread.virtualMachine.mirrorOfVoid, frame.thread))
}

Expand All @@ -36,16 +35,6 @@ class RuntimeDefaultEvaluator(val frame: JdiFrame, implicit val logger: Logger)
result <- loader.mirrorOfLiteral(value)
} yield result

/* -------------------------------------------------------------------------- */
/* Outer evaluation */
/* -------------------------------------------------------------------------- */
def evaluateOuter(tree: OuterTree): Safe[JdiValue] =
tree match {
case OuterModuleTree(module) => evaluateModule(module)
case outerClass: OuterClassTree =>
eval(outerClass.inner).map(_.asObject.getField("$outer"))
}

/* -------------------------------------------------------------------------- */
/* Field evaluation */
/* -------------------------------------------------------------------------- */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import scala.util.Success

import ch.epfl.scala.debugadapter.internal.SourceLookUpProvider

import scala.jdk.CollectionConverters.*

case class Call(fun: Term, argClause: Term.ArgClause)
case class PreparedCall(qual: Validation[RuntimeTree], name: String)

Expand Down Expand Up @@ -91,16 +93,24 @@ class RuntimeDefaultValidator(val frame: JdiFrame, val sourceLookUp: SourceLookU
.filter(_.`type`.name() != "scala.Function0", runtimeFatal = true)
.map(v => LocalVarTree(name, v.`type`))

// We might sometimes need to access a 'private' attribute of a class
private def fieldLookup(name: String, ref: ReferenceType) =
Option(ref.fieldByName(name))
.orElse { ref.visibleFields().asScala.find(_.name().endsWith("$" + name)) }

def fieldTreeByName(
of: Validation[RuntimeTree],
name: String
): Validation[RuntimeEvaluableTree] =
for {
ref <- extractReferenceType(of)
field <- Validation(ref.fieldByName(name))
_ = loadClassOnNeed(field)
fieldTree <- toStaticIfNeeded(field, of.get)
} yield fieldTree
of match {
case ReferenceTree(ref) =>
for {
field <- Validation.fromOption { fieldLookup(name, ref) }
_ = loadClassOnNeed(field)
fieldTree <- toStaticIfNeeded(field, of.get)
} yield fieldTree
case _ => Recoverable(s"Cannot access field $name from non reference type ${of.get.`type`.name()}")
}

private def inCompanion(name: Option[String], moduleName: String) = name
.filter(_.endsWith("$"))
Expand Down Expand Up @@ -247,13 +257,8 @@ class RuntimeDefaultValidator(val frame: JdiFrame, val sourceLookUp: SourceLookU
/* -------------------------------------------------------------------------- */
/* Looking for $outer */
/* -------------------------------------------------------------------------- */
def validateOuter(tree: RuntimeTree): Validation[RuntimeEvaluableTree] = {
for {
ref <- extractReferenceType(tree)
outer <- outerLookup(ref)
outerTree <- OuterTree(tree, outer)
} yield outerTree
}
def validateOuter(tree: RuntimeTree): Validation[RuntimeEvaluableTree] =
outerLookup(tree)

/* -------------------------------------------------------------------------- */
/* Flow control validation */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ trait RuntimeEvaluator {

def evaluateLiteral(tree: LiteralTree): Safe[JdiValue]

def evaluateOuter(tree: OuterTree): Safe[JdiValue]

def evaluateField(tree: InstanceFieldTree): Safe[JdiValue]

def evaluateStaticField(tree: StaticFieldTree): Safe[JdiValue]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,18 +306,20 @@ private[evaluator] class RuntimeEvaluationHelpers(frame: JdiFrame, sourceLookup:
}

// ! May not be correct when dealing with an object inside a class
def outerLookup(ref: ReferenceType): Validation[Type] =
Validation(ref.fieldByName("$outer"))
.map(_.`type`())
.orElse {
for {
outerName <- Validation.fromOption(removeLastInnerTypeFromFQCN(ref.name()))
outer <- loadClass(outerName + "$").map(_.`type`) match {
case Valid(Module(mod)) => Valid(mod)
case _ => Recoverable(s"Cannot find $$outer for $ref")
def outerLookup(tree: RuntimeTree): Validation[RuntimeEvaluableTree] =
tree match {
case ReferenceTree(ref) =>
Validation(ref.fieldByName("$outer"))
.flatMap(toStaticIfNeeded(_, tree))
.orElse {
removeLastInnerTypeFromFQCN(tree.`type`.name())
.map(name => loadClass(name + "$")) match {
case Some(Valid(Module(mod))) => Valid(mod)
case res => Recoverable(s"Cannot find $$outer for ${tree.`type`.name()}}")
}
}
} yield outer
}
case _ => Recoverable(s"Cannot find $$outer for non-reference type ${tree.`type`.name()}}")
}

def searchAllClassesFor(name: String, in: Option[String]): Validation[ClassTree] = {
def declaringTypeName = frame.current().location().declaringType().name()
Expand Down Expand Up @@ -369,16 +371,6 @@ private[evaluator] class RuntimeEvaluationHelpers(frame: JdiFrame, sourceLookup:
/* -------------------------------------------------------------------------- */
/* Useful patterns */
/* -------------------------------------------------------------------------- */
/* Extract reference if there is */
def extractReferenceType(tree: Validation[RuntimeTree]): Validation[ReferenceType] =
tree.flatMap(extractReferenceType)

def extractReferenceType(tree: RuntimeTree): Validation[ReferenceType] =
tree match {
case ReferenceTree(ref) => Valid(ref)
case t => illegalAccess(t, "ReferenceType")
}

/* Standardize method calls */
def extractCall(apply: Stat): Call =
apply match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ protected[internal] object RuntimeEvaluatorExtractors {
def unapply(cls: JdiClass): Option[ClassType] = unapply(cls.cls)

def unapply(tree: RuntimeTree): Option[RuntimeEvaluableTree] =
unapply(tree.`type`).map(_ => tree.asInstanceOf[RuntimeEvaluableTree])
tree match {
case cls: ClassTree => unapply(cls.`type`).map(TopLevelModuleTree(_))
case tree: RuntimeEvaluableTree => unapply(tree.`type`).map(_ => tree)
}
}

object ModuleCall {
Expand All @@ -41,8 +44,6 @@ protected[internal] object RuntimeEvaluatorExtractors {
tree match {
case mt: NestedModuleTree => unapply(mt.init.qual)
case ft: InstanceFieldTree => unapply(ft.qual)
case oct: OuterClassTree => unapply(oct.inner)
case OuterModuleTree(module) => unapply(module)
case IfTree(p, t, f, _) => unapply(p) || unapply(t) || unapply(f)
case _: MethodTree | _: NewInstanceTree => true
case _: LiteralTree | _: LocalVarTree | _: PreEvaluatedTree | _: ThisTree | UnitTree => false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class RuntimePreEvaluationValidator(

override def validateOuter(tree: RuntimeTree): Validation[RuntimeEvaluableTree] =
super.validateOuter(tree).flatMap {
case tree @ (_: OuterModuleTree | OuterClassTree(_: PreEvaluatedTree, _)) =>
case tree @ (_: FieldTree | _: TopLevelModuleTree) =>
preEvaluate(tree)
case tree => Valid(tree)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package ch.epfl.scala.debugadapter.internal.evaluator

import com.sun.jdi._
import RuntimeEvaluatorExtractors.{BooleanTree, IsAnyVal, Module}
import RuntimeEvaluatorExtractors.{BooleanTree, IsAnyVal}
import scala.util.Success
import ch.epfl.scala.debugadapter.Logger

Expand Down Expand Up @@ -31,8 +31,6 @@ sealed trait FieldTree extends RuntimeEvaluableTree {
def field: Field
}

sealed trait OuterTree extends RuntimeEvaluableTree

/* -------------------------------------------------------------------------- */
/* Simple trees */
/* -------------------------------------------------------------------------- */
Expand Down Expand Up @@ -227,39 +225,6 @@ case class NewInstanceTree(init: StaticMethodTree) extends RuntimeEvaluableTree
}
}

case class OuterClassTree(
inner: RuntimeEvaluableTree,
`type`: ClassType
) extends OuterTree {
override def prettyPrint(depth: Int): String = {
val indent = "\t" * (depth + 1)
s"""|OuterClassTree(
|${indent}of= ${inner.prettyPrint(depth + 1)}
|${indent}type= ${`type`}
|${indent.dropRight(1)})""".stripMargin
}
}

case class OuterModuleTree(
module: ModuleTree
) extends OuterTree {
override def `type`: ClassType = module.`type`
override def prettyPrint(depth: Int): String = {
val indent = "\t" * (depth + 1)
s"""|OuterModuleTree(
|${indent}module= ${module.prettyPrint(depth + 1)}
|${indent.dropRight(1)})""".stripMargin
}
}

object OuterTree {
def apply(of: RuntimeTree, tpe: Type): Validation[OuterTree] = (of, tpe) match {
case (tree: RuntimeEvaluableTree, Module(module)) => Valid(new OuterModuleTree(TopLevelModuleTree(module)))
case (tree: RuntimeEvaluableTree, ct: ClassType) => Valid(new OuterClassTree(tree, ct))
case _ => Recoverable("No valid outer can be found")
}
}

case class ThisTree(
`type`: ReferenceType
) extends RuntimeEvaluableTree {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,27 @@ abstract class RuntimeEvaluatorTests(val scalaVersion: ScalaVersion) extends Deb
)
}

test("Should access a field defined in super class --- scala") {
val source =
"""|package example
|
|class A(x: Int)
|class B(x: Int) extends A(x) { def foo = x}
|
|object Main {
| def main(args: Array[String]): Unit = {
| val b = new B(42)
| println("ok")
| }
|}
|""".stripMargin
implicit val debuggee = TestingDebuggee.mainClass(source, "example.Main", scalaVersion)
check(
Breakpoint(9),
Evaluation.success("b.x", 42)
)
}

test("Should resolve non-generic overloads --- scala") {
implicit val debuggee = method
check(
Expand Down Expand Up @@ -818,6 +839,53 @@ abstract class RuntimeEvaluatorTests(val scalaVersion: ScalaVersion) extends Deb
Evaluation.success("f1.bar { 1+1; 2+2 }", "bar 4")
)
}

test("Should evaluate when multiple outers are present") {
val source =
"""|package example
|
|class A(x: String) {
| def a = "a"
| class AInner {
| def xx: String = {
| x
| }
| def ai = "ai"
| }
|}
|
|class B(x: String, y: String) extends A(x) {
| def b = "b"
| class BInner extends AInner {
| def yy: String = {
| y
| }
| }
|}
|
|object Main {
| def main(args: Array[String]): Unit = {
| val b = new B("x", "y")
| val bInner = new b.BInner
| val aInner = new b.AInner
| bInner.yy
| println("ok")
| }
|}
|""".stripMargin
implicit val debuggee: TestingDebuggee = TestingDebuggee.mainClass(source, "example.Main", scalaVersion)
check(
Breakpoint(25),
Evaluation.success("b.x", "x"),
Breakpoint(17),
Evaluation.success("x", "x"),
Evaluation.success("y", "y"),
Evaluation.success("ai", "ai"),
Evaluation.success("a", "a"),
Breakpoint(28),
Evaluation.success("aInner.b", "b")
)
}
}

/* -------------------------------------------------------------------------- */
Expand Down

0 comments on commit d20bdbe

Please sign in to comment.