Author: Walter Erquinigo Date: 2023-09-06T20:13:48-04:00 New Revision: cf5d8def5cf66fbdfffa00a4845bd648ec58ed60
URL: https://github.com/llvm/llvm-project/commit/cf5d8def5cf66fbdfffa00a4845bd648ec58ed60 DIFF: https://github.com/llvm/llvm-project/commit/cf5d8def5cf66fbdfffa00a4845bd648ec58ed60.diff LOG: [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"> Added: Modified: lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py lldb/tools/lldb-vscode/JSONUtils.cpp lldb/tools/lldb-vscode/JSONUtils.h lldb/tools/lldb-vscode/lldb-vscode.cpp Removed: ################################################################################ diff --git a/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py b/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py index 6eb8e87190d0074..953f330e14a0f97 100644 --- a/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py +++ b/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py @@ -517,6 +517,20 @@ def test_indexedVariables(self): } 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): diff --git a/lldb/tools/lldb-vscode/JSONUtils.cpp b/lldb/tools/lldb-vscode/JSONUtils.cpp index 9a6fd26d2bd5864..e34448c9b7f2f85 100644 --- a/lldb/tools/lldb-vscode/JSONUtils.cpp +++ b/lldb/tools/lldb-vscode/JSONUtils.cpp @@ -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); diff --git a/lldb/tools/lldb-vscode/JSONUtils.h b/lldb/tools/lldb-vscode/JSONUtils.h index 516bc462eae1b16..2013147f5d3532a 100644 --- a/lldb/tools/lldb-vscode/JSONUtils.h +++ b/lldb/tools/lldb-vscode/JSONUtils.h @@ -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); diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp index 294783c4f505d37..5480edd74f24f47 100644 --- a/lldb/tools/lldb-vscode/lldb-vscode.cpp +++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp @@ -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; _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits