[Lldb-commits] [lldb] b9139ac - Fix expression evaluation result expansion in lldb-vscode

Jeffrey Tan via lldb-commits lldb-commits at lists.llvm.org
Tue Aug 3 15:31:58 PDT 2021


Author: Jeffrey Tan
Date: 2021-08-03T15:24:44-07:00
New Revision: b9139acb85a4e5203364838e47b2e1ee90055ad3

URL: https://github.com/llvm/llvm-project/commit/b9139acb85a4e5203364838e47b2e1ee90055ad3
DIFF: https://github.com/llvm/llvm-project/commit/b9139acb85a4e5203364838e47b2e1ee90055ad3.diff

LOG: Fix expression evaluation result expansion in lldb-vscode

VScode now sends a "scopes" DAP request immediately after any expression evaluation.
This scopes request would clear and invalidate any non-scoped expandable variables in g_vsc.variables, causing later "variables" request to return empty result.
The symptom is that any expandable variables in VScode watch window/debug console UI to return empty content.

This diff fixes this issue by only clearing the expandable variables at process continue time. To achieve this, we have to repopulate all scoped variables
during context switch for each "scopes" request without clearing global expandable variables.
So the PR puts the scoped variables into its own locals/globals/registers; and all expandable variables into separate "expandableVariables" list.
Also, instead of using the variable index for "variableReference", it generates a new variableReference id each time as the key of "expandableVariables".

As a further new feature, this PR adds a new "expandablePermanentVariables" which has the lifetime of debug session. Any expandable variables from debug console
are added into this list. This enables users to snapshot expanable old variable in debug console and compare with new variables if desire.

Reviewed By: clayborg

Differential Revision: https://reviews.llvm.org/D105166

Added: 
    

Modified: 
    lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py
    lldb/tools/lldb-vscode/VSCode.cpp
    lldb/tools/lldb-vscode/VSCode.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 765cfbe6ed5a..092d96edf4eb 100644
--- a/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py
+++ b/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py
@@ -23,7 +23,7 @@ class TestVSCode_variables(lldbvscode_testcase.VSCodeTestCaseBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    def verify_values(self, verify_dict, actual, varref_dict=None):
+    def verify_values(self, verify_dict, actual, varref_dict=None, expression=None):
         if 'equals' in verify_dict:
             verify = verify_dict['equals']
             for key in verify:
@@ -48,9 +48,13 @@ def verify_values(self, verify_dict, actual, varref_dict=None):
         if hasVariablesReference:
             # Remember variable references in case we want to test further
             # by using the evaluate name.
-            varRef = actual['variablesReference']
+            varRef = actual["variablesReference"]
             if varRef != 0 and varref_dict is not None:
-                varref_dict[actual['evaluateName']] = varRef
+                if expression is None:
+                    evaluateName = actual["evaluateName"]
+                else:
+                    evaluateName = expression
+                varref_dict[evaluateName] = varRef
         if ('hasVariablesReference' in verify_dict and
                 verify_dict['hasVariablesReference']):
             self.assertTrue(hasVariablesReference,
@@ -282,3 +286,111 @@ def test_scopes_variables_setVariable_evaluate(self):
         self.assertNotIn('x @ main.cpp:21', names)
 
         self.verify_variables(verify_locals, locals)
+
+    @skipIfWindows
+    @skipIfRemote
+    def test_scopes_and_evaluate_expansion(self):
+        """
+        Tests the evaluated expression expands successfully after "scopes" packets
+        and permanent expressions persist.
+        """
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program)
+        source = "main.cpp"
+        breakpoint1_line = line_number(source, "// breakpoint 1")
+        lines = [breakpoint1_line]
+        # Set breakpoint in the thread function so we can step the threads
+        breakpoint_ids = self.set_source_breakpoints(source, lines)
+        self.assertEqual(
+            len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
+        )
+        self.continue_to_breakpoints(breakpoint_ids)
+
+        # Verify locals
+        locals = self.vscode.get_local_variables()
+        buffer_children = make_buffer_verify_dict(0, 32)
+        verify_locals = {
+            "argc": {"equals": {"type": "int", "value": "1"}},
+            "argv": {
+                "equals": {"type": "const char **"},
+                "startswith": {"value": "0x"},
+                "hasVariablesReference": True,
+            },
+            "pt": {
+                "equals": {"type": "PointType"},
+                "hasVariablesReference": True,
+                "children": {
+                    "x": {"equals": {"type": "int", "value": "11"}},
+                    "y": {"equals": {"type": "int", "value": "22"}},
+                    "buffer": {"children": buffer_children},
+                },
+            },
+            "x": {"equals": {"type": "int"}},
+        }
+        self.verify_variables(verify_locals, locals)
+
+        # Evaluate expandable expression twice: once permanent (from repl)
+        # the other temporary (from other UI).
+        expandable_expression = {
+            "name": "pt",
+            "response": {
+                "equals": {"type": "PointType"},
+                "startswith": {"result": "PointType @ 0x"},
+                "hasVariablesReference": True,
+            },
+            "children": {
+                "x": {"equals": {"type": "int", "value": "11"}},
+                "y": {"equals": {"type": "int", "value": "22"}},
+                "buffer": {"children": buffer_children},
+            },
+        }
+
+        # Evaluate from permanent UI.
+        permanent_expr_varref_dict = {}
+        response = self.vscode.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.vscode.request_evaluate(expandable_expression["name"])
+        self.verify_values(
+            expandable_expression["response"],
+            response["body"],
+            temporary_expr_varref_dict,
+            expandable_expression["name"],
+        )
+
+        # Evaluate locals again.
+        locals = self.vscode.get_local_variables()
+        self.verify_variables(verify_locals, locals)
+
+        # Verify the evaluated expressions before second locals evaluation
+        # can be expanded.
+        var_ref = temporary_expr_varref_dict[expandable_expression["name"]]
+        response = self.vscode.request_variables(var_ref)
+        self.verify_variables(
+            expandable_expression["children"], response["body"]["variables"]
+        )
+
+        # Continue to breakpoint 3, permanent variable should still exist
+        # after resume.
+        breakpoint3_line = line_number(source, "// breakpoint 3")
+        lines = [breakpoint3_line]
+        breakpoint_ids = self.set_source_breakpoints(source, lines)
+        self.assertEqual(
+            len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
+        )
+        self.continue_to_breakpoints(breakpoint_ids)
+
+        var_ref = permanent_expr_varref_dict[expandable_expression["name"]]
+        response = self.vscode.request_variables(var_ref)
+        self.verify_variables(
+            expandable_expression["children"], response["body"]["variables"]
+        )

diff  --git a/lldb/tools/lldb-vscode/VSCode.cpp b/lldb/tools/lldb-vscode/VSCode.cpp
index a67fdd735224..3209eea4a897 100644
--- a/lldb/tools/lldb-vscode/VSCode.cpp
+++ b/lldb/tools/lldb-vscode/VSCode.cpp
@@ -30,8 +30,7 @@ namespace lldb_vscode {
 VSCode g_vsc;
 
 VSCode::VSCode()
-    : variables(), broadcaster("lldb-vscode"), num_regs(0), num_locals(0),
-      num_globals(0), log(),
+    : broadcaster("lldb-vscode"),
       exception_breakpoints(
           {{"cpp_catch", "C++ Catch", lldb::eLanguageTypeC_plus_plus},
            {"cpp_throw", "C++ Throw", lldb::eLanguageTypeC_plus_plus},
@@ -382,10 +381,12 @@ lldb::SBFrame VSCode::GetLLDBFrame(const llvm::json::Object &arguments) {
 
 llvm::json::Value VSCode::CreateTopLevelScopes() {
   llvm::json::Array scopes;
-  scopes.emplace_back(CreateScope("Locals", VARREF_LOCALS, num_locals, false));
-  scopes.emplace_back(
-      CreateScope("Globals", VARREF_GLOBALS, num_globals, false));
-  scopes.emplace_back(CreateScope("Registers", VARREF_REGS, num_regs, false));
+  scopes.emplace_back(CreateScope("Locals", VARREF_LOCALS,
+                                  g_vsc.variables.locals.GetSize(), false));
+  scopes.emplace_back(CreateScope("Globals", VARREF_GLOBALS,
+                                  g_vsc.variables.globals.GetSize(), false));
+  scopes.emplace_back(CreateScope("Registers", VARREF_REGS,
+                                  g_vsc.variables.registers.GetSize(), false));
   return llvm::json::Value(std::move(scopes));
 }
 
@@ -527,4 +528,44 @@ void VSCode::RegisterRequestCallback(std::string request,
   request_handlers[request] = callback;
 }
 
+void Variables::Clear() {
+  locals.Clear();
+  globals.Clear();
+  registers.Clear();
+  expandable_variables.clear();
+}
+
+int64_t Variables::GetNewVariableRefence(bool is_permanent) {
+  if (is_permanent)
+    return next_permanent_var_ref++;
+  return next_temporary_var_ref++;
+}
+
+bool Variables::IsPermanentVariableReference(int64_t var_ref) {
+  return var_ref >= PermanentVariableStartIndex;
+}
+
+lldb::SBValue Variables::GetVariable(int64_t var_ref) const {
+  if (IsPermanentVariableReference(var_ref)) {
+    auto pos = expandable_permanent_variables.find(var_ref);
+    if (pos != expandable_permanent_variables.end())
+      return pos->second;
+  } else {
+    auto pos = expandable_variables.find(var_ref);
+    if (pos != expandable_variables.end())
+      return pos->second;
+  }
+  return lldb::SBValue();
+}
+
+int64_t Variables::InsertExpandableVariable(lldb::SBValue variable,
+                                            bool is_permanent) {
+  int64_t var_ref = GetNewVariableRefence(is_permanent);
+  if (is_permanent)
+    expandable_permanent_variables.insert(std::make_pair(var_ref, variable));
+  else
+    expandable_variables.insert(std::make_pair(var_ref, variable));
+  return var_ref;
+}
+
 } // namespace lldb_vscode

diff  --git a/lldb/tools/lldb-vscode/VSCode.h b/lldb/tools/lldb-vscode/VSCode.h
index 29796767c9df..602cf758a9a1 100644
--- a/lldb/tools/lldb-vscode/VSCode.h
+++ b/lldb/tools/lldb-vscode/VSCode.h
@@ -58,9 +58,6 @@
 #define VARREF_GLOBALS (int64_t)2
 #define VARREF_REGS (int64_t)3
 #define VARREF_FIRST_VAR_IDX (int64_t)4
-#define VARREF_IS_SCOPE(v) (VARREF_LOCALS <= 1 && v < VARREF_FIRST_VAR_IDX)
-#define VARIDX_TO_VARREF(i) ((i) + VARREF_FIRST_VAR_IDX)
-#define VARREF_TO_VARIDX(v) ((v)-VARREF_FIRST_VAR_IDX)
 #define NO_TYPENAME "<no-type>"
 
 namespace lldb_vscode {
@@ -83,17 +80,54 @@ enum class PacketStatus {
   JSONNotObject
 };
 
+struct Variables {
+  /// Variable_reference start index of permanent expandable variable.
+  static constexpr int64_t PermanentVariableStartIndex = (1ll << 32);
+
+  lldb::SBValueList locals;
+  lldb::SBValueList globals;
+  lldb::SBValueList registers;
+
+  int64_t next_temporary_var_ref{VARREF_FIRST_VAR_IDX};
+  int64_t next_permanent_var_ref{PermanentVariableStartIndex};
+
+  /// Expandable variables that are alive in this stop state.
+  /// Will be cleared when debuggee resumes.
+  llvm::DenseMap<int64_t, lldb::SBValue> expandable_variables;
+  /// Expandable variables that persist across entire debug session.
+  /// These are the variables evaluated from debug console REPL.
+  llvm::DenseMap<int64_t, lldb::SBValue> expandable_permanent_variables;
+
+  /// Check if \p var_ref points to a variable that should persist for the
+  /// entire duration of the debug session, e.g. repl expandable variables
+  static bool IsPermanentVariableReference(int64_t var_ref);
+
+  /// \return a new variableReference.
+  /// Specify is_permanent as true for variable that should persist entire
+  /// debug session.
+  int64_t GetNewVariableRefence(bool is_permanent);
+
+  /// \return the expandable variable corresponding with variableReference
+  /// value of \p value.
+  /// If \p var_ref is invalid an empty SBValue is returned.
+  lldb::SBValue GetVariable(int64_t var_ref) const;
+
+  /// Insert a new \p variable.
+  /// \return variableReference assigned to this expandable variable.
+  int64_t InsertExpandableVariable(lldb::SBValue variable, bool is_permanent);
+
+  /// Clear all scope variables and non-permanent expandable variables.
+  void Clear();
+};
+
 struct VSCode {
   std::string debug_adaptor_path;
   InputStream input;
   OutputStream output;
   lldb::SBDebugger debugger;
   lldb::SBTarget target;
-  lldb::SBValueList variables;
+  Variables variables;
   lldb::SBBroadcaster broadcaster;
-  int64_t num_regs;
-  int64_t num_locals;
-  int64_t num_globals;
   std::thread event_thread;
   std::thread progress_event_thread;
   std::unique_ptr<std::ofstream> log;
@@ -206,6 +240,9 @@ struct VSCode {
   ///     IDE.
   void RegisterRequestCallback(std::string request, RequestCallback callback);
 
+  /// Debuggee will continue from stopped state.
+  void WillContinue() { variables.Clear(); }
+
 private:
   // Send the JSON in "json_str" to the "out" stream. Correctly send the
   // "Content-Length:" field followed by the length, followed by the raw

diff  --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp
index 9ab13cf140c6..59d8debe6378 100644
--- a/lldb/tools/lldb-vscode/lldb-vscode.cpp
+++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp
@@ -107,6 +107,19 @@ typedef void (*RequestCallback)(const llvm::json::Object &command);
 
 enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
 
+lldb::SBValueList *GetTopLevelScope(int64_t variablesReference) {
+  switch (variablesReference) {
+  case VARREF_LOCALS:
+    return &g_vsc.variables.locals;
+  case VARREF_GLOBALS:
+    return &g_vsc.variables.globals;
+  case VARREF_REGS:
+    return &g_vsc.variables.registers;
+  default:
+    return nullptr;
+  }
+}
+
 SOCKET AcceptConnection(int portno) {
   // Accept a socket connection from any host on "portno".
   SOCKET newsockfd = -1;
@@ -439,6 +452,7 @@ void EventThreadFunction() {
             }
             break;
           case lldb::eStateRunning:
+            g_vsc.WillContinue();
             break;
           case lldb::eStateExited: {
             // Run any exit LLDB commands the user specified in the
@@ -1196,6 +1210,10 @@ void request_evaluate(const llvm::json::Object &request) {
     lldb::SBValue value = frame.GetValueForVariablePath(
         expression.data(), lldb::eDynamicDontRunTarget);
 
+    // Freeze dry the value in case users expand it later in the debug console
+    if (value.GetError().Success() && context == "repl")
+      value = value.Persist();
+
     if (value.GetError().Fail() && context != "hover")
       value = frame.EvaluateExpression(expression.data());
 
@@ -1215,9 +1233,9 @@ void request_evaluate(const llvm::json::Object &request) {
       EmplaceSafeString(body, "type",
                         value_typename ? value_typename : NO_TYPENAME);
       if (value.MightHaveChildren()) {
-        auto variablesReference = VARIDX_TO_VARREF(g_vsc.variables.GetSize());
-        g_vsc.variables.Append(value);
-        body.try_emplace("variablesReference", variablesReference);
+        auto variableReference = g_vsc.variables.InsertExpandableVariable(
+            value, /*is_permanent=*/context == "repl");
+        body.try_emplace("variablesReference", variableReference);
       } else {
         body.try_emplace("variablesReference", (int64_t)0);
       }
@@ -1895,20 +1913,15 @@ void request_scopes(const llvm::json::Object &request) {
     frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
     frame.GetThread().SetSelectedFrame(frame.GetFrameID());
   }
-  g_vsc.variables.Clear();
-  g_vsc.variables.Append(frame.GetVariables(true,   // arguments
-                                            true,   // locals
-                                            false,  // statics
-                                            true)); // in_scope_only
-  g_vsc.num_locals = g_vsc.variables.GetSize();
-  g_vsc.variables.Append(frame.GetVariables(false,  // arguments
-                                            false,  // locals
-                                            true,   // statics
-                                            true)); // in_scope_only
-  g_vsc.num_globals = g_vsc.variables.GetSize() - (g_vsc.num_locals);
-  g_vsc.variables.Append(frame.GetRegisters());
-  g_vsc.num_regs =
-      g_vsc.variables.GetSize() - (g_vsc.num_locals + g_vsc.num_globals);
+  g_vsc.variables.locals = frame.GetVariables(/*arguments=*/true,
+                                              /*locals=*/true,
+                                              /*statics=*/false,
+                                              /*in_scope_only=*/true);
+  g_vsc.variables.globals = frame.GetVariables(/*arguments=*/false,
+                                               /*locals=*/false,
+                                               /*statics=*/true,
+                                               /*in_scope_only=*/true);
+  g_vsc.variables.registers = frame.GetRegisters();
   body.try_emplace("scopes", g_vsc.CreateTopLevelScopes());
   response.try_emplace("body", std::move(body));
   g_vsc.SendJSON(llvm::json::Value(std::move(response)));
@@ -2750,37 +2763,20 @@ void request_setVariable(const llvm::json::Object &request) {
   // of the variable within the g_vsc.variables list.
   const auto id_value = GetUnsigned(arguments, "id", UINT64_MAX);
   if (id_value != UINT64_MAX) {
-    variable = g_vsc.variables.GetValueAtIndex(id_value);
-  } else if (VARREF_IS_SCOPE(variablesReference)) {
+    variable = g_vsc.variables.GetVariable(id_value);
+  } else if (lldb::SBValueList *top_scope =
+                 GetTopLevelScope(variablesReference)) {
     // variablesReference is one of our scopes, not an actual variable it is
     // asking for a variable in locals or globals or registers
-    int64_t start_idx = 0;
-    int64_t end_idx = 0;
-    switch (variablesReference) {
-    case VARREF_LOCALS:
-      start_idx = 0;
-      end_idx = start_idx + g_vsc.num_locals;
-      break;
-    case VARREF_GLOBALS:
-      start_idx = g_vsc.num_locals;
-      end_idx = start_idx + g_vsc.num_globals;
-      break;
-    case VARREF_REGS:
-      start_idx = g_vsc.num_locals + g_vsc.num_globals;
-      end_idx = start_idx + g_vsc.num_regs;
-      break;
-    default:
-      break;
-    }
-
-    for (int64_t i = end_idx - 1; i >= start_idx; --i) {
-      lldb::SBValue curr_variable = g_vsc.variables.GetValueAtIndex(i);
+    int64_t end_idx = top_scope->GetSize();
+    // Searching backward so that we choose the variable in closest scope
+    // among variables of the same name.
+    for (int64_t i = end_idx - 1; i >= 0; --i) {
+      lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i);
       std::string variable_name = CreateUniqueVariableNameForDisplay(
           curr_variable, is_duplicated_variable_name);
       if (variable_name == name) {
         variable = curr_variable;
-        if (curr_variable.MightHaveChildren())
-          newVariablesReference = i;
         break;
       }
     }
@@ -2790,8 +2786,7 @@ void request_setVariable(const llvm::json::Object &request) {
 
     // We have a named item within an actual variable so we need to find it
     // withing the container variable by name.
-    const int64_t var_idx = VARREF_TO_VARIDX(variablesReference);
-    lldb::SBValue container = g_vsc.variables.GetValueAtIndex(var_idx);
+    lldb::SBValue container = g_vsc.variables.GetVariable(variablesReference);
     variable = container.GetChildMemberWithName(name.data());
     if (!variable.IsValid()) {
       if (name.startswith("[")) {
@@ -2803,14 +2798,6 @@ void request_setVariable(const llvm::json::Object &request) {
         }
       }
     }
-
-    // We don't know the index of the variable in our g_vsc.variables
-    if (variable.IsValid()) {
-      if (variable.MightHaveChildren()) {
-        newVariablesReference = VARIDX_TO_VARREF(g_vsc.variables.GetSize());
-        g_vsc.variables.Append(variable);
-      }
-    }
   }
 
   if (variable.IsValid()) {
@@ -2819,6 +2806,15 @@ void request_setVariable(const llvm::json::Object &request) {
     if (success) {
       SetValueForKey(variable, body, "value");
       EmplaceSafeString(body, "type", variable.GetType().GetDisplayTypeName());
+
+      // We don't know the index of the variable in our g_vsc.variables
+      // so always insert a new one to get its variablesReference.
+      // is_permanent is false because debug console does not support
+      // setVariable request.
+      if (variable.MightHaveChildren())
+        newVariablesReference = g_vsc.variables.InsertExpandableVariable(
+            variable, /*is_permanent=*/false);
+
       body.try_emplace("variablesReference", newVariablesReference);
     } else {
       EmplaceSafeString(body, "message", std::string(error.GetCString()));
@@ -2919,33 +2915,19 @@ void request_variables(const llvm::json::Object &request) {
   if (format)
     hex = GetBoolean(format, "hex", false);
 
-  if (VARREF_IS_SCOPE(variablesReference)) {
+  if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) {
     // variablesReference is one of our scopes, not an actual variable it is
     // asking for the list of args, locals or globals.
     int64_t start_idx = 0;
     int64_t num_children = 0;
-    switch (variablesReference) {
-    case VARREF_LOCALS:
-      start_idx = start;
-      num_children = g_vsc.num_locals;
-      break;
-    case VARREF_GLOBALS:
-      start_idx = start + g_vsc.num_locals + start;
-      num_children = g_vsc.num_globals;
-      break;
-    case VARREF_REGS:
-      start_idx = start + g_vsc.num_locals + g_vsc.num_globals;
-      num_children = g_vsc.num_regs;
-      break;
-    default:
-      break;
-    }
+
+    num_children = top_scope->GetSize();
     const int64_t end_idx = start_idx + ((count == 0) ? num_children : count);
 
     // We first find out which variable names are duplicated
     std::map<std::string, int> variable_name_counts;
     for (auto i = start_idx; i < end_idx; ++i) {
-      lldb::SBValue variable = g_vsc.variables.GetValueAtIndex(i);
+      lldb::SBValue variable = top_scope->GetValueAtIndex(i);
       if (!variable.IsValid())
         break;
       variable_name_counts[GetNonNullVariableName(variable)]++;
@@ -2953,19 +2935,24 @@ void request_variables(const llvm::json::Object &request) {
 
     // Now we construct the result with unique display variable names
     for (auto i = start_idx; i < end_idx; ++i) {
-      lldb::SBValue variable = g_vsc.variables.GetValueAtIndex(i);
+      lldb::SBValue variable = top_scope->GetValueAtIndex(i);
 
       if (!variable.IsValid())
         break;
-      variables.emplace_back(CreateVariable(variable, VARIDX_TO_VARREF(i), i,
-                                            hex,
+
+      int64_t var_ref = 0;
+      if (variable.MightHaveChildren()) {
+        var_ref = g_vsc.variables.InsertExpandableVariable(
+            variable, /*is_permanent=*/false);
+      }
+      variables.emplace_back(CreateVariable(
+          variable, var_ref, var_ref != 0 ? var_ref : UINT64_MAX, hex,
           variable_name_counts[GetNonNullVariableName(variable)] > 1));
     }
   } else {
     // We are expanding a variable that has children, so we will return its
     // children.
-    const int64_t var_idx = VARREF_TO_VARIDX(variablesReference);
-    lldb::SBValue variable = g_vsc.variables.GetValueAtIndex(var_idx);
+    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);
@@ -2974,11 +2961,12 @@ void request_variables(const llvm::json::Object &request) {
         if (!child.IsValid())
           break;
         if (child.MightHaveChildren()) {
-          const int64_t var_idx = g_vsc.variables.GetSize();
-          auto childVariablesReferences = VARIDX_TO_VARREF(var_idx);
-          variables.emplace_back(
-              CreateVariable(child, childVariablesReferences, var_idx, hex));
-          g_vsc.variables.Append(child);
+          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));
         } else {
           variables.emplace_back(CreateVariable(child, 0, INT64_MAX, hex));
         }


        


More information about the lldb-commits mailing list