[lldb-vscode] Show a fake child with the raw value of synthetic types (#65552)
Currently, if the user wants to inspect the raw version of a synthetic variable, they have to go to the debug console and type `frame var <variable>`, which is not a great experience. Taking inspiration from CodeLLDB, this adds a `[raw]` child to every synthetic variable so that this kind of inspection can be done visually. Some examples: <img width="500" alt="Screenshot 2023-09-06 at 7 56 25 PM" src="https://github.com/llvm/llvm-project/assets/1613874/7fefb7c5-0da7-49c7-968b-78ac88348fea"> <img width="479" alt="Screenshot 2023-09-06 at 6 58 25 PM" src="https://github.com/llvm/llvm-project/assets/1613874/6e650567-16e1-462f-9bf5-4a3a605cf6fc">
This commit is contained in:
@@ -517,6 +517,20 @@ class TestVSCode_variables(lldbvscode_testcase.VSCodeTestCaseBase):
|
||||
}
|
||||
self.verify_variables(verify_locals, locals)
|
||||
|
||||
# We also verify that we produce a "[raw]" fake child with the real
|
||||
# SBValue for the synthetic type.
|
||||
verify_children = {
|
||||
"[0]": {"equals": {"type": "int", "value": "0"}},
|
||||
"[1]": {"equals": {"type": "int", "value": "0"}},
|
||||
"[2]": {"equals": {"type": "int", "value": "0"}},
|
||||
"[3]": {"equals": {"type": "int", "value": "0"}},
|
||||
"[4]": {"equals": {"type": "int", "value": "0"}},
|
||||
"[raw]": {"contains": {"type": ["vector"]}},
|
||||
}
|
||||
children = self.vscode.request_variables(locals[2]["variablesReference"])["body"]["variables"]
|
||||
self.verify_variables(verify_children, children)
|
||||
|
||||
|
||||
@skipIfWindows
|
||||
@skipIfRemote
|
||||
def test_registers(self):
|
||||
|
||||
@@ -1103,10 +1103,13 @@ std::string CreateUniqueVariableNameForDisplay(lldb::SBValue v,
|
||||
// }
|
||||
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
|
||||
int64_t varID, bool format_hex,
|
||||
bool is_name_duplicated) {
|
||||
bool is_name_duplicated,
|
||||
std::optional<std::string> custom_name) {
|
||||
llvm::json::Object object;
|
||||
EmplaceSafeString(object, "name",
|
||||
CreateUniqueVariableNameForDisplay(v, is_name_duplicated));
|
||||
EmplaceSafeString(
|
||||
object, "name",
|
||||
custom_name ? *custom_name
|
||||
: CreateUniqueVariableNameForDisplay(v, is_name_duplicated));
|
||||
|
||||
if (format_hex)
|
||||
v.SetFormat(lldb::eFormatHex);
|
||||
@@ -1131,15 +1134,19 @@ llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
|
||||
const bool is_synthetic = v.IsSynthetic();
|
||||
if (is_array || is_synthetic) {
|
||||
const auto num_children = v.GetNumChildren();
|
||||
// We create a "[raw]" fake child for each synthetic type, so we have to
|
||||
// account for it when returning indexed variables. We don't need to do this
|
||||
// for non-indexed ones.
|
||||
int actual_num_children = num_children + (is_synthetic ? 1 : 0);
|
||||
if (is_array) {
|
||||
object.try_emplace("indexedVariables", num_children);
|
||||
object.try_emplace("indexedVariables", actual_num_children);
|
||||
} else if (num_children > 0) {
|
||||
// If a type has a synthetic child provider, then the SBType of "v" won't
|
||||
// tell us anything about what might be displayed. So we can check if the
|
||||
// first child's name is "[0]" and then we can say it is indexed.
|
||||
const char *first_child_name = v.GetChildAtIndex(0).GetName();
|
||||
if (first_child_name && strcmp(first_child_name, "[0]") == 0)
|
||||
object.try_emplace("indexedVariables", num_children);
|
||||
object.try_emplace("indexedVariables", actual_num_children);
|
||||
}
|
||||
}
|
||||
EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME);
|
||||
|
||||
@@ -440,12 +440,17 @@ std::string CreateUniqueVariableNameForDisplay(lldb::SBValue v,
|
||||
/// As VSCode doesn't render two of more variables with the same name, we
|
||||
/// apply a suffix to distinguish duplicated variables.
|
||||
///
|
||||
/// \param[in] custom_name
|
||||
/// A provided custom name that is used instead of the SBValue's when
|
||||
/// creating the JSON representation.
|
||||
///
|
||||
/// \return
|
||||
/// A "Variable" JSON object with that follows the formal JSON
|
||||
/// definition outlined by Microsoft.
|
||||
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
|
||||
int64_t varID, bool format_hex,
|
||||
bool is_name_duplicated = false);
|
||||
bool is_name_duplicated = false,
|
||||
std::optional<std::string> custom_name = {});
|
||||
|
||||
llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit unit);
|
||||
|
||||
|
||||
@@ -3251,7 +3251,7 @@ void request_variables(const llvm::json::Object &request) {
|
||||
break;
|
||||
|
||||
int64_t var_ref = 0;
|
||||
if (variable.MightHaveChildren()) {
|
||||
if (variable.MightHaveChildren() || variable.IsSynthetic()) {
|
||||
var_ref = g_vsc.variables.InsertExpandableVariable(
|
||||
variable, /*is_permanent=*/false);
|
||||
}
|
||||
@@ -3264,23 +3264,36 @@ void request_variables(const llvm::json::Object &request) {
|
||||
// children.
|
||||
lldb::SBValue variable = g_vsc.variables.GetVariable(variablesReference);
|
||||
if (variable.IsValid()) {
|
||||
const auto num_children = variable.GetNumChildren();
|
||||
const int64_t end_idx = start + ((count == 0) ? num_children : count);
|
||||
for (auto i = start; i < end_idx; ++i) {
|
||||
lldb::SBValue child = variable.GetChildAtIndex(i);
|
||||
auto addChild = [&](lldb::SBValue child,
|
||||
std::optional<std::string> custom_name = {}) {
|
||||
if (!child.IsValid())
|
||||
break;
|
||||
return;
|
||||
if (child.MightHaveChildren()) {
|
||||
auto is_permanent =
|
||||
g_vsc.variables.IsPermanentVariableReference(variablesReference);
|
||||
auto childVariablesReferences =
|
||||
g_vsc.variables.InsertExpandableVariable(child, is_permanent);
|
||||
variables.emplace_back(CreateVariable(child, childVariablesReferences,
|
||||
childVariablesReferences, hex));
|
||||
variables.emplace_back(CreateVariable(
|
||||
child, childVariablesReferences, childVariablesReferences, hex,
|
||||
/*is_name_duplicated=*/false, custom_name));
|
||||
} else {
|
||||
variables.emplace_back(CreateVariable(child, 0, INT64_MAX, hex));
|
||||
variables.emplace_back(CreateVariable(child, 0, INT64_MAX, hex,
|
||||
/*is_name_duplicated=*/false,
|
||||
custom_name));
|
||||
}
|
||||
}
|
||||
};
|
||||
const int64_t num_children = variable.GetNumChildren();
|
||||
int64_t end_idx = start + ((count == 0) ? num_children : count);
|
||||
int64_t i = start;
|
||||
for (; i < end_idx && i < num_children; ++i)
|
||||
addChild(variable.GetChildAtIndex(i));
|
||||
|
||||
// If we haven't filled the count quota from the request, we insert a new
|
||||
// "[raw]" child that can be used to inspect the raw version of a
|
||||
// synthetic member. That eliminates the need for the user to go to the
|
||||
// debug console and type `frame var <variable> to get these values.
|
||||
if (variable.IsSynthetic() && i == num_children)
|
||||
addChild(variable.GetNonSyntheticValue(), "[raw]");
|
||||
}
|
||||
}
|
||||
llvm::json::Object body;
|
||||
|
||||
Reference in New Issue
Block a user