[Lldb-commits] [lldb] [lldb-dap] Updating VariableDescription to use GetDescription() as a fallback. (PR #77026)
John Harrison via lldb-commits
lldb-commits at lists.llvm.org
Wed Jan 10 17:22:56 PST 2024
https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/77026
>From 7656af47e058aa7101504cb31aaa067178110351 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Thu, 4 Jan 2024 15:42:35 -0800
Subject: [PATCH 1/5] [lldb-dap] Updating VariableDescription to use
GetDescription() as a fallback.
When generating a `display_value` for a variable the current approach calls
`SBValue::GetValue()` and `SBValue::GetSummary()` to generate a `display_value`
for the `SBValue`. However, there are cases where both of these return an empty
string and the fallback is to print a pointer and type name instead (e.g.
"FooBarType @ 0x00321").
For swift types, lldb includes a langauge runtime plugin that can generate a
user description of the object but this is only used with
`SBValue::GetDescription()`.
For example:
```
$ lldb swift-binary
... stop at breakpoint ...
lldb> script
>>> event = lldb.frame.GetValueForVariablePath("event")
>>> print("Value", event.GetValue())
Value None
>>> print("Summary", event.GetSummary())
Summary None
>>> print("Description", event)
Description (main.Event) event = (name = "Greetings", time = 2024-01-04 23:38:06 UTC)
```
With this change, if GetValue and GetSummary return empty then we try
`SBValue::GetDescription()` as a fallback before using the previous logic of
printing "<type> @ <addr>".
---
lldb/tools/lldb-dap/JSONUtils.cpp | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index df17ac9d849176..f8ac53ef809e6e 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -1042,10 +1042,14 @@ VariableDescription::VariableDescription(lldb::SBValue v, bool format_hex,
os_display_value << " " << *effective_summary;
} else if (effective_summary) {
os_display_value << *effective_summary;
-
- // As last resort, we print its type and address if available.
} else {
- if (!raw_display_type_name.empty()) {
+ lldb::SBStream description;
+ // Try letting lldb generate a description.
+ if (v.GetDescription(description) && description.GetSize()) {
+ os_display_value << description.GetData();
+
+ // As last resort, we print its type and address if available.
+ } else if (!raw_display_type_name.empty()) {
os_display_value << raw_display_type_name;
lldb::addr_t address = v.GetLoadAddress();
if (address != LLDB_INVALID_ADDRESS)
>From 4c3ad4b1c9363dc9fbaebe7aa005889e87ca5039 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Fri, 5 Jan 2024 16:48:27 -0800
Subject: [PATCH 2/5] Tweaking the format to remove trailing newlines and
updating tests to cover the adjusted printing.
---
.../lldb-dap/evaluate/TestDAP_evaluate.py | 17 +++-
.../lldb-dap/variables/TestDAP_variables.py | 93 ++++++++++++++++---
.../API/tools/lldb-dap/variables/main.cpp | 2 +-
lldb/tools/lldb-dap/JSONUtils.cpp | 47 ++++++----
4 files changed, 127 insertions(+), 32 deletions(-)
diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
index de9d2c93a1109d..d9e96b8219af34 100644
--- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
+++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
@@ -2,6 +2,7 @@
Test lldb-dap completions request
"""
+import re
import lldbdap_testcase
import dap_server
@@ -10,7 +11,7 @@
from lldbsuite.test.lldbtest import *
-class TestDAP_variables(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase):
def assertEvaluate(self, expression, regex):
self.assertRegexpMatches(
self.dap_server.request_evaluate(expression, context=self.context)["body"][
@@ -60,7 +61,12 @@ def run_test_evaluate_expressions(
self.assertEvaluate("static_int", "42")
self.assertEvaluate("non_static_int", "43")
self.assertEvaluate(
- "struct1", "{foo:15}" if enableAutoVariableSummaries else "my_struct @ 0x"
+ "struct1",
+ re.escape(
+ "{foo:15}"
+ if enableAutoVariableSummaries
+ else "(my_struct) struct1 = (foo = 15)"
+ ),
)
self.assertEvaluate(
"struct2", "0x.* {foo:16}" if enableAutoVariableSummaries else "0x.*"
@@ -96,7 +102,12 @@ def run_test_evaluate_expressions(
"non_static_int", "10"
) # different variable with the same name
self.assertEvaluate(
- "struct1", "{foo:15}" if enableAutoVariableSummaries else "my_struct @ 0x"
+ "struct1",
+ re.escape(
+ "{foo:15}"
+ if enableAutoVariableSummaries
+ else "(my_struct) struct1 = (foo = 15)"
+ ),
)
self.assertEvaluate("struct1.foo", "15")
self.assertEvaluate("struct2->foo", "16")
diff --git a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
index 58d6b941ac81a7..16824827d14d91 100644
--- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
+++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
@@ -3,6 +3,7 @@
"""
import os
+import re
import dap_server
import lldbdap_testcase
@@ -39,7 +40,18 @@ def verify_values(self, verify_dict, actual, varref_dict=None, expression=None):
startswith = actual_value.startswith(verify_value)
self.assertTrue(
startswith,
- ('"%s" value "%s" doesn\'t start with' ' "%s")')
+ ('"%s" value "%s" doesn\'t start with "%s")')
+ % (key, actual_value, verify_value),
+ )
+ if "matches" in verify_dict:
+ verify = verify_dict["matches"]
+ for key in verify:
+ verify_value = verify[key]
+ actual_value = actual[key]
+ self.assertRegex(
+ actual_value,
+ verify_value,
+ ('"%s" value "%s" doesn\'t match pattern "%s")')
% (key, actual_value, verify_value),
)
if "contains" in verify_dict:
@@ -150,7 +162,7 @@ def do_test_scopes_variables_setVariable_evaluate(
self.continue_to_breakpoints(breakpoint_ids)
locals = self.dap_server.get_local_variables()
globals = self.dap_server.get_global_variables()
- buffer_children = make_buffer_verify_dict(0, 32)
+ buffer_children = make_buffer_verify_dict(0, 16)
verify_locals = {
"argc": {
"equals": {
@@ -242,19 +254,57 @@ def do_test_scopes_variables_setVariable_evaluate(
},
"pt": {
"equals": {"type": "PointType"},
- "startswith": {
- "result": "{x:11, y:22}"
+ "equals": {
+ "result": "{x:11, y:22, buffer:{...}}"
if enableAutoVariableSummaries
- else "PointType @ 0x"
+ else """(PointType) pt = {
+ x = 11
+ y = 22
+ buffer = {
+ [0] = 0
+ [1] = 1
+ [2] = 2
+ [3] = 3
+ [4] = 4
+ [5] = 5
+ [6] = 6
+ [7] = 7
+ [8] = 8
+ [9] = 9
+ [10] = 10
+ [11] = 11
+ [12] = 12
+ [13] = 13
+ [14] = 14
+ [15] = 15
+ }
+}"""
},
"hasVariablesReference": True,
},
"pt.buffer": {
"equals": {"type": "int[32]"},
- "startswith": {
+ "equals": {
"result": "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...}"
if enableAutoVariableSummaries
- else "int[32] @ 0x"
+ else """(int[16]) buffer = {
+ [0] = 0
+ [1] = 1
+ [2] = 2
+ [3] = 3
+ [4] = 4
+ [5] = 5
+ [6] = 6
+ [7] = 7
+ [8] = 8
+ [9] = 9
+ [10] = 10
+ [11] = 11
+ [12] = 12
+ [13] = 13
+ [14] = 14
+ [15] = 15
+}"""
},
"hasVariablesReference": True,
},
@@ -440,7 +490,7 @@ def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: boo
},
"buffer": {
"children": buffer_children,
- "equals": {"indexedVariables": 32},
+ "equals": {"indexedVariables": 16},
},
},
},
@@ -457,10 +507,31 @@ def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: boo
"name": "pt",
"response": {
"equals": {"type": "PointType"},
- "startswith": {
- "result": "{x:11, y:22}"
+ "matches": {
+ "result": re.escape("{x:11, y:22, buffer:{...}}")
if enableAutoVariableSummaries
- else "PointType @ 0x"
+ else r"""\(PointType\) (\$0|pt) = \{
+ x = 11
+ y = 22
+ buffer = \{
+ \[0\] = 0
+ \[1\] = 1
+ \[2\] = 2
+ \[3\] = 3
+ \[4\] = 4
+ \[5\] = 5
+ \[6\] = 6
+ \[7\] = 7
+ \[8\] = 8
+ \[9\] = 9
+ \[10\] = 10
+ \[11\] = 11
+ \[12\] = 12
+ \[13\] = 13
+ \[14\] = 14
+ \[15\] = 15
+ \}
+\}"""
},
"missing": ["indexedVariables"],
"hasVariablesReference": True,
diff --git a/lldb/test/API/tools/lldb-dap/variables/main.cpp b/lldb/test/API/tools/lldb-dap/variables/main.cpp
index da09ecb474f3b2..3557067ed9ce13 100644
--- a/lldb/test/API/tools/lldb-dap/variables/main.cpp
+++ b/lldb/test/API/tools/lldb-dap/variables/main.cpp
@@ -1,5 +1,5 @@
-#define BUFFER_SIZE 32
+#define BUFFER_SIZE 16
struct PointType {
int x;
int y;
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index f8ac53ef809e6e..88f9df20b40409 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -135,6 +135,18 @@ std::vector<std::string> GetStrings(const llvm::json::Object *obj,
return strs;
}
+static std::string GetDescriptionTrimmed(lldb::SBValue &value) {
+ lldb::SBStream stream;
+ value.GetDescription(stream);
+ const char *description = stream.GetData();
+ size_t length = stream.GetSize();
+ if (length > 0 &&
+ (description[length - 1] == '\n' || description[length - 1] == '\r')) {
+ --length;
+ }
+ return std::string(description, length);
+}
+
/// Create a short summary for a container that contains the summary of its
/// first children, so that the user can get a glimpse of its contents at a
/// glance.
@@ -173,21 +185,21 @@ TryCreateAutoSummaryForContainer(lldb::SBValue &v) {
lldb::SBValue child = v.GetChildAtIndex(i);
if (llvm::StringRef name = child.GetName(); !name.empty()) {
- llvm::StringRef value;
+ llvm::StringRef desc;
if (llvm::StringRef summary = child.GetSummary(); !summary.empty())
- value = summary;
+ desc = summary;
+ else if (llvm::StringRef value = child.GetValue(); !value.empty())
+ desc = value;
else
- value = child.GetValue();
-
- if (!value.empty()) {
- // If the child is an indexed entry, we don't show its index to save
- // characters.
- if (name.starts_with("["))
- os << separator << value;
- else
- os << separator << name << ":" << value;
- separator = ", ";
- }
+ desc = "{...}"; // Fallback for nested types.
+
+ // If the child is an indexed entry, we don't show its index to save
+ // characters.
+ if (name.starts_with("["))
+ os << separator << desc;
+ else
+ os << separator << name << ":" << desc;
+ separator = ", ";
}
}
os << "}";
@@ -1043,10 +1055,11 @@ VariableDescription::VariableDescription(lldb::SBValue v, bool format_hex,
} else if (effective_summary) {
os_display_value << *effective_summary;
} else {
- lldb::SBStream description;
- // Try letting lldb generate a description.
- if (v.GetDescription(description) && description.GetSize()) {
- os_display_value << description.GetData();
+ // Try the value description, which may call into language runtime
+ // specific formatters, see ValueObjectPrinter.
+ std::string description = GetDescriptionTrimmed(v);
+ if (!description.empty()) {
+ os_display_value << description;
// As last resort, we print its type and address if available.
} else if (!raw_display_type_name.empty()) {
>From 86b8fd1493c7a0df9f34613cfcf078609074313c Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 9 Jan 2024 11:40:28 -0800
Subject: [PATCH 3/5] Ensuring the SBValue::GetDescription returns the correct
value to inidicate if the description was actually detected.
---
lldb/source/API/SBValue.cpp | 13 +++++++------
lldb/tools/lldb-dap/JSONUtils.cpp | 5 ++++-
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index 89d26a1fbe2824..b3e896dedabc0a 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -1210,15 +1210,16 @@ bool SBValue::GetDescription(SBStream &description) {
ValueLocker locker;
lldb::ValueObjectSP value_sp(GetSP(locker));
- if (value_sp) {
- DumpValueObjectOptions options;
- options.SetUseDynamicType(m_opaque_sp->GetUseDynamic());
- options.SetUseSyntheticValue(m_opaque_sp->GetUseSynthetic());
- value_sp->Dump(strm, options);
- } else {
+ if (!value_sp) {
strm.PutCString("No value");
+ return false;
}
+ DumpValueObjectOptions options;
+ options.SetUseDynamicType(m_opaque_sp->GetUseDynamic());
+ options.SetUseSyntheticValue(m_opaque_sp->GetUseSynthetic());
+ value_sp->Dump(strm, options);
+
return true;
}
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 88f9df20b40409..b45d737cceb58a 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -137,7 +137,10 @@ std::vector<std::string> GetStrings(const llvm::json::Object *obj,
static std::string GetDescriptionTrimmed(lldb::SBValue &value) {
lldb::SBStream stream;
- value.GetDescription(stream);
+ if (!value.GetDescription(stream)) {
+ return "";
+ }
+
const char *description = stream.GetData();
size_t length = stream.GetSize();
if (length > 0 &&
>From f3d1412fe1f4b6641d100a628844db1a13996baf Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 9 Jan 2024 13:30:11 -0800
Subject: [PATCH 4/5] Rename GetDescriptionTrimmed>TryCreateDescription and
simplify the impl.
---
lldb/tools/lldb-dap/JSONUtils.cpp | 25 +++++++++++--------------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index b45d737cceb58a..1d0790ff6cc113 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -135,19 +135,16 @@ std::vector<std::string> GetStrings(const llvm::json::Object *obj,
return strs;
}
-static std::string GetDescriptionTrimmed(lldb::SBValue &value) {
+/// Creates a description of the specified value. The description may be
+/// generated by the language runtime of the value, if one is detected
+/// and configured.
+static std::optional<std::string> TryCreateDescription(lldb::SBValue &value) {
lldb::SBStream stream;
- if (!value.GetDescription(stream)) {
- return "";
- }
+ if (!value.IsValid() || !value.GetDescription(stream))
+ return std::nullopt;
- const char *description = stream.GetData();
- size_t length = stream.GetSize();
- if (length > 0 &&
- (description[length - 1] == '\n' || description[length - 1] == '\r')) {
- --length;
- }
- return std::string(description, length);
+ llvm::StringRef description = stream.GetData();
+ return description.trim().str();
}
/// Create a short summary for a container that contains the summary of its
@@ -1060,9 +1057,9 @@ VariableDescription::VariableDescription(lldb::SBValue v, bool format_hex,
} else {
// Try the value description, which may call into language runtime
// specific formatters, see ValueObjectPrinter.
- std::string description = GetDescriptionTrimmed(v);
- if (!description.empty()) {
- os_display_value << description;
+ std::optional<std::string> description = TryCreateDescription(v);
+ if (description) {
+ os_display_value << *description;
// As last resort, we print its type and address if available.
} else if (!raw_display_type_name.empty()) {
>From d8eb78e220b2127b78cee2868daddbe57ae2c5c8 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Wed, 10 Jan 2024 13:16:51 -0800
Subject: [PATCH 5/5] Adjusting how variables are printed in the DAP
evalulation 'repl' and 'hover' context to have more detail object
descriptions.
---
.../lldb-dap/evaluate/TestDAP_evaluate.py | 66 ++++--
.../lldb-dap/variables/TestDAP_variables.py | 189 +++++++++---------
lldb/tools/lldb-dap/JSONUtils.cpp | 40 ++--
lldb/tools/lldb-dap/JSONUtils.h | 3 +
lldb/tools/lldb-dap/lldb-dap.cpp | 2 +-
5 files changed, 163 insertions(+), 137 deletions(-)
diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
index d9e96b8219af34..4016316a509130 100644
--- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
+++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
@@ -26,6 +26,9 @@ def assertEvaluateFailure(self, expression):
self.dap_server.request_evaluate(expression, context=self.context)["body"],
)
+ def isResultExpandedDescription(self):
+ return self.context == "repl" or self.context == "hover"
+
def isExpressionParsedExpected(self):
return self.context != "hover"
@@ -60,21 +63,30 @@ def run_test_evaluate_expressions(
self.assertEvaluate("var2", "21")
self.assertEvaluate("static_int", "42")
self.assertEvaluate("non_static_int", "43")
- self.assertEvaluate(
- "struct1",
- re.escape(
- "{foo:15}"
- if enableAutoVariableSummaries
- else "(my_struct) struct1 = (foo = 15)"
- ),
- )
- self.assertEvaluate(
- "struct2", "0x.* {foo:16}" if enableAutoVariableSummaries else "0x.*"
- )
- self.assertEvaluate("struct3", "0x.*0")
self.assertEvaluate("struct1.foo", "15")
self.assertEvaluate("struct2->foo", "16")
+ if self.isResultExpandedDescription():
+ self.assertEvaluate(
+ "struct1",
+ r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)",
+ )
+ self.assertEvaluate(
+ "struct2", r"\(my_struct \*\) (struct2|\$\d+) = 0x.*"
+ )
+ self.assertEvaluate("struct3", r"\(my_struct \*\) (struct3|\$\d+) = nullptr")
+ else:
+ self.assertEvaluate(
+ "struct1",
+ re.escape("{foo:15}")
+ if enableAutoVariableSummaries
+ else "my_struct @ 0x",
+ )
+ self.assertEvaluate(
+ "struct2", "0x.* {foo:16}" if enableAutoVariableSummaries else "0x.*"
+ )
+ self.assertEvaluate("struct3", "0x.*0")
+
self.assertEvaluateFailure("var") # local variable of a_function
self.assertEvaluateFailure("my_struct") # type name
self.assertEvaluateFailure("int") # type name
@@ -101,14 +113,18 @@ def run_test_evaluate_expressions(
self.assertEvaluate(
"non_static_int", "10"
) # different variable with the same name
- self.assertEvaluate(
- "struct1",
- re.escape(
- "{foo:15}"
+ if self.isResultExpandedDescription():
+ self.assertEvaluate(
+ "struct1",
+ r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)",
+ )
+ else:
+ self.assertEvaluate(
+ "struct1",
+ re.escape("{foo:15}")
if enableAutoVariableSummaries
- else "(my_struct) struct1 = (foo = 15)"
- ),
- )
+ else "my_struct @ 0x",
+ )
self.assertEvaluate("struct1.foo", "15")
self.assertEvaluate("struct2->foo", "16")
@@ -175,16 +191,22 @@ def test_generic_evaluate_expressions(self):
@skipIfRemote
def test_repl_evaluate_expressions(self):
# Tests expression evaluations that are triggered from the Debug Console
- self.run_test_evaluate_expressions("repl", enableAutoVariableSummaries=True)
+ self.run_test_evaluate_expressions("repl", enableAutoVariableSummaries=False)
@skipIfWindows
@skipIfRemote
def test_watch_evaluate_expressions(self):
# Tests expression evaluations that are triggered from a watch expression
- self.run_test_evaluate_expressions("watch", enableAutoVariableSummaries=False)
+ self.run_test_evaluate_expressions("watch", enableAutoVariableSummaries=True)
@skipIfWindows
@skipIfRemote
def test_hover_evaluate_expressions(self):
# Tests expression evaluations that are triggered when hovering on the editor
- self.run_test_evaluate_expressions("hover", enableAutoVariableSummaries=True)
+ self.run_test_evaluate_expressions("hover", enableAutoVariableSummaries=False)
+
+ @skipIfWindows
+ @skipIfRemote
+ def test_variable_evaluate_expressions(self):
+ # Tests expression evaluations that are triggered in the variable explorer
+ self.run_test_evaluate_expressions("variable", enableAutoVariableSummaries=True)
diff --git a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
index 16824827d14d91..2cee170d8dafc9 100644
--- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
+++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
@@ -3,7 +3,6 @@
"""
import os
-import re
import dap_server
import lldbdap_testcase
@@ -254,57 +253,19 @@ def do_test_scopes_variables_setVariable_evaluate(
},
"pt": {
"equals": {"type": "PointType"},
- "equals": {
+ "startswith": {
"result": "{x:11, y:22, buffer:{...}}"
if enableAutoVariableSummaries
- else """(PointType) pt = {
- x = 11
- y = 22
- buffer = {
- [0] = 0
- [1] = 1
- [2] = 2
- [3] = 3
- [4] = 4
- [5] = 5
- [6] = 6
- [7] = 7
- [8] = 8
- [9] = 9
- [10] = 10
- [11] = 11
- [12] = 12
- [13] = 13
- [14] = 14
- [15] = 15
- }
-}"""
+ else "PointType @ 0x"
},
"hasVariablesReference": True,
},
"pt.buffer": {
- "equals": {"type": "int[32]"},
- "equals": {
+ "equals": {"type": "int[16]"},
+ "startswith": {
"result": "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...}"
if enableAutoVariableSummaries
- else """(int[16]) buffer = {
- [0] = 0
- [1] = 1
- [2] = 2
- [3] = 3
- [4] = 4
- [5] = 5
- [6] = 6
- [7] = 7
- [8] = 8
- [9] = 9
- [10] = 10
- [11] = 11
- [12] = 12
- [13] = 13
- [14] = 14
- [15] = 15
-}"""
+ else "int[16] @ 0x"
},
"hasVariablesReference": True,
},
@@ -505,36 +466,85 @@ def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: boo
# the other temporary (from other UI).
expandable_expression = {
"name": "pt",
- "response": {
- "equals": {"type": "PointType"},
- "matches": {
- "result": re.escape("{x:11, y:22, buffer:{...}}")
- if enableAutoVariableSummaries
- else r"""\(PointType\) (\$0|pt) = \{
+ "context": {
+ "repl": {
+ "equals": {"type": "PointType"},
+ "equals": {
+ "result": """(PointType) $0 = {
x = 11
y = 22
- buffer = \{
- \[0\] = 0
- \[1\] = 1
- \[2\] = 2
- \[3\] = 3
- \[4\] = 4
- \[5\] = 5
- \[6\] = 6
- \[7\] = 7
- \[8\] = 8
- \[9\] = 9
- \[10\] = 10
- \[11\] = 11
- \[12\] = 12
- \[13\] = 13
- \[14\] = 14
- \[15\] = 15
- \}
-\}"""
+ buffer = {
+ [0] = 0
+ [1] = 1
+ [2] = 2
+ [3] = 3
+ [4] = 4
+ [5] = 5
+ [6] = 6
+ [7] = 7
+ [8] = 8
+ [9] = 9
+ [10] = 10
+ [11] = 11
+ [12] = 12
+ [13] = 13
+ [14] = 14
+ [15] = 15
+ }
+}"""
+ },
+ "missing": ["indexedVariables"],
+ "hasVariablesReference": True,
+ },
+ "hover": {
+ "equals": {"type": "PointType"},
+ "equals": {
+ "result": """(PointType) pt = {
+ x = 11
+ y = 22
+ buffer = {
+ [0] = 0
+ [1] = 1
+ [2] = 2
+ [3] = 3
+ [4] = 4
+ [5] = 5
+ [6] = 6
+ [7] = 7
+ [8] = 8
+ [9] = 9
+ [10] = 10
+ [11] = 11
+ [12] = 12
+ [13] = 13
+ [14] = 14
+ [15] = 15
+ }
+}"""
+ },
+ "missing": ["indexedVariables"],
+ "hasVariablesReference": True,
+ },
+ "watch": {
+ "equals": {"type": "PointType"},
+ "startswith": {
+ "result": "{x:11, y:22, buffer:{...}}"
+ if enableAutoVariableSummaries
+ else "PointType @ 0x"
+ },
+ "missing": ["indexedVariables"],
+ "hasVariablesReference": True,
+ },
+ "variables": {
+ "equals": {"type": "PointType"},
+ "startswith": {
+ "result": "{x:11, y:22, buffer:{...}}"
+ if enableAutoVariableSummaries
+ else "PointType @ 0x"
+ },
+ "missing": ["indexedVariables"],
+ "hasVariablesReference": True,
},
- "missing": ["indexedVariables"],
- "hasVariablesReference": True,
},
"children": {
"x": {"equals": {"type": "int", "value": "11"}},
@@ -543,27 +553,18 @@ def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: boo
},
}
- # Evaluate from permanent UI.
- permanent_expr_varref_dict = {}
- response = self.dap_server.request_evaluate(
- expandable_expression["name"], frameIndex=0, threadId=None, context="repl"
- )
- self.verify_values(
- expandable_expression["response"],
- response["body"],
- permanent_expr_varref_dict,
- expandable_expression["name"],
- )
-
- # Evaluate from temporary UI.
- temporary_expr_varref_dict = {}
- response = self.dap_server.request_evaluate(expandable_expression["name"])
- self.verify_values(
- expandable_expression["response"],
- response["body"],
- temporary_expr_varref_dict,
- expandable_expression["name"],
- )
+ # Evaluate from known contexts.
+ expr_varref_dict = {}
+ for context, verify_dict in expandable_expression["context"].items():
+ response = self.dap_server.request_evaluate(
+ expandable_expression["name"], frameIndex=0, threadId=None, context=context
+ )
+ self.verify_values(
+ verify_dict,
+ response["body"],
+ expr_varref_dict,
+ expandable_expression["name"],
+ )
# Evaluate locals again.
locals = self.dap_server.get_local_variables()
@@ -571,7 +572,7 @@ def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: boo
# Verify the evaluated expressions before second locals evaluation
# can be expanded.
- var_ref = temporary_expr_varref_dict[expandable_expression["name"]]
+ var_ref = expr_varref_dict[expandable_expression["name"]]
response = self.dap_server.request_variables(var_ref)
self.verify_variables(
expandable_expression["children"], response["body"]["variables"]
@@ -587,7 +588,7 @@ def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: boo
)
self.continue_to_breakpoints(breakpoint_ids)
- var_ref = permanent_expr_varref_dict[expandable_expression["name"]]
+ var_ref = expr_varref_dict[expandable_expression["name"]]
response = self.dap_server.request_variables(var_ref)
self.verify_variables(
expandable_expression["children"], response["body"]["variables"]
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 1d0790ff6cc113..793246e4551170 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -135,18 +135,6 @@ std::vector<std::string> GetStrings(const llvm::json::Object *obj,
return strs;
}
-/// Creates a description of the specified value. The description may be
-/// generated by the language runtime of the value, if one is detected
-/// and configured.
-static std::optional<std::string> TryCreateDescription(lldb::SBValue &value) {
- lldb::SBStream stream;
- if (!value.IsValid() || !value.GetDescription(stream))
- return std::nullopt;
-
- llvm::StringRef description = stream.GetData();
- return description.trim().str();
-}
-
/// Create a short summary for a container that contains the summary of its
/// first children, so that the user can get a glimpse of its contents at a
/// glance.
@@ -1054,15 +1042,10 @@ VariableDescription::VariableDescription(lldb::SBValue v, bool format_hex,
os_display_value << " " << *effective_summary;
} else if (effective_summary) {
os_display_value << *effective_summary;
+
+ // As last resort, we print its type and address if available.
} else {
- // Try the value description, which may call into language runtime
- // specific formatters, see ValueObjectPrinter.
- std::optional<std::string> description = TryCreateDescription(v);
- if (description) {
- os_display_value << *description;
-
- // As last resort, we print its type and address if available.
- } else if (!raw_display_type_name.empty()) {
+ if (!raw_display_type_name.empty()) {
os_display_value << raw_display_type_name;
lldb::addr_t address = v.GetLoadAddress();
if (address != LLDB_INVALID_ADDRESS)
@@ -1108,6 +1091,23 @@ llvm::json::Object VariableDescription::GetVariableExtensionsJSON() {
return extensions;
}
+std::string VariableDescription::GetResult(llvm::StringRef context) {
+ // In repl and hover context, the results can be displayed as multiple lines
+ // so more detailed descriptions can be returned.
+ if (context != "repl" && context != "hover")
+ return display_value;
+
+ lldb::SBStream stream;
+ // Try the SBValue::GetDescription(), which may call into language runtime
+ // specific formatters (see ValueObjectPrinter), otherwise return the
+ // display_value.
+ if (!v.IsValid() || !v.GetDescription(stream))
+ return display_value;
+
+ llvm::StringRef description = stream.GetData();
+ return description.trim().str();
+}
+
// "Variable": {
// "type": "object",
// "description": "A Variable is a name/value pair. Optionally a variable
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index 7f9fc7037c63d6..056d04945f03d4 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -405,6 +405,9 @@ struct VariableDescription {
/// Create a JSON object that represents these extensions to the DAP variable
/// response.
llvm::json::Object GetVariableExtensionsJSON();
+
+ /// Returns a description of the value appropraite for the specified context.
+ std::string GetResult(llvm::StringRef context);
};
/// Create a "Variable" object for a LLDB thread object.
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 0d45b0379c8526..e91b4115156b73 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -1317,7 +1317,7 @@ void request_evaluate(const llvm::json::Object &request) {
EmplaceSafeString(response, "message", "evaluate failed");
} else {
VariableDescription desc(value);
- EmplaceSafeString(body, "result", desc.display_value);
+ EmplaceSafeString(body, "result", desc.GetResult(context));
EmplaceSafeString(body, "type", desc.display_type_name);
if (value.MightHaveChildren()) {
auto variableReference = g_dap.variables.InsertExpandableVariable(
More information about the lldb-commits
mailing list