Majority of the time users are less interested on the memory address of a type. It is mostly useful for pointer types (the memory address is shown). It makes the view more bloated without adding useful information. can always fall back to the debug console or watch pane to view the information if necessary.
358 lines
14 KiB
Python
358 lines
14 KiB
Python
"""
|
|
Test lldb-dap evaluate request
|
|
"""
|
|
|
|
import re
|
|
|
|
import lldbdap_testcase
|
|
from lldbsuite.test.decorators import skipIfWindows
|
|
from lldbsuite.test.lldbtest import line_number
|
|
from typing import TypedDict, Optional
|
|
|
|
|
|
class EvaluateResponseBody(TypedDict, total=False):
|
|
result: str
|
|
variablesReference: int
|
|
type: Optional[str]
|
|
memoryReference: Optional[str]
|
|
valueLocationReference: Optional[int]
|
|
|
|
|
|
class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase):
|
|
def assertEvaluate(
|
|
self,
|
|
expression,
|
|
result: str,
|
|
want_type="",
|
|
want_varref=False,
|
|
want_memref=True,
|
|
want_locref=False,
|
|
is_hex=None,
|
|
):
|
|
resp = self.dap_server.request_evaluate(
|
|
expression, context=self.context, is_hex=is_hex
|
|
)
|
|
self.assertTrue(
|
|
resp["success"], f"Failed to evaluate expression {expression!r}"
|
|
)
|
|
body: EvaluateResponseBody = resp["body"]
|
|
self.assertRegex(
|
|
body["result"],
|
|
result,
|
|
f"Unexpected 'result' for expression {expression!r} in response body {body}",
|
|
)
|
|
if want_varref:
|
|
self.assertNotEqual(
|
|
body["variablesReference"],
|
|
0,
|
|
f"Unexpected 'variablesReference' for expression {expression!r} in response body {body}",
|
|
)
|
|
else:
|
|
self.assertEqual(
|
|
body["variablesReference"],
|
|
0,
|
|
f"Unexpected 'variablesReference' for expression {expression!r} in response body {body}",
|
|
)
|
|
if want_type:
|
|
self.assertEqual(
|
|
body["type"],
|
|
want_type,
|
|
f"Unexpected 'type' for expression {expression!r} in response body {body}",
|
|
)
|
|
if want_memref:
|
|
self.assertIn(
|
|
"memoryReference",
|
|
body,
|
|
f"Unexpected 'memoryReference' for expression {expression!r} in response body {body}",
|
|
)
|
|
if want_locref:
|
|
self.assertIn(
|
|
"valueLocationReference",
|
|
body,
|
|
f"Unexpected 'valueLocationReference' for expression {expression!r} in response body {body}",
|
|
)
|
|
|
|
def assertEvaluateFailure(self, expression):
|
|
self.assertNotIn(
|
|
"result",
|
|
self.dap_server.request_evaluate(expression, context=self.context)["body"],
|
|
)
|
|
|
|
def isResultExpandedDescription(self):
|
|
return self.context == "repl"
|
|
|
|
def isExpressionParsedExpected(self):
|
|
return self.context != "hover"
|
|
|
|
def run_test_evaluate_expressions(
|
|
self, context=None, enableAutoVariableSummaries=False
|
|
):
|
|
"""
|
|
Tests the evaluate expression request at different breakpoints
|
|
"""
|
|
self.context = context
|
|
program = self.getBuildArtifact("a.out")
|
|
self.build_and_launch(
|
|
program,
|
|
enableAutoVariableSummaries=enableAutoVariableSummaries,
|
|
)
|
|
source = "main.cpp"
|
|
breakpoint_lines = [
|
|
line_number(source, "// breakpoint 1"),
|
|
line_number(source, "// breakpoint 2"),
|
|
line_number(source, "// breakpoint 3"),
|
|
line_number(source, "// breakpoint 4"),
|
|
line_number(source, "// breakpoint 5"),
|
|
line_number(source, "// breakpoint 6"),
|
|
line_number(source, "// breakpoint 7"),
|
|
line_number(source, "// breakpoint 8"),
|
|
]
|
|
breakpoint_ids = self.set_source_breakpoints(source, breakpoint_lines)
|
|
|
|
self.assertEqual(
|
|
len(breakpoint_ids),
|
|
len(breakpoint_lines),
|
|
"Did not resolve all the breakpoints.",
|
|
)
|
|
breakpoint_1 = breakpoint_ids[0]
|
|
breakpoint_2 = breakpoint_ids[1]
|
|
breakpoint_3 = breakpoint_ids[2]
|
|
breakpoint_4 = breakpoint_ids[3]
|
|
breakpoint_5 = breakpoint_ids[4]
|
|
breakpoint_6 = breakpoint_ids[5]
|
|
breakpoint_7 = breakpoint_ids[6]
|
|
breakpoint_8 = breakpoint_ids[7]
|
|
self.continue_to_breakpoint(breakpoint_1)
|
|
|
|
# Expressions at breakpoint 1, which is in main
|
|
self.assertEvaluate("var1", "20", want_type="int")
|
|
# Empty expression should equate to the previous expression.
|
|
if context == "repl":
|
|
self.assertEvaluate("", "20")
|
|
else:
|
|
self.assertEvaluateFailure("")
|
|
self.assertEvaluate("var2", "21", want_type="int")
|
|
if context == "repl":
|
|
self.assertEvaluate("", "21", want_type="int")
|
|
self.assertEvaluate("", "21", want_type="int")
|
|
self.assertEvaluate("static_int", "0x0000002a", want_type="int", is_hex=True)
|
|
self.assertEvaluate(
|
|
"non_static_int", "0x0000002b", want_type="int", is_hex=True
|
|
)
|
|
self.assertEvaluate("struct1.foo", "0x0000000f", want_type="int", is_hex=True)
|
|
self.assertEvaluate("struct2->foo", "0x00000010", want_type="int", is_hex=True)
|
|
self.assertEvaluate("static_int", "42", want_type="int")
|
|
self.assertEvaluate("non_static_int", "43", want_type="int")
|
|
self.assertEvaluate("struct1.foo", "15", want_type="int")
|
|
self.assertEvaluate("struct2->foo", "16", want_type="int")
|
|
|
|
if self.isResultExpandedDescription():
|
|
self.assertEvaluate(
|
|
"struct1",
|
|
r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)",
|
|
want_type="my_struct",
|
|
want_varref=True,
|
|
)
|
|
self.assertEvaluate(
|
|
"struct2",
|
|
r"\(my_struct \*\) (struct2|\$\d+) = 0x.*",
|
|
want_type="my_struct *",
|
|
want_varref=True,
|
|
)
|
|
self.assertEvaluate(
|
|
"struct3",
|
|
r"\(my_struct \*\) (struct3|\$\d+) = nullptr",
|
|
want_type="my_struct *",
|
|
want_varref=True,
|
|
)
|
|
else:
|
|
self.assertEvaluate(
|
|
"struct1",
|
|
(re.escape("{foo:15}") if enableAutoVariableSummaries else "my_struct"),
|
|
want_varref=True,
|
|
)
|
|
self.assertEvaluate(
|
|
"struct2",
|
|
"0x.* {foo:16}" if enableAutoVariableSummaries else "0x.*",
|
|
want_varref=True,
|
|
want_type="my_struct *",
|
|
)
|
|
self.assertEvaluate(
|
|
"struct3", "0x.*0", want_varref=True, want_type="my_struct *"
|
|
)
|
|
|
|
if context == "repl":
|
|
# In the repl context expressions may be interpreted as lldb
|
|
# commands since no variables have the same name as the command.
|
|
self.assertEvaluate("list", r".*", want_memref=False)
|
|
else:
|
|
self.assertEvaluateFailure("list") # local variable of a_function
|
|
|
|
self.assertEvaluateFailure("my_struct") # type name
|
|
self.assertEvaluateFailure("int") # type name
|
|
self.assertEvaluateFailure("foo") # member of my_struct
|
|
|
|
if self.isExpressionParsedExpected():
|
|
self.assertEvaluate(
|
|
"a_function",
|
|
"0x.*a.out`a_function.*",
|
|
want_type="int (*)(int)",
|
|
want_varref=True,
|
|
want_memref=False,
|
|
want_locref=True,
|
|
)
|
|
self.assertEvaluate(
|
|
"a_function(1)", "1", want_memref=False, want_type="int"
|
|
)
|
|
self.assertEvaluate("var2 + struct1.foo", "36", want_memref=False)
|
|
self.assertEvaluate(
|
|
"foo_func",
|
|
"0x.*a.out`foo_func.*",
|
|
want_type="int (*)()",
|
|
want_varref=True,
|
|
want_memref=False,
|
|
want_locref=True,
|
|
)
|
|
self.assertEvaluate("foo_var", "44")
|
|
else:
|
|
self.assertEvaluateFailure("a_function")
|
|
self.assertEvaluateFailure("a_function(1)")
|
|
self.assertEvaluateFailure("var2 + struct1.foo")
|
|
self.assertEvaluateFailure("foo_func")
|
|
self.assertEvaluate("foo_var", "44")
|
|
|
|
# Expressions at breakpoint 2, which is an anonymous block
|
|
self.continue_to_breakpoint(breakpoint_2)
|
|
self.assertEvaluate("var1", "20")
|
|
self.assertEvaluate("var2", "2") # different variable with the same name
|
|
self.assertEvaluate("static_int", "42")
|
|
self.assertEvaluate(
|
|
"non_static_int", "10"
|
|
) # different variable with the same name
|
|
if self.isResultExpandedDescription():
|
|
self.assertEvaluate(
|
|
"struct1",
|
|
r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)",
|
|
want_type="my_struct",
|
|
want_varref=True,
|
|
)
|
|
else:
|
|
self.assertEvaluate(
|
|
"struct1",
|
|
(re.escape("{foo:15}") if enableAutoVariableSummaries else "my_struct"),
|
|
want_type="my_struct",
|
|
want_varref=True,
|
|
)
|
|
self.assertEvaluate("struct1.foo", "15")
|
|
self.assertEvaluate("struct2->foo", "16")
|
|
|
|
if self.isExpressionParsedExpected():
|
|
self.assertEvaluate(
|
|
"a_function",
|
|
"0x.*a.out`a_function.*",
|
|
want_type="int (*)(int)",
|
|
want_varref=True,
|
|
want_memref=False,
|
|
want_locref=True,
|
|
)
|
|
self.assertEvaluate("a_function(1)", "1", want_memref=False)
|
|
self.assertEvaluate("var2 + struct1.foo", "17", want_memref=False)
|
|
self.assertEvaluate(
|
|
"foo_func", "0x.*a.out`foo_func.*", want_varref=True, want_memref=False
|
|
)
|
|
self.assertEvaluate("foo_var", "44")
|
|
else:
|
|
self.assertEvaluateFailure("a_function")
|
|
self.assertEvaluateFailure("a_function(1)")
|
|
self.assertEvaluateFailure("var2 + struct1.foo")
|
|
self.assertEvaluateFailure("foo_func")
|
|
self.assertEvaluate("foo_var", "44")
|
|
|
|
# Expressions at breakpoint 3, which is inside a_function
|
|
self.continue_to_breakpoint(breakpoint_3)
|
|
self.assertEvaluate("list", "42")
|
|
self.assertEvaluate("static_int", "42")
|
|
self.assertEvaluate("non_static_int", "43")
|
|
|
|
self.assertEvaluateFailure("var1")
|
|
self.assertEvaluateFailure("var2")
|
|
self.assertEvaluateFailure("struct1")
|
|
self.assertEvaluateFailure("struct1.foo")
|
|
self.assertEvaluateFailure("struct2->foo")
|
|
self.assertEvaluateFailure("var2 + struct1.foo")
|
|
|
|
if self.isExpressionParsedExpected():
|
|
self.assertEvaluate(
|
|
"a_function",
|
|
"0x.*a.out`a_function.*",
|
|
want_varref=True,
|
|
want_memref=False,
|
|
want_locref=True,
|
|
)
|
|
self.assertEvaluate("a_function(1)", "1", want_memref=False)
|
|
self.assertEvaluate("list + 1", "43", want_memref=False)
|
|
self.assertEvaluate(
|
|
"foo_func", "0x.*a.out`foo_func.*", want_varref=True, want_memref=False
|
|
)
|
|
self.assertEvaluate("foo_var", "44")
|
|
else:
|
|
self.assertEvaluateFailure("a_function")
|
|
self.assertEvaluateFailure("a_function(1)")
|
|
self.assertEvaluateFailure("list + 1")
|
|
self.assertEvaluateFailure("foo_func")
|
|
self.assertEvaluate("foo_var", "44")
|
|
|
|
# Now we check that values are updated after stepping
|
|
self.continue_to_breakpoint(breakpoint_4)
|
|
self.assertEvaluate("my_vec", "size=2", want_varref=True)
|
|
self.continue_to_breakpoint(breakpoint_5)
|
|
self.assertEvaluate("my_vec", "size=3", want_varref=True)
|
|
|
|
self.assertEvaluate("my_map", "size=2", want_varref=True)
|
|
self.continue_to_breakpoint(breakpoint_6)
|
|
self.assertEvaluate("my_map", "size=3", want_varref=True)
|
|
|
|
self.assertEvaluate("my_bool_vec", "size=1", want_varref=True)
|
|
self.continue_to_breakpoint(breakpoint_7)
|
|
self.assertEvaluate("my_bool_vec", "size=2", want_varref=True)
|
|
|
|
self.continue_to_breakpoint(breakpoint_8)
|
|
# Test memory read, especially with 'empty' repeat commands.
|
|
if context == "repl":
|
|
self.assertEvaluate(
|
|
"memory read -c 1 &my_ints", ".* 05 .*\n", want_memref=False
|
|
)
|
|
self.assertEvaluate("", ".* 0a .*\n", want_memref=False)
|
|
self.assertEvaluate("", ".* 0f .*\n", want_memref=False)
|
|
self.assertEvaluate("", ".* 14 .*\n", want_memref=False)
|
|
self.assertEvaluate("", ".* 19 .*\n", want_memref=False)
|
|
|
|
self.continue_to_exit()
|
|
|
|
@skipIfWindows
|
|
def test_generic_evaluate_expressions(self):
|
|
# Tests context-less expression evaluations
|
|
self.run_test_evaluate_expressions(enableAutoVariableSummaries=False)
|
|
|
|
@skipIfWindows
|
|
def test_repl_evaluate_expressions(self):
|
|
# Tests expression evaluations that are triggered from the Debug Console
|
|
self.run_test_evaluate_expressions("repl", enableAutoVariableSummaries=False)
|
|
|
|
@skipIfWindows
|
|
def test_watch_evaluate_expressions(self):
|
|
# Tests expression evaluations that are triggered from a watch expression
|
|
self.run_test_evaluate_expressions("watch", enableAutoVariableSummaries=True)
|
|
|
|
@skipIfWindows
|
|
def test_hover_evaluate_expressions(self):
|
|
# Tests expression evaluations that are triggered when hovering on the editor
|
|
self.run_test_evaluate_expressions("hover", enableAutoVariableSummaries=False)
|
|
|
|
@skipIfWindows
|
|
def test_variable_evaluate_expressions(self):
|
|
# Tests expression evaluations that are triggered in the variable explorer
|
|
self.run_test_evaluate_expressions(
|
|
"variables", enableAutoVariableSummaries=True
|
|
)
|