Skip to content

Commit d03426f

Browse files
authored
fix evaluation in static context (#549)
* fix evaluation in static context * added static context tree node * static
1 parent a97d3b2 commit d03426f

File tree

5 files changed

+97
-37
lines changed

5 files changed

+97
-37
lines changed

modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimeDefaultValidator.scala

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class RuntimeDefaultValidator(val frame: JdiFrame, val sourceLookUp: SourceLookU
4949

5050
protected def validateWithClass(expression: Stat): Validation[RuntimeTree] =
5151
expression match {
52-
case value: Term.Name => validateName(value.value, false).orElse(validateClass(value.value, thisTree))
52+
case value: Term.Name => validateName(value.value, false).orElse(validateClass(value.value, currentLocation))
5353
case Term.Select(qual, name) =>
5454
validateWithClass(qual).transform {
5555
case qual @ Valid(q) =>
@@ -87,6 +87,12 @@ class RuntimeDefaultValidator(val frame: JdiFrame, val sourceLookUp: SourceLookU
8787
frame.thisObject
8888
.map(ths => ThisTree(ths.reference.referenceType().asInstanceOf[ClassType]))
8989
}
90+
lazy val currentLocation: Validation[RuntimeTree] = thisTree.orElse {
91+
frame.current().location().declaringType() match {
92+
case ct: ClassType => Valid(StaticTree(ct))
93+
case _ => Recoverable("Cannot get current location")
94+
}
95+
}
9096

9197
/* -------------------------------------------------------------------------- */
9298
/* Name validation */
@@ -148,7 +154,7 @@ class RuntimeDefaultValidator(val frame: JdiFrame, val sourceLookUp: SourceLookU
148154
searchClasses(name.stripSuffix("$"), of.map(_.`type`.name()).toOption)
149155
.flatMap { cls =>
150156
of match {
151-
case Valid(_: RuntimeEvaluableTree) | _: Invalid => Valid(ClassTree(cls))
157+
case Valid(_: RuntimeEvaluableTree | _: StaticTree) | _: Invalid => Valid(ClassTree(cls))
152158
case Valid(ct: ClassTree) =>
153159
if (cls.isStatic()) Valid(ClassTree(cls))
154160
else CompilerRecoverable(s"Cannot access non-static class ${cls.name} from ${ct.`type`.name()}")
@@ -161,7 +167,6 @@ class RuntimeDefaultValidator(val frame: JdiFrame, val sourceLookUp: SourceLookU
161167
}
162168
}
163169

164-
// ! Does not work as expected inside a static context
165170
def validateMember(
166171
name: String,
167172
of: RuntimeTree,
@@ -181,7 +186,7 @@ class RuntimeDefaultValidator(val frame: JdiFrame, val sourceLookUp: SourceLookU
181186

182187
def validateName(name: String, methodFirst: Boolean): Validation[RuntimeEvaluableTree] =
183188
localVarTreeByName(NameTransformer.encode(name))
184-
.orElse(thisTree.flatMap(validateMember(name, _, methodFirst)))
189+
.orElse(currentLocation.flatMap(validateMember(name, _, methodFirst)))
185190

186191
/* -------------------------------------------------------------------------- */
187192
/* Apply validation */
@@ -222,7 +227,7 @@ class RuntimeDefaultValidator(val frame: JdiFrame, val sourceLookUp: SourceLookU
222227
searchClassesQCN(qual.toString + "$")
223228
}
224229
PreparedCall(qualTree, name.value)
225-
case name: Term.Name => PreparedCall(thisTree, name.value)
230+
case name: Term.Name => PreparedCall(currentLocation, name.value)
226231
}
227232

228233
val validatedArgs = call.argClause.map(validate).traverse
@@ -310,7 +315,7 @@ class RuntimeDefaultValidator(val frame: JdiFrame, val sourceLookUp: SourceLookU
310315
case select: Term.Select =>
311316
validateWithClass(select.qual).flatMap(fieldTreeByName(_, select.name.value, false))
312317
case name: Term.Name =>
313-
localVarTreeByName(name.value, false).orElse(thisTree.flatMap(fieldTreeByName(_, name.value, false)))
318+
localVarTreeByName(name.value, false).orElse(currentLocation.flatMap(fieldTreeByName(_, name.value, false)))
314319
case _ => Recoverable("Unsupported assignment")
315320
}
316321

modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimeEvaluationHelpers.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -403,13 +403,13 @@ private[evaluator] class RuntimeEvaluationHelpers(frame: JdiFrame, sourceLookup:
403403
def toStaticIfNeeded(field: Field, on: RuntimeTree): Validation[RuntimeEvaluableTree] =
404404
(field.`type`, on) match {
405405
case (Module(module), _) => Valid(TopLevelModuleTree(module))
406-
case (_, cls: ClassTree) => Valid(StaticFieldTree(field, cls.`type`))
406+
case (_, cls: RuntimeValidationTree) =>
407+
if (field.isStatic) Valid(StaticFieldTree(field, cls.`type`))
408+
else Fatal(s"Accessing instance field $field from static context ${cls.`type`} is not allowed")
407409
case (_, Module(mod)) => Valid(InstanceFieldTree(field, mod))
408410
case (_, eval: RuntimeEvaluableTree) =>
409411
if (field.isStatic())
410-
CompilerRecoverable(
411-
s"Accessing static field $field from instance ${eval.`type`} can lead to unexpected behavior"
412-
)
412+
Fatal(s"Accessing static field $field from instance ${eval.`type`} can lead to unexpected behavior")
413413
else Valid(InstanceFieldTree(field, eval))
414414
}
415415

@@ -418,13 +418,13 @@ private[evaluator] class RuntimeEvaluationHelpers(frame: JdiFrame, sourceLookup:
418418
args: Seq[RuntimeEvaluableTree],
419419
on: RuntimeTree
420420
): Validation[MethodTree] = on match {
421-
case cls: ClassTree => Valid(StaticMethodTree(method, args, cls.`type`))
421+
case cls: RuntimeValidationTree =>
422+
if (method.isStatic()) Valid(StaticMethodTree(method, args, cls.`type`))
423+
else Fatal(s"Accessing instance method $method from static context ${cls.`type`} is not allowed")
422424
case Module(mod) => Valid(InstanceMethodTree(method, args, mod))
423425
case eval: RuntimeEvaluableTree =>
424426
if (method.isStatic())
425-
CompilerRecoverable(
426-
s"Accessing static method $method from instance ${eval.`type`} can lead to unexpected behavior"
427-
)
427+
Fatal(s"Accessing static method $method from instance ${eval.`type`} can lead to unexpected behavior")
428428
else Valid(InstanceMethodTree(method, args, eval))
429429
}
430430
}

modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimeEvaluatorExtractors.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ protected[internal] object RuntimeEvaluatorExtractors {
2121

2222
def unapply(tree: RuntimeTree): Option[RuntimeEvaluableTree] =
2323
tree match {
24-
case cls: ClassTree => unapply(cls.`type`).map(TopLevelModuleTree(_))
24+
case cls: RuntimeValidationTree => unapply(cls.`type`).map(TopLevelModuleTree(_))
2525
case tree: RuntimeEvaluableTree => unapply(tree.`type`).map(_ => tree)
2626
}
2727
}
@@ -45,7 +45,7 @@ protected[internal] object RuntimeEvaluatorExtractors {
4545
case AssignTree(lhs, rhs, _) => unapply(lhs) || unapply(rhs)
4646
case _: MethodTree | _: NewInstanceTree => true
4747
case _: LiteralTree | _: LocalVarTree | _: PreEvaluatedTree | _: ThisTree | UnitTree => false
48-
case _: StaticFieldTree | _: ClassTree | _: TopLevelModuleTree => false
48+
case _: StaticFieldTree | _: ClassTree | _: StaticTree | _: TopLevelModuleTree => false
4949
case _: PrimitiveBinaryOpTree | _: PrimitiveUnaryOpTree | _: ArrayElemTree => false
5050
}
5151
def unapply(tree: Validation[RuntimeTree]): Validation[RuntimeTree] =

modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimeTree.scala

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,12 @@ sealed trait RuntimeTree {
1313
override def toString(): String = prettyPrint(0)
1414
def prettyPrint(depth: Int): String
1515
}
16-
sealed trait RuntimeValidationTree extends RuntimeTree
17-
sealed trait RuntimeEvaluableTree extends RuntimeTree
18-
19-
sealed trait TypeTree extends RuntimeTree {
16+
sealed trait RuntimeValidationTree extends RuntimeTree {
2017
override def `type`: ClassType
2118
}
19+
sealed trait RuntimeEvaluableTree extends RuntimeTree
2220

23-
sealed trait ModuleTree extends RuntimeEvaluableTree with TypeTree
21+
sealed trait ModuleTree extends RuntimeEvaluableTree
2422

2523
sealed trait MethodTree extends RuntimeEvaluableTree {
2624
def method: Method
@@ -266,8 +264,7 @@ case class NestedModuleTree(
266264

267265
case class ClassTree(
268266
`type`: ClassType
269-
) extends RuntimeValidationTree
270-
with TypeTree {
267+
) extends RuntimeValidationTree {
271268
override def prettyPrint(depth: Int): String = {
272269
val indent = "\t" * (depth + 1)
273270
s"""|ClassTree(
@@ -276,6 +273,17 @@ case class ClassTree(
276273
}
277274
}
278275

276+
case class StaticTree(
277+
`type`: ClassType
278+
) extends RuntimeValidationTree {
279+
override def prettyPrint(depth: Int): String = {
280+
val indent = "\t" * (depth + 1)
281+
s"""|StaticTree(
282+
|${indent}t= ${`type`},
283+
|${indent.dropRight(1)})""".stripMargin
284+
}
285+
}
286+
279287
/* -------------------------------------------------------------------------- */
280288
/* Pre-evaluated trees */
281289
/* -------------------------------------------------------------------------- */

modules/tests/src/test/scala/ch/epfl/scala/debugadapter/internal/JavaRuntimeEvaluatorTests.scala

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,7 @@ class JavaRuntimeEvaluatorTests extends DebugTestSuite {
208208
protected override def defaultConfig: DebugConfig =
209209
super.defaultConfig.copy(evaluationMode = DebugConfig.RuntimeEvaluationOnly)
210210

211-
// TODO: fix bug when trying to evaluate 'l'
212-
// TODO: operations on primitive types (+...)
213-
test("should retrieve the value of a local variable from jdi --- java") {
211+
test("should retrieve the value of a local variable from jdi") {
214212
implicit val debuggee = localVar
215213
check(
216214
Breakpoint(10),
@@ -223,7 +221,7 @@ class JavaRuntimeEvaluatorTests extends DebugTestSuite {
223221
)
224222
}
225223

226-
test("Should retrieve the value of a field, should it be private or not --- java") {
224+
test("Should retrieve the value of a field, should it be private or not") {
227225
implicit val debuggee = fieldMethod
228226
check(
229227
Breakpoint(13),
@@ -235,14 +233,14 @@ class JavaRuntimeEvaluatorTests extends DebugTestSuite {
235233
Evaluation.success("foo.superfoo", "hello super"),
236234
Evaluation.success("Foo.foofoo", "foofoo"),
237235
Evaluation.success("SuperFoo.foofoo", "superfoofoo"),
238-
Evaluation.failed("coucou"),
236+
Evaluation.success("coucou", "coucou"),
239237
Evaluation.failed("lapin"),
240238
Evaluation.failed("love")
241239
)
242240
)
243241
}
244242

245-
test("Should retrieve the value of a field without selector --- java") {
243+
test("Should retrieve the value of a field without selector") {
246244
implicit val debuggee = fieldMethod
247245
check(
248246
Breakpoint(17),
@@ -254,7 +252,7 @@ class JavaRuntimeEvaluatorTests extends DebugTestSuite {
254252
)
255253
}
256254

257-
test("Should call static method --- java") {
255+
test("Should call static method") {
258256
implicit val debuggee = fieldMethod
259257
check(
260258
Breakpoint(17),
@@ -266,7 +264,7 @@ class JavaRuntimeEvaluatorTests extends DebugTestSuite {
266264
)
267265
}
268266

269-
test("Should get the value of a field in a nested type --- java") {
267+
test("Should get the value of a field in a nested type") {
270268
implicit val debuggee = nested
271269
check(
272270
Breakpoint(11),
@@ -284,7 +282,7 @@ class JavaRuntimeEvaluatorTests extends DebugTestSuite {
284282
)
285283
}
286284

287-
test("Should not call static members from instance") {
285+
test("Should not call static members from instance or instance member from static") {
288286
implicit val debuggee = fieldMethod
289287
check(
290288
Breakpoint(13),
@@ -293,13 +291,15 @@ class JavaRuntimeEvaluatorTests extends DebugTestSuite {
293291
Evaluation.failed("hiddenFoo.foofoo"),
294292
Evaluation.failed("foo.foofoo"),
295293
Evaluation.failed("superfoo.foofoo"),
296-
Evaluation.failed("main.staticMethod()")
294+
Evaluation.failed("main.staticMethod()"),
295+
Evaluation.failed("foo()"),
296+
Evaluation.failed("lapin")
297297
)
298298
)
299299

300300
}
301301

302-
test("Should call a method on an instance of a nested type --- java") {
302+
test("Should call a method on an instance of a nested type") {
303303
implicit val debuggee = nested
304304
check(
305305
Breakpoint(11),
@@ -310,7 +310,7 @@ class JavaRuntimeEvaluatorTests extends DebugTestSuite {
310310
)
311311
}
312312

313-
test("Should call a static method on nested & inner types --- java") {
313+
test("Should call a static method on nested & inner types") {
314314
implicit val debuggee = nested
315315
check(
316316
Breakpoint(11),
@@ -323,7 +323,7 @@ class JavaRuntimeEvaluatorTests extends DebugTestSuite {
323323
)
324324
}
325325

326-
test("Should pre evaluate method and resolve most precise method --- java") {
326+
test("Should pre evaluate method and resolve most precise method") {
327327
implicit val debuggee = preEvaluation
328328
check(
329329
Breakpoint(8),
@@ -339,7 +339,7 @@ class JavaRuntimeEvaluatorTests extends DebugTestSuite {
339339
)
340340
}
341341

342-
test("Should instantiate inner classes --- java") {
342+
test("Should instantiate inner classes") {
343343
implicit val debuggee = nested
344344
check(
345345
Breakpoint(15),
@@ -351,4 +351,51 @@ class JavaRuntimeEvaluatorTests extends DebugTestSuite {
351351
)
352352
)
353353
}
354+
355+
test("Should evaluate static members from a static context") {
356+
val source =
357+
"""|package example;
358+
|
359+
|public class Main {
360+
| public static int i = 5;
361+
| public static void main(String[] args) {
362+
| System.out.println(i);
363+
| }
364+
| public static String foo() { return "foo"; }
365+
| static class Bar {
366+
| public int x = 42;
367+
| public static String bar() { return "bar"; }
368+
| public Bar(int x) { this.x = x; }
369+
| }
370+
|}""".stripMargin
371+
implicit val debuggee: TestingDebuggee = TestingDebuggee.fromJavaSource(source, "example.Main", scalaVersion)
372+
check(
373+
Breakpoint(6),
374+
Evaluation.success("i", 5),
375+
Evaluation.success("foo()", "foo"),
376+
Evaluation.success("new Bar(42)", ObjectRef("Main$Bar")),
377+
Evaluation.success("new Bar(42).x", 42),
378+
Evaluation.success("Bar.bar()", "bar")
379+
)
380+
}
381+
382+
test("should call nested type in static context") {
383+
val source =
384+
"""|package example;
385+
|
386+
|class Test {
387+
| public static void main(String[] args) {
388+
| System.out.println(B.bar);
389+
| }
390+
|
391+
| class B {
392+
| static final String bar = "bar";
393+
| }
394+
|}""".stripMargin
395+
implicit val debuggee: TestingDebuggee = TestingDebuggee.fromJavaSource(source, "example.Test", scalaVersion)
396+
check(
397+
Breakpoint(5),
398+
Evaluation.success("B.bar", "bar")
399+
)
400+
}
354401
}

0 commit comments

Comments
 (0)