[Lldb-commits] [lldb] lldb-dap: Stop using replicated variable ids (PR #124232)
Anthony Eid via lldb-commits
lldb-commits at lists.llvm.org
Fri Jan 30 02:18:12 PST 2026
https://github.com/Anthony-Eid updated https://github.com/llvm/llvm-project/pull/124232
>From 30658e994b18b7c0db114a297036421c8de2dea3 Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Wed, 27 Aug 2025 13:04:26 -0400
Subject: [PATCH 01/39] Fix variable request from reusing variable_ids
---
.../lldb-dap/Handler/ScopesRequestHandler.cpp | 45 ++++++-----
lldb/tools/lldb-dap/JSONUtils.h | 1 +
lldb/tools/lldb-dap/Variables.cpp | 80 +++++++++++++++++--
lldb/tools/lldb-dap/Variables.h | 18 ++++-
4 files changed, 119 insertions(+), 25 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
index aaad0e20f9c21..160d8e264d089 100644
--- a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
@@ -29,8 +29,8 @@ namespace lldb_dap {
///
/// \return
/// A `protocol::Scope`
-static Scope CreateScope(const llvm::StringRef name, int64_t variablesReference,
- int64_t namedVariables, bool expensive) {
+Scope CreateScope(const llvm::StringRef name, int64_t variablesReference,
+ int64_t namedVariables, bool expensive) {
Scope scope;
scope.name = name;
@@ -75,22 +75,31 @@ ScopesRequestHandler::Run(const ScopesArguments &args) const {
frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
frame.GetThread().SetSelectedFrame(frame.GetFrameID());
}
- dap.variables.locals = frame.GetVariables(/*arguments=*/true,
- /*locals=*/true,
- /*statics=*/false,
- /*in_scope_only=*/true);
- dap.variables.globals = frame.GetVariables(/*arguments=*/false,
- /*locals=*/false,
- /*statics=*/true,
- /*in_scope_only=*/true);
- dap.variables.registers = frame.GetRegisters();
-
- std::vector scopes = {CreateScope("Locals", VARREF_LOCALS,
- dap.variables.locals.GetSize(), false),
- CreateScope("Globals", VARREF_GLOBALS,
- dap.variables.globals.GetSize(), false),
- CreateScope("Registers", VARREF_REGS,
- dap.variables.registers.GetSize(), false)};
+
+ uint32_t frame_id = frame.GetFrameID();
+
+ dap.variables.ReadyFrame(frame_id, frame);
+ dap.variables.SwitchFrame(frame_id);
+
+ std::vector<Scope> scopes = {};
+
+ int64_t variable_reference = dap.variables.GetNewVariableReference(false);
+ scopes.push_back(CreateScope("Locals", variable_reference,
+ dap.variables.locals.GetSize(), false));
+
+ dap.variables.AddScopeKind(variable_reference, ScopeKind::Locals, frame_id);
+
+ variable_reference = dap.variables.GetNewVariableReference(false);
+ scopes.push_back(CreateScope("Globals", variable_reference,
+ dap.variables.globals.GetSize(), false));
+ dap.variables.AddScopeKind(variable_reference, ScopeKind::Globals, frame_id);
+
+ variable_reference = dap.variables.GetNewVariableReference(false);
+ scopes.push_back(CreateScope("Registers", variable_reference,
+ dap.variables.registers.GetSize(), false));
+
+ dap.variables.AddScopeKind(variable_reference, ScopeKind::Registers,
+ frame_id);
return ScopesResponseBody{std::move(scopes)};
}
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index e9094f67b94ec..6575411acd878 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -9,6 +9,7 @@
#ifndef LLDB_TOOLS_LLDB_DAP_JSONUTILS_H
#define LLDB_TOOLS_LLDB_DAP_JSONUTILS_H
+#include "DAP.h"
#include "DAPForward.h"
#include "Protocol/ProtocolTypes.h"
#include "lldb/API/SBCompileUnit.h"
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 777e3183d8c0d..9ed3773df817d 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -8,20 +8,33 @@
#include "Variables.h"
#include "JSONUtils.h"
+#include "lldb/API/SBFrame.h"
using namespace lldb_dap;
lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
- switch (variablesReference) {
- case VARREF_LOCALS:
+ auto iter = m_scope_kinds.find(variablesReference);
+ if (iter == m_scope_kinds.end()) {
+ return nullptr;
+ }
+
+ ScopeKind scope_kind = iter->second.first;
+ uint32_t frame_id = iter->second.second;
+
+ if (!SwitchFrame(frame_id)) {
+ return nullptr;
+ }
+
+ switch (scope_kind) {
+ case lldb_dap::ScopeKind::Locals:
return &locals;
- case VARREF_GLOBALS:
+ case lldb_dap::ScopeKind::Globals:
return &globals;
- case VARREF_REGS:
+ case lldb_dap::ScopeKind::Registers:
return ®isters;
- default:
- return nullptr;
}
+
+ return nullptr;
}
void Variables::Clear() {
@@ -29,6 +42,8 @@ void Variables::Clear() {
globals.Clear();
registers.Clear();
m_referencedvariables.clear();
+ m_frames.clear();
+ m_next_temporary_var_ref = VARREF_FIRST_VAR_IDX;
}
int64_t Variables::GetNewVariableReference(bool is_permanent) {
@@ -103,3 +118,56 @@ lldb::SBValue Variables::FindVariable(uint64_t variablesReference,
}
return variable;
}
+
+std::optional<ScopeKind>
+Variables::GetScopeKind(const int64_t variablesReference) {
+ auto iter = m_scope_kinds.find(variablesReference);
+ if (iter != m_scope_kinds.end()) {
+ return iter->second.first;
+ }
+
+ return std::nullopt;
+}
+
+bool Variables::SwitchFrame(const uint32_t frame_id) {
+ auto iter = m_frames.find(frame_id);
+
+ if (iter == m_frames.end()) {
+ return false;
+ }
+
+ auto [frame_locals, frame_globals, frame_registers] = iter->second;
+
+ locals = frame_locals;
+ globals = frame_globals;
+ registers = frame_registers;
+
+ return true;
+}
+
+void Variables::ReadyFrame(uint32_t frame_id, lldb::SBFrame &frame) {
+ if (m_frames.find(frame_id) == m_frames.end()) {
+
+ auto locals = frame.GetVariables(/*arguments=*/true,
+ /*locals=*/true,
+ /*statics=*/false,
+ /*in_scope_only=*/true);
+
+ auto globals = frame.GetVariables(/*arguments=*/false,
+ /*locals=*/false,
+ /*statics=*/true,
+ /*in_scope_only=*/true);
+
+ auto registers = frame.GetRegisters();
+
+ m_frames.insert(
+ std::make_pair(frame_id, std::make_tuple(locals, globals, registers)));
+ }
+}
+
+void Variables::AddScopeKind(int64_t variable_reference, ScopeKind kind,
+ uint32_t frame_id) {
+
+ m_scope_kinds[variable_reference] =
+ std::make_pair(ScopeKind::Globals, frame_id);
+}
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index 0ed84b36aef99..2f40f87e9b4bb 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -12,6 +12,7 @@
#include "lldb/API/SBValue.h"
#include "lldb/API/SBValueList.h"
#include "llvm/ADT/DenseMap.h"
+#include <map>
#define VARREF_FIRST_VAR_IDX (int64_t)4
#define VARREF_LOCALS (int64_t)1
@@ -20,6 +21,8 @@
namespace lldb_dap {
+enum ScopeKind { Locals, Globals, Registers };
+
struct Variables {
lldb::SBValueList locals;
lldb::SBValueList globals;
@@ -47,12 +50,23 @@ struct Variables {
lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name);
+ bool SwitchFrame(uint32_t frame_id);
+ /// Initialize a frame if it hasn't been already, otherwise do nothing
+ void ReadyFrame(uint32_t frame_id, lldb::SBFrame &frame);
+ std::optional<ScopeKind> GetScopeKind(const int64_t variablesReference);
+
/// Clear all scope variables and non-permanent expandable variables.
void Clear();
+ void AddScopeKind(int64_t variable_reference, ScopeKind kind,
+ uint32_t frame_id);
+
private:
/// Variable_reference start index of permanent expandable variable.
static constexpr int64_t PermanentVariableStartIndex = (1ll << 32);
+ int64_t m_next_temporary_var_ref{VARREF_FIRST_VAR_IDX};
+
+ std::map<int, std::pair<ScopeKind, uint32_t>> m_scope_kinds;
/// Variables that are alive in this stop state.
/// Will be cleared when debuggee resumes.
@@ -62,7 +76,9 @@ struct Variables {
/// These are the variables evaluated from debug console REPL.
llvm::DenseMap<int64_t, lldb::SBValue> m_referencedpermanent_variables;
- int64_t m_next_temporary_var_ref{VARREF_FIRST_VAR_IDX};
+ std::map<uint32_t,
+ std::tuple<lldb::SBValueList, lldb::SBValueList, lldb::SBValueList>>
+ m_frames;
int64_t m_next_permanent_var_ref{PermanentVariableStartIndex};
};
>From 9707978154cc43ee6f7e3d54d0159a0c40d5bbf0 Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Wed, 27 Aug 2025 13:12:02 -0400
Subject: [PATCH 02/39] Add createScope changes
---
lldb/tools/lldb-dap/JSONUtils.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 4f26599a49bac..7c256481f8c71 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -11,6 +11,7 @@
#include "ExceptionBreakpoint.h"
#include "LLDBUtils.h"
#include "ProtocolUtils.h"
+#include "Variables.h"
#include "lldb/API/SBAddress.h"
#include "lldb/API/SBCompileUnit.h"
#include "lldb/API/SBDeclaration.h"
@@ -358,16 +359,16 @@ void FillResponse(const llvm::json::Object &request,
// "required": [ "name", "variablesReference", "expensive" ]
// }
llvm::json::Value CreateScope(const llvm::StringRef name,
- int64_t variablesReference,
+ int64_t variablesReference, ScopeKind kind,
int64_t namedVariables, bool expensive) {
llvm::json::Object object;
EmplaceSafeString(object, "name", name.str());
// TODO: Support "arguments" scope. At the moment lldb-dap includes the
// arguments into the "locals" scope.
- if (variablesReference == VARREF_LOCALS) {
+ if (kind == ScopeKind::Locals) {
object.try_emplace("presentationHint", "locals");
- } else if (variablesReference == VARREF_REGS) {
+ } else if (kind == ScopeKind::Registers) {
object.try_emplace("presentationHint", "registers");
}
>From 50230c201673a58236bf5bccfe3036ca5d14cc95 Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Wed, 27 Aug 2025 18:19:44 -0400
Subject: [PATCH 03/39] Fix some bugs
---
lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp | 1 +
lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp | 4 ++--
lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp | 7 ++++---
lldb/tools/lldb-dap/Variables.cpp | 4 ++--
4 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
index e1556846dff19..e207f830183b6 100644
--- a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
@@ -162,6 +162,7 @@ void EvaluateRequestHandler::operator()(
// focus_tid to the current frame for any thread related events.
if (frame.IsValid()) {
dap.focus_tid = frame.GetThread().GetThreadID();
+ dap.variables.SwitchFrame(frame.GetFrameID());
}
bool required_command_failed = false;
diff --git a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
index 160d8e264d089..553a7b30adba2 100644
--- a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
@@ -41,9 +41,9 @@ Scope CreateScope(const llvm::StringRef name, int64_t variablesReference,
// if we add the arguments above the local scope as the locals scope will not
// be expanded if we enter a function with arguments. It becomes more
// annoying when the scope has arguments, return_value and locals.
- if (variablesReference == VARREF_LOCALS)
+ if (name == "Locals")
scope.presentationHint = Scope::eScopePresentationHintLocals;
- else if (variablesReference == VARREF_REGS)
+ else if (name == "Registers")
scope.presentationHint = Scope::eScopePresentationHintRegisters;
scope.variablesReference = variablesReference;
diff --git a/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
index 5fa2b1ef5e20d..3d99983ac0c83 100644
--- a/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
@@ -38,7 +38,8 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
int64_t start_idx = 0;
int64_t num_children = 0;
- if (var_ref == VARREF_REGS) {
+ std::optional<ScopeKind> scope_kind = dap.variables.GetScopeKind(var_ref);
+ if (scope_kind && *scope_kind == ScopeKind::Registers) {
// Change the default format of any pointer sized registers in the first
// register set to be the lldb::eFormatAddressInfo so we show the pointer
// and resolve what the pointer resolves to. Only change the format if the
@@ -58,7 +59,7 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
}
num_children = top_scope->GetSize();
- if (num_children == 0 && var_ref == VARREF_LOCALS) {
+ if (num_children == 0 && scope_kind && *scope_kind == ScopeKind::Locals) {
// Check for an error in the SBValueList that might explain why we don't
// have locals. If we have an error display it as the sole value in the
// the locals.
@@ -94,7 +95,7 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
}
// Show return value if there is any ( in the local top frame )
- if (var_ref == VARREF_LOCALS) {
+ if (scope_kind && *scope_kind == ScopeKind::Locals) {
auto process = dap.target.GetProcess();
auto selected_thread = process.GetSelectedThread();
lldb::SBValue stop_return_value = selected_thread.GetStopReturnValue();
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 9ed3773df817d..b5cd9d69d385f 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -42,6 +42,7 @@ void Variables::Clear() {
globals.Clear();
registers.Clear();
m_referencedvariables.clear();
+ m_scope_kinds.clear();
m_frames.clear();
m_next_temporary_var_ref = VARREF_FIRST_VAR_IDX;
}
@@ -168,6 +169,5 @@ void Variables::ReadyFrame(uint32_t frame_id, lldb::SBFrame &frame) {
void Variables::AddScopeKind(int64_t variable_reference, ScopeKind kind,
uint32_t frame_id) {
- m_scope_kinds[variable_reference] =
- std::make_pair(ScopeKind::Globals, frame_id);
+ m_scope_kinds[variable_reference] = std::make_pair(kind, frame_id);
}
>From c73d4c4f82afbee0399396909a0becf98aac257e Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Wed, 27 Aug 2025 18:31:25 -0400
Subject: [PATCH 04/39] remove unused GetScopedsKind func
---
lldb/tools/lldb-dap/JSONUtils.cpp | 85 -------------------------------
lldb/tools/lldb-dap/Variables.h | 2 +-
2 files changed, 1 insertion(+), 86 deletions(-)
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 7c256481f8c71..ad1f035caaf54 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -293,91 +293,6 @@ void FillResponse(const llvm::json::Object &request,
response.try_emplace("success", true);
}
-// "Scope": {
-// "type": "object",
-// "description": "A Scope is a named container for variables. Optionally
-// a scope can map to a source or a range within a source.",
-// "properties": {
-// "name": {
-// "type": "string",
-// "description": "Name of the scope such as 'Arguments', 'Locals'."
-// },
-// "presentationHint": {
-// "type": "string",
-// "description": "An optional hint for how to present this scope in the
-// UI. If this attribute is missing, the scope is shown
-// with a generic UI.",
-// "_enum": [ "arguments", "locals", "registers" ],
-// },
-// "variablesReference": {
-// "type": "integer",
-// "description": "The variables of this scope can be retrieved by
-// passing the value of variablesReference to the
-// VariablesRequest."
-// },
-// "namedVariables": {
-// "type": "integer",
-// "description": "The number of named variables in this scope. The
-// client can use this optional information to present
-// the variables in a paged UI and fetch them in chunks."
-// },
-// "indexedVariables": {
-// "type": "integer",
-// "description": "The number of indexed variables in this scope. The
-// client can use this optional information to present
-// the variables in a paged UI and fetch them in chunks."
-// },
-// "expensive": {
-// "type": "boolean",
-// "description": "If true, the number of variables in this scope is
-// large or expensive to retrieve."
-// },
-// "source": {
-// "$ref": "#/definitions/Source",
-// "description": "Optional source for this scope."
-// },
-// "line": {
-// "type": "integer",
-// "description": "Optional start line of the range covered by this
-// scope."
-// },
-// "column": {
-// "type": "integer",
-// "description": "Optional start column of the range covered by this
-// scope."
-// },
-// "endLine": {
-// "type": "integer",
-// "description": "Optional end line of the range covered by this scope."
-// },
-// "endColumn": {
-// "type": "integer",
-// "description": "Optional end column of the range covered by this
-// scope."
-// }
-// },
-// "required": [ "name", "variablesReference", "expensive" ]
-// }
-llvm::json::Value CreateScope(const llvm::StringRef name,
- int64_t variablesReference, ScopeKind kind,
- int64_t namedVariables, bool expensive) {
- llvm::json::Object object;
- EmplaceSafeString(object, "name", name.str());
-
- // TODO: Support "arguments" scope. At the moment lldb-dap includes the
- // arguments into the "locals" scope.
- if (kind == ScopeKind::Locals) {
- object.try_emplace("presentationHint", "locals");
- } else if (kind == ScopeKind::Registers) {
- object.try_emplace("presentationHint", "registers");
- }
-
- object.try_emplace("variablesReference", variablesReference);
- object.try_emplace("expensive", expensive);
- object.try_emplace("namedVariables", namedVariables);
- return llvm::json::Value(std::move(object));
-}
-
// "Event": {
// "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
// "type": "object",
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index 2f40f87e9b4bb..a8fad707e2e08 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -66,7 +66,7 @@ struct Variables {
static constexpr int64_t PermanentVariableStartIndex = (1ll << 32);
int64_t m_next_temporary_var_ref{VARREF_FIRST_VAR_IDX};
- std::map<int, std::pair<ScopeKind, uint32_t>> m_scope_kinds;
+ std::map<int64_t, std::pair<ScopeKind, uint32_t>> m_scope_kinds;
/// Variables that are alive in this stop state.
/// Will be cleared when debuggee resumes.
>From 2712abdb5a21e466a4ee8a289c52c8bc9e537c63 Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Thu, 28 Aug 2025 00:25:00 -0400
Subject: [PATCH 05/39] Fix hardcoded scope references in DAP variable handling
The test and API changes now get scope references dynamically instead of
using hardcoded values.
This fixes the below tests:
- testDAP_memory
- TestDAP_variables
- GetTopLevelScope_ReturnsCorrectScope
---
.../test/tools/lldb-dap/lldbdap_testcase.py | 33 ++++++++++++++++--
.../tools/lldb-dap/memory/TestDAP_memory.py | 4 ++-
.../lldb-dap/variables/TestDAP_variables.py | 17 +++++-----
lldb/unittests/DAP/VariablesTest.cpp | 34 ++++++++++++++++---
4 files changed, 73 insertions(+), 15 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index b28a78792c70f..a26be6e5d1bfb 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -351,11 +351,40 @@ def get_local_as_int(self, name, threadId=None):
def set_local(self, name, value, id=None):
"""Set a top level local variable only."""
- return self.dap_server.request_setVariable(1, name, str(value), id=id)
+ # Get the locals scope reference dynamically
+ locals_ref = self.get_locals_scope_reference()
+ if locals_ref is None:
+ return None
+ return self.dap_server.request_setVariable(locals_ref, name, str(value), id=id)
def set_global(self, name, value, id=None):
"""Set a top level global variable only."""
- return self.dap_server.request_setVariable(2, name, str(value), id=id)
+ # Get the globals scope reference dynamically
+ stackFrame = self.dap_server.get_stackFrame()
+ if stackFrame is None:
+ return None
+ frameId = stackFrame["id"]
+ scopes_response = self.dap_server.request_scopes(frameId)
+ frame_scopes = scopes_response["body"]["scopes"]
+ for scope in frame_scopes:
+ if scope["name"] == "Globals":
+ varRef = scope["variablesReference"]
+ return self.dap_server.request_setVariable(varRef, name, str(value), id=id)
+ return None
+
+
+ def get_locals_scope_reference(self):
+ """Get the variablesReference for the locals scope."""
+ stackFrame = self.dap_server.get_stackFrame()
+ if stackFrame is None:
+ return None
+ frameId = stackFrame["id"]
+ scopes_response = self.dap_server.request_scopes(frameId)
+ frame_scopes = scopes_response["body"]["scopes"]
+ for scope in frame_scopes:
+ if scope["name"] == "Locals":
+ return scope["variablesReference"]
+ return None
def stepIn(
self,
diff --git a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py
index f51056d7020c6..5e1dc8d30ff8d 100644
--- a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py
+++ b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py
@@ -70,9 +70,11 @@ def test_memory_refs_set_variable(self):
self.continue_to_next_stop()
ptr_value = self.get_local_as_int("rawptr")
+ locals_ref = self.get_locals_scope_reference()
+ self.assertIsNotNone(locals_ref, "Failed to get locals scope reference")
self.assertIn(
"memoryReference",
- self.dap_server.request_setVariable(1, "rawptr", ptr_value + 2)[
+ self.dap_server.request_setVariable(locals_ref, "rawptr", ptr_value + 2)[
"body"
].keys(),
)
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 a3a4bdaaf40a6..a51d1f0ee179c 100644
--- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
+++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
@@ -341,24 +341,25 @@ def do_test_scopes_variables_setVariable_evaluate(
self.verify_variables(verify_locals, self.dap_server.get_local_variables())
# Now we verify that we correctly change the name of a variable with and without differentiator suffix
- self.assertFalse(self.dap_server.request_setVariable(1, "x2", 9)["success"])
+ local_scope_ref = self.get_locals_scope_reference()
+ self.assertFalse(self.dap_server.request_setVariable(local_scope_ref, "x2", 9)["success"])
self.assertFalse(
- self.dap_server.request_setVariable(1, "x @ main.cpp:0", 9)["success"]
+ self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:0", 9)["success"]
)
self.assertTrue(
- self.dap_server.request_setVariable(1, "x @ main.cpp:19", 19)["success"]
+ self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:19", 19)["success"]
)
self.assertTrue(
- self.dap_server.request_setVariable(1, "x @ main.cpp:21", 21)["success"]
+ self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:21", 21)["success"]
)
self.assertTrue(
- self.dap_server.request_setVariable(1, "x @ main.cpp:23", 23)["success"]
+ self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:23", 23)["success"]
)
# The following should have no effect
self.assertFalse(
- self.dap_server.request_setVariable(1, "x @ main.cpp:23", "invalid")[
+ self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:23", "invalid")[
"success"
]
)
@@ -370,7 +371,7 @@ def do_test_scopes_variables_setVariable_evaluate(
self.verify_variables(verify_locals, self.dap_server.get_local_variables())
# The plain x variable shold refer to the innermost x
- self.assertTrue(self.dap_server.request_setVariable(1, "x", 22)["success"])
+ self.assertTrue(self.dap_server.request_setVariable(local_scope_ref, "x", 22)["success"])
verify_locals["x @ main.cpp:23"]["equals"]["value"] = "22"
self.verify_variables(verify_locals, self.dap_server.get_local_variables())
@@ -709,7 +710,7 @@ def test_return_variables(self):
break
self.assertFalse(
- self.dap_server.request_setVariable(1, "(Return Value)", 20)["success"]
+ self.dap_server.request_setVariable(self.get_locals_scope_reference(), "(Return Value)", 20)["success"]
)
@skipIfWindows
diff --git a/lldb/unittests/DAP/VariablesTest.cpp b/lldb/unittests/DAP/VariablesTest.cpp
index 6b14fc6c3945d..40884e89e0c54 100644
--- a/lldb/unittests/DAP/VariablesTest.cpp
+++ b/lldb/unittests/DAP/VariablesTest.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "Variables.h"
+#include "lldb/API/SBFrame.h"
#include "lldb/API/SBValue.h"
#include "lldb/API/SBValueList.h"
#include "gtest/gtest.h"
@@ -66,20 +67,45 @@ TEST_F(VariablesTest, Clear_RemovesTemporaryKeepsPermanent) {
}
TEST_F(VariablesTest, GetTopLevelScope_ReturnsCorrectScope) {
+ lldb::SBFrame frame;
+ uint32_t frame_id = 0;
+
+ vars.ReadyFrame(frame_id, frame);
+ vars.SwitchFrame(frame_id);
+
vars.locals.Append(lldb::SBValue());
vars.globals.Append(lldb::SBValue());
vars.registers.Append(lldb::SBValue());
- EXPECT_EQ(vars.GetTopLevelScope(VARREF_LOCALS), &vars.locals);
- EXPECT_EQ(vars.GetTopLevelScope(VARREF_GLOBALS), &vars.globals);
- EXPECT_EQ(vars.GetTopLevelScope(VARREF_REGS), &vars.registers);
+ int64_t locals_ref = vars.GetNewVariableReference(false);
+ vars.AddScopeKind(locals_ref, ScopeKind::Locals, frame_id);
+
+ int64_t globals_ref = vars.GetNewVariableReference(false);
+ vars.AddScopeKind(globals_ref, ScopeKind::Globals, frame_id);
+
+ int64_t registers_ref = vars.GetNewVariableReference(false);
+ vars.AddScopeKind(registers_ref, ScopeKind::Registers, frame_id);
+
+ EXPECT_EQ(vars.GetTopLevelScope(locals_ref), &vars.locals);
+ EXPECT_EQ(vars.GetTopLevelScope(globals_ref), &vars.globals);
+ EXPECT_EQ(vars.GetTopLevelScope(registers_ref), &vars.registers);
EXPECT_EQ(vars.GetTopLevelScope(9999), nullptr);
}
TEST_F(VariablesTest, FindVariable_LocalsByName) {
+ lldb::SBFrame frame;
+ uint32_t frame_id = 0;
+
+ vars.ReadyFrame(frame_id, frame);
+ vars.SwitchFrame(frame_id);
+
lldb::SBValue dummy;
vars.locals.Append(dummy);
- lldb::SBValue found = vars.FindVariable(VARREF_LOCALS, "");
+
+ int64_t locals_ref = vars.GetNewVariableReference(false);
+ vars.AddScopeKind(locals_ref, ScopeKind::Locals, frame_id);
+
+ lldb::SBValue found = vars.FindVariable(locals_ref, "");
EXPECT_EQ(found.IsValid(), dummy.IsValid());
}
>From d0f1e86dd1cd081427329c5b53b262274653556c Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Thu, 28 Aug 2025 00:54:37 -0400
Subject: [PATCH 06/39] Fix set data breakpoint test by using the correct
variable reference
Variable reference 1 was hard coded to always be the local scope but
since the var_ref of local scope is dynamic now it uses a helper
function in the test
---
.../lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
index a542a318050dd..211d71d43d155 100644
--- a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
+++ b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py
@@ -106,8 +106,10 @@ def test_functionality(self):
self.set_source_breakpoints(source, [first_loop_break_line])
self.continue_to_next_stop()
self.dap_server.get_local_variables()
+ locals_ref = self.get_locals_scope_reference()
+ self.assertIsNotNone(locals_ref, "Failed to get locals scope reference")
# Test write watchpoints on x, arr[2]
- response_x = self.dap_server.request_dataBreakpointInfo(1, "x")
+ response_x = self.dap_server.request_dataBreakpointInfo(locals_ref, "x")
arr = self.dap_server.get_local_variable("arr")
response_arr_2 = self.dap_server.request_dataBreakpointInfo(
arr["variablesReference"], "[2]"
>From 875b926e8b04b072d56dbf2d75ce9f5732341382 Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Thu, 28 Aug 2025 02:16:27 -0400
Subject: [PATCH 07/39] Have llm (Claude Opus 4) Generate a regression test
---
.../lldb-dap/variables/TestDAP_variables.py | 41 +++++++++++++++++++
1 file changed, 41 insertions(+)
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 a51d1f0ee179c..342dc4de7b895 100644
--- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
+++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
@@ -832,3 +832,44 @@ def test_value_format(self):
self.assertEqual(var_pt_x["value"], "11")
var_pt_y = self.dap_server.get_local_variable_child("pt", "y", is_hex=is_hex)
self.assertEqual(var_pt_y["value"], "22")
+
+ @skipIfWindows
+ def test_variable_id_uniqueness_simple(self):
+ """
+ Simple regression test for variable ID uniqueness across frames.
+ Ensures variable IDs are not reused between different scopes/frames.
+ """
+ program = self.getBuildArtifact("a.out")
+ self.build_and_launch(program)
+ source = "main.cpp"
+
+ # Set breakpoint at test_indexedVariables call to get multiple frames
+ bp_line = line_number(source, "// breakpoint 3")
+ self.set_source_breakpoints(source, [bp_line])
+ self.continue_to_next_stop()
+
+ # Get stack frames
+ frames = self.get_stackFrames()
+ self.assertGreaterEqual(len(frames), 2, "Need at least 2 frames")
+
+ # Track all variable references for uniqueness check
+ all_refs = set()
+
+ # Check first 2-3 frames
+ for i in range(min(3, len(frames))):
+ frame_id = frames[i]['id']
+ scopes = self.dap_server.request_scopes(frame_id)["body"]["scopes"]
+
+ for scope in scopes:
+ ref = scope['variablesReference']
+ if ref != 0: # 0 means no variables
+ # Ensure this reference hasn't been used before
+ self.assertNotIn(ref, all_refs,
+ f"Variable reference {ref} was reused!")
+ all_refs.add(ref)
+
+ # Verify we collected references and they're still accessible
+ self.assertGreater(len(all_refs), 0, "Should have found variable references")
+ for ref in all_refs:
+ response = self.dap_server.request_variables(ref)
+ self.assertTrue(response['success'], f"Failed to access reference {ref}")
>From 4dae801003ed014378cfd95bfea1b6ddac0664b2 Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Thu, 28 Aug 2025 02:26:15 -0400
Subject: [PATCH 08/39] Format test files with darker
---
.../lldb-dap/variables/TestDAP_variables.py | 19 +++++++------------
1 file changed, 7 insertions(+), 12 deletions(-)
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 342dc4de7b895..705863edbc861 100644
--- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
+++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
@@ -843,33 +843,28 @@ def test_variable_id_uniqueness_simple(self):
self.build_and_launch(program)
source = "main.cpp"
- # Set breakpoint at test_indexedVariables call to get multiple frames
bp_line = line_number(source, "// breakpoint 3")
self.set_source_breakpoints(source, [bp_line])
self.continue_to_next_stop()
- # Get stack frames
frames = self.get_stackFrames()
self.assertGreaterEqual(len(frames), 2, "Need at least 2 frames")
- # Track all variable references for uniqueness check
all_refs = set()
- # Check first 2-3 frames
for i in range(min(3, len(frames))):
- frame_id = frames[i]['id']
+ frame_id = frames[i]["id"]
scopes = self.dap_server.request_scopes(frame_id)["body"]["scopes"]
for scope in scopes:
- ref = scope['variablesReference']
- if ref != 0: # 0 means no variables
- # Ensure this reference hasn't been used before
- self.assertNotIn(ref, all_refs,
- f"Variable reference {ref} was reused!")
+ ref = scope["variablesReference"]
+ if ref != 0:
+ self.assertNotIn(
+ ref, all_refs, f"Variable reference {ref} was reused!"
+ )
all_refs.add(ref)
- # Verify we collected references and they're still accessible
self.assertGreater(len(all_refs), 0, "Should have found variable references")
for ref in all_refs:
response = self.dap_server.request_variables(ref)
- self.assertTrue(response['success'], f"Failed to access reference {ref}")
+ self.assertTrue(response["success"], f"Failed to access reference {ref}")
>From a52fe0777cc872932f583926aa6c9098962d84aa Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Thu, 28 Aug 2025 02:33:27 -0400
Subject: [PATCH 09/39] Remove unused global defines and set First Var Ref to 1
---
lldb/tools/lldb-dap/Variables.h | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index a8fad707e2e08..2ea386d8d50c9 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -14,10 +14,7 @@
#include "llvm/ADT/DenseMap.h"
#include <map>
-#define VARREF_FIRST_VAR_IDX (int64_t)4
-#define VARREF_LOCALS (int64_t)1
-#define VARREF_GLOBALS (int64_t)2
-#define VARREF_REGS (int64_t)3
+#define VARREF_FIRST_VAR_IDX (int64_t)1
namespace lldb_dap {
>From 5f47586f82d1df6f7c3a4f8335c46cd191262bfa Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Thu, 28 Aug 2025 18:12:23 -0400
Subject: [PATCH 10/39] Use scopekind instead of hardcoded comparisons in
CreateScope
---
.../lldb-dap/Handler/ScopesRequestHandler.cpp | 25 +++++++++++++------
1 file changed, 18 insertions(+), 7 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
index 553a7b30adba2..2073eb6f9fc76 100644
--- a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
@@ -8,6 +8,7 @@
#include "DAP.h"
#include "RequestHandler.h"
+#include "Variables.h"
using namespace lldb_dap::protocol;
namespace lldb_dap {
@@ -29,10 +30,9 @@ namespace lldb_dap {
///
/// \return
/// A `protocol::Scope`
-Scope CreateScope(const llvm::StringRef name, int64_t variablesReference,
+Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
int64_t namedVariables, bool expensive) {
Scope scope;
- scope.name = name;
// TODO: Support "arguments" and "return value" scope.
// At the moment lldb-dap includes the arguments and return_value into the
@@ -41,10 +41,21 @@ Scope CreateScope(const llvm::StringRef name, int64_t variablesReference,
// if we add the arguments above the local scope as the locals scope will not
// be expanded if we enter a function with arguments. It becomes more
// annoying when the scope has arguments, return_value and locals.
- if (name == "Locals")
+ switch (kind) {
+ case ScopeKind::Locals:
scope.presentationHint = Scope::eScopePresentationHintLocals;
- else if (name == "Registers")
+ scope.name = "Locals";
+ break;
+ case ScopeKind::Globals:
+ scope.name = "Globals";
+ break;
+ case ScopeKind::Registers:
scope.presentationHint = Scope::eScopePresentationHintRegisters;
+ scope.name = "Registers";
+ break;
+ default:
+ llvm_unreachable("Unhandled ScopeKind");
+ }
scope.variablesReference = variablesReference;
scope.namedVariables = namedVariables;
@@ -84,18 +95,18 @@ ScopesRequestHandler::Run(const ScopesArguments &args) const {
std::vector<Scope> scopes = {};
int64_t variable_reference = dap.variables.GetNewVariableReference(false);
- scopes.push_back(CreateScope("Locals", variable_reference,
+ scopes.push_back(CreateScope(ScopeKind::Locals, variable_reference,
dap.variables.locals.GetSize(), false));
dap.variables.AddScopeKind(variable_reference, ScopeKind::Locals, frame_id);
variable_reference = dap.variables.GetNewVariableReference(false);
- scopes.push_back(CreateScope("Globals", variable_reference,
+ scopes.push_back(CreateScope(ScopeKind::Globals, variable_reference,
dap.variables.globals.GetSize(), false));
dap.variables.AddScopeKind(variable_reference, ScopeKind::Globals, frame_id);
variable_reference = dap.variables.GetNewVariableReference(false);
- scopes.push_back(CreateScope("Registers", variable_reference,
+ scopes.push_back(CreateScope(ScopeKind::Registers, variable_reference,
dap.variables.registers.GetSize(), false));
dap.variables.AddScopeKind(variable_reference, ScopeKind::Registers,
>From b7548f1e8d1bff888b3987d78ad694db1a9e310f Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Fri, 29 Aug 2025 01:43:41 -0400
Subject: [PATCH 11/39] Stop storing duplicate scopes in variables
I got rid of a variables locals, globals, and register fields and added
some helper methods to access scopes based on a var_ref or a frame_id
and a ScopeKind variant
---
.../Handler/EvaluateRequestHandler.cpp | 1 -
.../lldb-dap/Handler/ScopesRequestHandler.cpp | 25 ++++----
.../Handler/VariablesRequestHandler.cpp | 13 ++--
lldb/tools/lldb-dap/SourceBreakpoint.cpp | 1 -
lldb/tools/lldb-dap/Variables.cpp | 64 +++++++++++++------
lldb/tools/lldb-dap/Variables.h | 21 ++++--
6 files changed, 79 insertions(+), 46 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
index e207f830183b6..e1556846dff19 100644
--- a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
@@ -162,7 +162,6 @@ void EvaluateRequestHandler::operator()(
// focus_tid to the current frame for any thread related events.
if (frame.IsValid()) {
dap.focus_tid = frame.GetThread().GetThreadID();
- dap.variables.SwitchFrame(frame.GetFrameID());
}
bool required_command_failed = false;
diff --git a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
index 2073eb6f9fc76..1f4641340d234 100644
--- a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
@@ -30,8 +30,8 @@ namespace lldb_dap {
///
/// \return
/// A `protocol::Scope`
-Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
- int64_t namedVariables, bool expensive) {
+static Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
+ int64_t namedVariables, bool expensive) {
Scope scope;
// TODO: Support "arguments" and "return value" scope.
@@ -53,8 +53,6 @@ Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
scope.presentationHint = Scope::eScopePresentationHintRegisters;
scope.name = "Registers";
break;
- default:
- llvm_unreachable("Unhandled ScopeKind");
}
scope.variablesReference = variablesReference;
@@ -90,24 +88,27 @@ ScopesRequestHandler::Run(const ScopesArguments &args) const {
uint32_t frame_id = frame.GetFrameID();
dap.variables.ReadyFrame(frame_id, frame);
- dap.variables.SwitchFrame(frame_id);
- std::vector<Scope> scopes = {};
+ std::vector<protocol::Scope> scopes = {};
int64_t variable_reference = dap.variables.GetNewVariableReference(false);
- scopes.push_back(CreateScope(ScopeKind::Locals, variable_reference,
- dap.variables.locals.GetSize(), false));
+ scopes.push_back(CreateScope(
+ ScopeKind::Locals, variable_reference,
+ dap.variables.GetScope(frame_id, ScopeKind::Locals)->GetSize(), false));
dap.variables.AddScopeKind(variable_reference, ScopeKind::Locals, frame_id);
variable_reference = dap.variables.GetNewVariableReference(false);
- scopes.push_back(CreateScope(ScopeKind::Globals, variable_reference,
- dap.variables.globals.GetSize(), false));
+ scopes.push_back(CreateScope(
+ ScopeKind::Globals, variable_reference,
+ dap.variables.GetScope(frame_id, ScopeKind::Globals)->GetSize(), false));
dap.variables.AddScopeKind(variable_reference, ScopeKind::Globals, frame_id);
variable_reference = dap.variables.GetNewVariableReference(false);
- scopes.push_back(CreateScope(ScopeKind::Registers, variable_reference,
- dap.variables.registers.GetSize(), false));
+ scopes.push_back(CreateScope(
+ ScopeKind::Registers, variable_reference,
+ dap.variables.GetScope(frame_id, ScopeKind::Registers)->GetSize(),
+ false));
dap.variables.AddScopeKind(variable_reference, ScopeKind::Registers,
frame_id);
diff --git a/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
index 3d99983ac0c83..ca5da1e78a79f 100644
--- a/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
@@ -11,6 +11,7 @@
#include "Handler/RequestHandler.h"
#include "JSONUtils.h"
#include "ProtocolUtils.h"
+#include "Variables.h"
using namespace llvm;
using namespace lldb_dap::protocol;
@@ -38,15 +39,16 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
int64_t start_idx = 0;
int64_t num_children = 0;
- std::optional<ScopeKind> scope_kind = dap.variables.GetScopeKind(var_ref);
- if (scope_kind && *scope_kind == ScopeKind::Registers) {
+ std::optional<ScopeData> scope_data = dap.variables.GetScopeKind(var_ref);
+ if (scope_data.has_value() && scope_data->kind == ScopeKind::Registers) {
+
// Change the default format of any pointer sized registers in the first
// register set to be the lldb::eFormatAddressInfo so we show the pointer
// and resolve what the pointer resolves to. Only change the format if the
// format was set to the default format or if it was hex as some registers
// have formats set for them.
const uint32_t addr_size = dap.target.GetProcess().GetAddressByteSize();
- lldb::SBValue reg_set = dap.variables.registers.GetValueAtIndex(0);
+ lldb::SBValue reg_set = scope_data->scope.GetValueAtIndex(0);
const uint32_t num_regs = reg_set.GetNumChildren();
for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
lldb::SBValue reg = reg_set.GetChildAtIndex(reg_idx);
@@ -59,7 +61,8 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
}
num_children = top_scope->GetSize();
- if (num_children == 0 && scope_kind && *scope_kind == ScopeKind::Locals) {
+ if (num_children == 0 && scope_data &&
+ scope_data->kind == ScopeKind::Locals) {
// Check for an error in the SBValueList that might explain why we don't
// have locals. If we have an error display it as the sole value in the
// the locals.
@@ -95,7 +98,7 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
}
// Show return value if there is any ( in the local top frame )
- if (scope_kind && *scope_kind == ScopeKind::Locals) {
+ if (scope_data && scope_data->kind == ScopeKind::Locals) {
auto process = dap.target.GetProcess();
auto selected_thread = process.GetSelectedThread();
lldb::SBValue stop_return_value = selected_thread.GetStopReturnValue();
diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
index 843a5eb09c7ae..6f776f4d5f429 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
@@ -10,7 +10,6 @@
#include "BreakpointBase.h"
#include "DAP.h"
#include "JSONUtils.h"
-#include "ProtocolUtils.h"
#include "lldb/API/SBBreakpoint.h"
#include "lldb/API/SBFileSpec.h"
#include "lldb/API/SBFileSpecList.h"
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index b5cd9d69d385f..82e8950c2deb9 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -9,6 +9,9 @@
#include "Variables.h"
#include "JSONUtils.h"
#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBValueList.h"
+#include <cstdint>
+#include <optional>
using namespace lldb_dap;
@@ -21,26 +24,24 @@ lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
ScopeKind scope_kind = iter->second.first;
uint32_t frame_id = iter->second.second;
- if (!SwitchFrame(frame_id)) {
+ auto frame_iter = m_frames.find(frame_id);
+ if (frame_iter == m_frames.end()) {
return nullptr;
}
switch (scope_kind) {
case lldb_dap::ScopeKind::Locals:
- return &locals;
+ return &std::get<0>(frame_iter->second);
case lldb_dap::ScopeKind::Globals:
- return &globals;
+ return &std::get<1>(frame_iter->second);
case lldb_dap::ScopeKind::Registers:
- return ®isters;
+ return &std::get<2>(frame_iter->second);
}
return nullptr;
}
void Variables::Clear() {
- locals.Clear();
- globals.Clear();
- registers.Clear();
m_referencedvariables.clear();
m_scope_kinds.clear();
m_frames.clear();
@@ -120,30 +121,51 @@ lldb::SBValue Variables::FindVariable(uint64_t variablesReference,
return variable;
}
-std::optional<ScopeKind>
+std::optional<ScopeData>
Variables::GetScopeKind(const int64_t variablesReference) {
- auto iter = m_scope_kinds.find(variablesReference);
- if (iter != m_scope_kinds.end()) {
- return iter->second.first;
+ auto scope_kind_iter = m_scope_kinds.find(variablesReference);
+ if (scope_kind_iter == m_scope_kinds.end()) {
+ return std::nullopt;
+ }
+
+ auto scope_iter = m_frames.find(scope_kind_iter->second.second);
+ if (scope_iter == m_frames.end()) {
+ return std::nullopt;
+ }
+
+ switch (scope_kind_iter->second.first) {
+ case lldb_dap::ScopeKind::Locals:
+ return ScopeData(scope_kind_iter->second.first,
+ std::get<0>(scope_iter->second));
+ case lldb_dap::ScopeKind::Globals:
+ return ScopeData(scope_kind_iter->second.first,
+ std::get<1>(scope_iter->second));
+ case lldb_dap::ScopeKind::Registers:
+ return ScopeData(scope_kind_iter->second.first,
+ std::get<2>(scope_iter->second));
}
return std::nullopt;
}
-bool Variables::SwitchFrame(const uint32_t frame_id) {
- auto iter = m_frames.find(frame_id);
+lldb::SBValueList *Variables::GetScope(const uint32_t frame_id,
+ const ScopeKind kind) {
- if (iter == m_frames.end()) {
- return false;
+ auto frame = m_frames.find(frame_id);
+ if (m_frames.find(frame_id) == m_frames.end()) {
+ return nullptr;
}
- auto [frame_locals, frame_globals, frame_registers] = iter->second;
-
- locals = frame_locals;
- globals = frame_globals;
- registers = frame_registers;
+ switch (kind) {
+ case ScopeKind::Locals:
+ return &std::get<0>(frame->second);
+ case ScopeKind::Globals:
+ return &std::get<1>(frame->second);
+ case ScopeKind::Registers:
+ return &std::get<2>(frame->second);
+ }
- return true;
+ return nullptr;
}
void Variables::ReadyFrame(uint32_t frame_id, lldb::SBFrame &frame) {
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index 2ea386d8d50c9..e001d688bd039 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -13,6 +13,7 @@
#include "lldb/API/SBValueList.h"
#include "llvm/ADT/DenseMap.h"
#include <map>
+#include <utility>
#define VARREF_FIRST_VAR_IDX (int64_t)1
@@ -20,11 +21,15 @@ namespace lldb_dap {
enum ScopeKind { Locals, Globals, Registers };
-struct Variables {
- lldb::SBValueList locals;
- lldb::SBValueList globals;
- lldb::SBValueList registers;
+struct ScopeData {
+ ScopeKind kind;
+ lldb::SBValueList scope;
+
+ ScopeData(ScopeKind kind, lldb::SBValueList scope)
+ : kind(kind), scope(scope) {}
+};
+struct 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);
@@ -39,6 +44,8 @@ struct Variables {
/// If \p var_ref is invalid an empty SBValue is returned.
lldb::SBValue GetVariable(int64_t var_ref) const;
+ lldb::SBValueList *GetScope(const uint32_t frame_id, const ScopeKind kind);
+
/// Insert a new \p variable.
/// \return variableReference assigned to this expandable variable.
int64_t InsertVariable(lldb::SBValue variable, bool is_permanent);
@@ -47,10 +54,9 @@ struct Variables {
lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name);
- bool SwitchFrame(uint32_t frame_id);
/// Initialize a frame if it hasn't been already, otherwise do nothing
void ReadyFrame(uint32_t frame_id, lldb::SBFrame &frame);
- std::optional<ScopeKind> GetScopeKind(const int64_t variablesReference);
+ std::optional<ScopeData> GetScopeKind(const int64_t variablesReference);
/// Clear all scope variables and non-permanent expandable variables.
void Clear();
@@ -63,6 +69,7 @@ struct Variables {
static constexpr int64_t PermanentVariableStartIndex = (1ll << 32);
int64_t m_next_temporary_var_ref{VARREF_FIRST_VAR_IDX};
+ // Variable Reference, frame_id
std::map<int64_t, std::pair<ScopeKind, uint32_t>> m_scope_kinds;
/// Variables that are alive in this stop state.
@@ -73,6 +80,8 @@ struct Variables {
/// These are the variables evaluated from debug console REPL.
llvm::DenseMap<int64_t, lldb::SBValue> m_referencedpermanent_variables;
+ /// Key = frame_id
+ /// Value = (locals, globals Registers) scopes
std::map<uint32_t,
std::tuple<lldb::SBValueList, lldb::SBValueList, lldb::SBValueList>>
m_frames;
>From c8196a27ea8ff5b89a5d83aa3ee85f227858a4fe Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Fri, 29 Aug 2025 02:01:04 -0400
Subject: [PATCH 12/39] Apply Python formatting to fix CI darker checks
---
.../test/tools/lldb-dap/lldbdap_testcase.py | 5 +--
.../lldb-dap/variables/TestDAP_variables.py | 34 +++++++++++++------
2 files changed, 27 insertions(+), 12 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index a26be6e5d1bfb..9c363e54e747c 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -369,10 +369,11 @@ def set_global(self, name, value, id=None):
for scope in frame_scopes:
if scope["name"] == "Globals":
varRef = scope["variablesReference"]
- return self.dap_server.request_setVariable(varRef, name, str(value), id=id)
+ return self.dap_server.request_setVariable(
+ varRef, name, str(value), id=id
+ )
return None
-
def get_locals_scope_reference(self):
"""Get the variablesReference for the locals scope."""
stackFrame = self.dap_server.get_stackFrame()
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 705863edbc861..6d83661ede922 100644
--- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
+++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
@@ -342,26 +342,36 @@ def do_test_scopes_variables_setVariable_evaluate(
# Now we verify that we correctly change the name of a variable with and without differentiator suffix
local_scope_ref = self.get_locals_scope_reference()
- self.assertFalse(self.dap_server.request_setVariable(local_scope_ref, "x2", 9)["success"])
self.assertFalse(
- self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:0", 9)["success"]
+ self.dap_server.request_setVariable(local_scope_ref, "x2", 9)["success"]
+ )
+ self.assertFalse(
+ self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:0", 9)[
+ "success"
+ ]
)
self.assertTrue(
- self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:19", 19)["success"]
+ self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:19", 19)[
+ "success"
+ ]
)
self.assertTrue(
- self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:21", 21)["success"]
+ self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:21", 21)[
+ "success"
+ ]
)
self.assertTrue(
- self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:23", 23)["success"]
+ self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:23", 23)[
+ "success"
+ ]
)
# The following should have no effect
self.assertFalse(
- self.dap_server.request_setVariable(local_scope_ref, "x @ main.cpp:23", "invalid")[
- "success"
- ]
+ self.dap_server.request_setVariable(
+ local_scope_ref, "x @ main.cpp:23", "invalid"
+ )["success"]
)
verify_locals["x @ main.cpp:19"]["equals"]["value"] = "19"
@@ -371,7 +381,9 @@ def do_test_scopes_variables_setVariable_evaluate(
self.verify_variables(verify_locals, self.dap_server.get_local_variables())
# The plain x variable shold refer to the innermost x
- self.assertTrue(self.dap_server.request_setVariable(local_scope_ref, "x", 22)["success"])
+ self.assertTrue(
+ self.dap_server.request_setVariable(local_scope_ref, "x", 22)["success"]
+ )
verify_locals["x @ main.cpp:23"]["equals"]["value"] = "22"
self.verify_variables(verify_locals, self.dap_server.get_local_variables())
@@ -710,7 +722,9 @@ def test_return_variables(self):
break
self.assertFalse(
- self.dap_server.request_setVariable(self.get_locals_scope_reference(), "(Return Value)", 20)["success"]
+ self.dap_server.request_setVariable(
+ self.get_locals_scope_reference(), "(Return Value)", 20
+ )["success"]
)
@skipIfWindows
>From 23879526b6304f21dac5ea4e584e347f6d8fe0a5 Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Fri, 29 Aug 2025 02:30:46 -0400
Subject: [PATCH 13/39] Hopefully fix failing tests (They're not running on my
end)
---
lldb/unittests/DAP/VariablesTest.cpp | 18 ++++++------------
1 file changed, 6 insertions(+), 12 deletions(-)
diff --git a/lldb/unittests/DAP/VariablesTest.cpp b/lldb/unittests/DAP/VariablesTest.cpp
index 40884e89e0c54..46b171a443586 100644
--- a/lldb/unittests/DAP/VariablesTest.cpp
+++ b/lldb/unittests/DAP/VariablesTest.cpp
@@ -71,11 +71,6 @@ TEST_F(VariablesTest, GetTopLevelScope_ReturnsCorrectScope) {
uint32_t frame_id = 0;
vars.ReadyFrame(frame_id, frame);
- vars.SwitchFrame(frame_id);
-
- vars.locals.Append(lldb::SBValue());
- vars.globals.Append(lldb::SBValue());
- vars.registers.Append(lldb::SBValue());
int64_t locals_ref = vars.GetNewVariableReference(false);
vars.AddScopeKind(locals_ref, ScopeKind::Locals, frame_id);
@@ -86,9 +81,12 @@ TEST_F(VariablesTest, GetTopLevelScope_ReturnsCorrectScope) {
int64_t registers_ref = vars.GetNewVariableReference(false);
vars.AddScopeKind(registers_ref, ScopeKind::Registers, frame_id);
- EXPECT_EQ(vars.GetTopLevelScope(locals_ref), &vars.locals);
- EXPECT_EQ(vars.GetTopLevelScope(globals_ref), &vars.globals);
- EXPECT_EQ(vars.GetTopLevelScope(registers_ref), &vars.registers);
+ EXPECT_EQ(vars.GetTopLevelScope(locals_ref),
+ &vars.GetScope(frame_id, lldb_dap::ScopeKind::Locals));
+ EXPECT_EQ(vars.GetTopLevelScope(globals_ref),
+ &vars.GetScope(frame_id, lldb_dap::ScopeKind::Globals));
+ EXPECT_EQ(vars.GetTopLevelScope(registers_ref),
+ &vars.GetScope(frame_id, lldb_dap::ScopeKind::Registers));
EXPECT_EQ(vars.GetTopLevelScope(9999), nullptr);
}
@@ -97,10 +95,6 @@ TEST_F(VariablesTest, FindVariable_LocalsByName) {
uint32_t frame_id = 0;
vars.ReadyFrame(frame_id, frame);
- vars.SwitchFrame(frame_id);
-
- lldb::SBValue dummy;
- vars.locals.Append(dummy);
int64_t locals_ref = vars.GetNewVariableReference(false);
vars.AddScopeKind(locals_ref, ScopeKind::Locals, frame_id);
>From bc0032595afe685a75c25dab514f7d4100c8b440 Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Fri, 29 Aug 2025 12:20:49 -0400
Subject: [PATCH 14/39] Actually fix the failing tests for real
---
lldb/unittests/DAP/VariablesTest.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/lldb/unittests/DAP/VariablesTest.cpp b/lldb/unittests/DAP/VariablesTest.cpp
index 46b171a443586..2c5e608c30675 100644
--- a/lldb/unittests/DAP/VariablesTest.cpp
+++ b/lldb/unittests/DAP/VariablesTest.cpp
@@ -82,11 +82,11 @@ TEST_F(VariablesTest, GetTopLevelScope_ReturnsCorrectScope) {
vars.AddScopeKind(registers_ref, ScopeKind::Registers, frame_id);
EXPECT_EQ(vars.GetTopLevelScope(locals_ref),
- &vars.GetScope(frame_id, lldb_dap::ScopeKind::Locals));
+ vars.GetScope(frame_id, lldb_dap::ScopeKind::Locals));
EXPECT_EQ(vars.GetTopLevelScope(globals_ref),
- &vars.GetScope(frame_id, lldb_dap::ScopeKind::Globals));
+ vars.GetScope(frame_id, lldb_dap::ScopeKind::Globals));
EXPECT_EQ(vars.GetTopLevelScope(registers_ref),
- &vars.GetScope(frame_id, lldb_dap::ScopeKind::Registers));
+ vars.GetScope(frame_id, lldb_dap::ScopeKind::Registers));
EXPECT_EQ(vars.GetTopLevelScope(9999), nullptr);
}
@@ -100,6 +100,7 @@ TEST_F(VariablesTest, FindVariable_LocalsByName) {
vars.AddScopeKind(locals_ref, ScopeKind::Locals, frame_id);
lldb::SBValue found = vars.FindVariable(locals_ref, "");
+ lldb::SBValue dummy;
EXPECT_EQ(found.IsValid(), dummy.IsValid());
}
>From facb48958e9f426e075ebef7d95614ff6d020ff4 Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Tue, 2 Sep 2025 10:47:57 -0400
Subject: [PATCH 15/39] Simplify Scopes request handler
Merge ReadyFrame and Switch Frame functions. Also get rid of
AddScopeKind function in favor of inlining it in ReadyFrame
---
.../lldb-dap/Handler/ScopesRequestHandler.cpp | 77 +------------------
lldb/tools/lldb-dap/JSONUtils.cpp | 1 -
lldb/tools/lldb-dap/JSONUtils.h | 1 -
lldb/tools/lldb-dap/Variables.cpp | 72 +++++++++++++++--
lldb/tools/lldb-dap/Variables.h | 25 +++++-
5 files changed, 90 insertions(+), 86 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
index 1f4641340d234..0f0c80383bf2d 100644
--- a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
@@ -13,55 +13,6 @@
using namespace lldb_dap::protocol;
namespace lldb_dap {
-/// Creates a `protocol::Scope` struct.
-///
-///
-/// \param[in] name
-/// The value to place into the "name" key
-///
-/// \param[in] variablesReference
-/// The value to place into the "variablesReference" key
-///
-/// \param[in] namedVariables
-/// The value to place into the "namedVariables" key
-///
-/// \param[in] expensive
-/// The value to place into the "expensive" key
-///
-/// \return
-/// A `protocol::Scope`
-static Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
- int64_t namedVariables, bool expensive) {
- Scope scope;
-
- // TODO: Support "arguments" and "return value" scope.
- // At the moment lldb-dap includes the arguments and return_value into the
- // "locals" scope.
- // vscode only expands the first non-expensive scope, this causes friction
- // if we add the arguments above the local scope as the locals scope will not
- // be expanded if we enter a function with arguments. It becomes more
- // annoying when the scope has arguments, return_value and locals.
- switch (kind) {
- case ScopeKind::Locals:
- scope.presentationHint = Scope::eScopePresentationHintLocals;
- scope.name = "Locals";
- break;
- case ScopeKind::Globals:
- scope.name = "Globals";
- break;
- case ScopeKind::Registers:
- scope.presentationHint = Scope::eScopePresentationHintRegisters;
- scope.name = "Registers";
- break;
- }
-
- scope.variablesReference = variablesReference;
- scope.namedVariables = namedVariables;
- scope.expensive = expensive;
-
- return scope;
-}
-
llvm::Expected<ScopesResponseBody>
ScopesRequestHandler::Run(const ScopesArguments &args) const {
lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
@@ -86,32 +37,8 @@ ScopesRequestHandler::Run(const ScopesArguments &args) const {
}
uint32_t frame_id = frame.GetFrameID();
-
- dap.variables.ReadyFrame(frame_id, frame);
-
- std::vector<protocol::Scope> scopes = {};
-
- int64_t variable_reference = dap.variables.GetNewVariableReference(false);
- scopes.push_back(CreateScope(
- ScopeKind::Locals, variable_reference,
- dap.variables.GetScope(frame_id, ScopeKind::Locals)->GetSize(), false));
-
- dap.variables.AddScopeKind(variable_reference, ScopeKind::Locals, frame_id);
-
- variable_reference = dap.variables.GetNewVariableReference(false);
- scopes.push_back(CreateScope(
- ScopeKind::Globals, variable_reference,
- dap.variables.GetScope(frame_id, ScopeKind::Globals)->GetSize(), false));
- dap.variables.AddScopeKind(variable_reference, ScopeKind::Globals, frame_id);
-
- variable_reference = dap.variables.GetNewVariableReference(false);
- scopes.push_back(CreateScope(
- ScopeKind::Registers, variable_reference,
- dap.variables.GetScope(frame_id, ScopeKind::Registers)->GetSize(),
- false));
-
- dap.variables.AddScopeKind(variable_reference, ScopeKind::Registers,
- frame_id);
+ std::vector<protocol::Scope> scopes =
+ dap.variables.ReadyFrame(frame_id, frame);
return ScopesResponseBody{std::move(scopes)};
}
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index ad1f035caaf54..a4c42346d31f8 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -11,7 +11,6 @@
#include "ExceptionBreakpoint.h"
#include "LLDBUtils.h"
#include "ProtocolUtils.h"
-#include "Variables.h"
#include "lldb/API/SBAddress.h"
#include "lldb/API/SBCompileUnit.h"
#include "lldb/API/SBDeclaration.h"
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index 6575411acd878..e9094f67b94ec 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -9,7 +9,6 @@
#ifndef LLDB_TOOLS_LLDB_DAP_JSONUTILS_H
#define LLDB_TOOLS_LLDB_DAP_JSONUTILS_H
-#include "DAP.h"
#include "DAPForward.h"
#include "Protocol/ProtocolTypes.h"
#include "lldb/API/SBCompileUnit.h"
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 82e8950c2deb9..4566931d0e7cc 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -8,13 +8,49 @@
#include "Variables.h"
#include "JSONUtils.h"
+#include "Protocol/ProtocolTypes.h"
#include "lldb/API/SBFrame.h"
#include "lldb/API/SBValueList.h"
#include <cstdint>
#include <optional>
+#include <vector>
using namespace lldb_dap;
+namespace lldb_dap {
+
+protocol::Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
+ int64_t namedVariables, bool expensive) {
+ protocol::Scope scope;
+
+ // TODO: Support "arguments" and "return value" scope.
+ // At the moment lldb-he arguments and return_value into the
+ // "locals" scope.
+ // vscode only expands the first non-expensive scope, this causes friction
+ // if we add the arguments above the local scope as the locals scope will not
+ // be expanded if we enter a function with arguments. It becomes more
+ // annoying when the scope has arguments, return_value and locals.
+ switch (kind) {
+ case ScopeKind::Locals:
+ scope.presentationHint = protocol::Scope::eScopePresentationHintLocals;
+ scope.name = "Locals";
+ break;
+ case ScopeKind::Globals:
+ scope.name = "Globals";
+ break;
+ case ScopeKind::Registers:
+ scope.presentationHint = protocol::Scope::eScopePresentationHintRegisters;
+ scope.name = "Registers";
+ break;
+ }
+
+ scope.variablesReference = variablesReference;
+ scope.namedVariables = namedVariables;
+ scope.expensive = expensive;
+
+ return scope;
+}
+
lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
auto iter = m_scope_kinds.find(variablesReference);
if (iter == m_scope_kinds.end()) {
@@ -168,7 +204,9 @@ lldb::SBValueList *Variables::GetScope(const uint32_t frame_id,
return nullptr;
}
-void Variables::ReadyFrame(uint32_t frame_id, lldb::SBFrame &frame) {
+std::vector<protocol::Scope> Variables::ReadyFrame(uint32_t frame_id,
+ lldb::SBFrame &frame) {
+
if (m_frames.find(frame_id) == m_frames.end()) {
auto locals = frame.GetVariables(/*arguments=*/true,
@@ -186,10 +224,34 @@ void Variables::ReadyFrame(uint32_t frame_id, lldb::SBFrame &frame) {
m_frames.insert(
std::make_pair(frame_id, std::make_tuple(locals, globals, registers)));
}
-}
-void Variables::AddScopeKind(int64_t variable_reference, ScopeKind kind,
- uint32_t frame_id) {
+ std::vector<protocol::Scope> scopes = {};
+
+ int64_t variable_reference = GetNewVariableReference(false);
+
+ scopes.push_back(CreateScope(ScopeKind::Locals, variable_reference,
+ GetScope(frame_id, ScopeKind::Locals)->GetSize(),
+ false));
- m_scope_kinds[variable_reference] = std::make_pair(kind, frame_id);
+ m_scope_kinds[variable_reference] =
+ std::make_pair(ScopeKind::Locals, frame_id);
+
+ variable_reference = GetNewVariableReference(false);
+ scopes.push_back(
+ CreateScope(ScopeKind::Globals, variable_reference,
+ GetScope(frame_id, ScopeKind::Globals)->GetSize(), false));
+ m_scope_kinds[variable_reference] =
+ std::make_pair(ScopeKind::Globals, frame_id);
+
+ variable_reference = GetNewVariableReference(false);
+ scopes.push_back(
+ CreateScope(ScopeKind::Registers, variable_reference,
+ GetScope(frame_id, ScopeKind::Registers)->GetSize(), false));
+
+ m_scope_kinds[variable_reference] =
+ std::make_pair(ScopeKind::Registers, frame_id);
+
+ return scopes;
}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index e001d688bd039..2470cd030b47d 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -9,6 +9,7 @@
#ifndef LLDB_TOOLS_LLDB_DAP_VARIABLES_H
#define LLDB_TOOLS_LLDB_DAP_VARIABLES_H
+#include "Protocol/ProtocolTypes.h"
#include "lldb/API/SBValue.h"
#include "lldb/API/SBValueList.h"
#include "llvm/ADT/DenseMap.h"
@@ -20,6 +21,24 @@
namespace lldb_dap {
enum ScopeKind { Locals, Globals, Registers };
+/// Creates a `protocol::Scope` struct.
+///
+/// \param[in] kind
+/// The kind of scope to create
+///
+/// \param[in] variablesReference
+/// The value to place into the "variablesReference" key
+///
+/// \param[in] namedVariables
+/// The value to place into the "namedVariables" key
+///
+/// \param[in] expensive
+/// The value to place into the "expensive" key
+///
+/// \return
+/// A `protocol::Scope`
+protocol::Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
+ int64_t namedVariables, bool expensive);
struct ScopeData {
ScopeKind kind;
@@ -55,15 +74,13 @@ struct Variables {
lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name);
/// Initialize a frame if it hasn't been already, otherwise do nothing
- void ReadyFrame(uint32_t frame_id, lldb::SBFrame &frame);
+ std::vector<protocol::Scope> ReadyFrame(uint32_t frame_id,
+ lldb::SBFrame &frame);
std::optional<ScopeData> GetScopeKind(const int64_t variablesReference);
/// Clear all scope variables and non-permanent expandable variables.
void Clear();
- void AddScopeKind(int64_t variable_reference, ScopeKind kind,
- uint32_t frame_id);
-
private:
/// Variable_reference start index of permanent expandable variable.
static constexpr int64_t PermanentVariableStartIndex = (1ll << 32);
>From c4df0a8aaca50ef1c473d28643f8788b691e6694 Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Tue, 2 Sep 2025 14:20:09 -0400
Subject: [PATCH 16/39] Fix variables test
---
lldb/unittests/DAP/VariablesTest.cpp | 18 +++++-------------
1 file changed, 5 insertions(+), 13 deletions(-)
diff --git a/lldb/unittests/DAP/VariablesTest.cpp b/lldb/unittests/DAP/VariablesTest.cpp
index 2c5e608c30675..8857d420ced20 100644
--- a/lldb/unittests/DAP/VariablesTest.cpp
+++ b/lldb/unittests/DAP/VariablesTest.cpp
@@ -72,20 +72,13 @@ TEST_F(VariablesTest, GetTopLevelScope_ReturnsCorrectScope) {
vars.ReadyFrame(frame_id, frame);
- int64_t locals_ref = vars.GetNewVariableReference(false);
- vars.AddScopeKind(locals_ref, ScopeKind::Locals, frame_id);
-
- int64_t globals_ref = vars.GetNewVariableReference(false);
- vars.AddScopeKind(globals_ref, ScopeKind::Globals, frame_id);
-
- int64_t registers_ref = vars.GetNewVariableReference(false);
- vars.AddScopeKind(registers_ref, ScopeKind::Registers, frame_id);
+ int64_t next_variable_ref = vars.GetNewVariableReference(false);
- EXPECT_EQ(vars.GetTopLevelScope(locals_ref),
+ EXPECT_EQ(vars.GetTopLevelScope(next_variable_ref - 3),
vars.GetScope(frame_id, lldb_dap::ScopeKind::Locals));
- EXPECT_EQ(vars.GetTopLevelScope(globals_ref),
+ EXPECT_EQ(vars.GetTopLevelScope(next_variable_ref - 2),
vars.GetScope(frame_id, lldb_dap::ScopeKind::Globals));
- EXPECT_EQ(vars.GetTopLevelScope(registers_ref),
+ EXPECT_EQ(vars.GetTopLevelScope(next_variable_ref - 1),
vars.GetScope(frame_id, lldb_dap::ScopeKind::Registers));
EXPECT_EQ(vars.GetTopLevelScope(9999), nullptr);
}
@@ -97,9 +90,8 @@ TEST_F(VariablesTest, FindVariable_LocalsByName) {
vars.ReadyFrame(frame_id, frame);
int64_t locals_ref = vars.GetNewVariableReference(false);
- vars.AddScopeKind(locals_ref, ScopeKind::Locals, frame_id);
- lldb::SBValue found = vars.FindVariable(locals_ref, "");
+ lldb::SBValue found = vars.FindVariable(locals_ref - 1, "");
lldb::SBValue dummy;
EXPECT_EQ(found.IsValid(), dummy.IsValid());
>From 348f21ca0507d08dd30c5ebb4929b5007a098a85 Mon Sep 17 00:00:00 2001
From: Anthony <hello at anthonyeid.me>
Date: Fri, 5 Sep 2025 01:47:01 -0400
Subject: [PATCH 17/39] Simplify ScopeData and improve naming in ready frame
function
---
lldb/tools/lldb-dap/Variables.cpp | 36 +++++++++++++++----------------
lldb/tools/lldb-dap/Variables.h | 3 ---
2 files changed, 18 insertions(+), 21 deletions(-)
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 4566931d0e7cc..8f0c48f4e1ca6 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -169,16 +169,19 @@ Variables::GetScopeKind(const int64_t variablesReference) {
return std::nullopt;
}
+ ScopeData scope_data = ScopeData();
+ scope_data.kind = scope_kind_iter->second.first;
+
switch (scope_kind_iter->second.first) {
case lldb_dap::ScopeKind::Locals:
- return ScopeData(scope_kind_iter->second.first,
- std::get<0>(scope_iter->second));
+ scope_data.scope = std::get<0>(scope_iter->second);
+ return scope_data;
case lldb_dap::ScopeKind::Globals:
- return ScopeData(scope_kind_iter->second.first,
- std::get<1>(scope_iter->second));
+ scope_data.scope = std::get<1>(scope_iter->second);
+ return scope_data;
case lldb_dap::ScopeKind::Registers:
- return ScopeData(scope_kind_iter->second.first,
- std::get<2>(scope_iter->second));
+ scope_data.scope = std::get<2>(scope_iter->second);
+ return scope_data;
}
return std::nullopt;
@@ -227,29 +230,26 @@ std::vector<protocol::Scope> Variables::ReadyFrame(uint32_t frame_id,
std::vector<protocol::Scope> scopes = {};
- int64_t variable_reference = GetNewVariableReference(false);
+ int64_t locals_ref = GetNewVariableReference(false);
- scopes.push_back(CreateScope(ScopeKind::Locals, variable_reference,
+ scopes.push_back(CreateScope(ScopeKind::Locals, locals_ref,
GetScope(frame_id, ScopeKind::Locals)->GetSize(),
false));
- m_scope_kinds[variable_reference] =
- std::make_pair(ScopeKind::Locals, frame_id);
+ m_scope_kinds[locals_ref] = std::make_pair(ScopeKind::Locals, frame_id);
- variable_reference = GetNewVariableReference(false);
+ int64_t globals_ref = GetNewVariableReference(false);
scopes.push_back(
- CreateScope(ScopeKind::Globals, variable_reference,
+ CreateScope(ScopeKind::Globals, globals_ref,
GetScope(frame_id, ScopeKind::Globals)->GetSize(), false));
- m_scope_kinds[variable_reference] =
- std::make_pair(ScopeKind::Globals, frame_id);
+ m_scope_kinds[globals_ref] = std::make_pair(ScopeKind::Globals, frame_id);
- variable_reference = GetNewVariableReference(false);
+ int64_t registers_ref = GetNewVariableReference(false);
scopes.push_back(
- CreateScope(ScopeKind::Registers, variable_reference,
+ CreateScope(ScopeKind::Registers, registers_ref,
GetScope(frame_id, ScopeKind::Registers)->GetSize(), false));
- m_scope_kinds[variable_reference] =
- std::make_pair(ScopeKind::Registers, frame_id);
+ m_scope_kinds[registers_ref] = std::make_pair(ScopeKind::Registers, frame_id);
return scopes;
}
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index 2470cd030b47d..c3ce14c1661fe 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -43,9 +43,6 @@ protocol::Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
struct ScopeData {
ScopeKind kind;
lldb::SBValueList scope;
-
- ScopeData(ScopeKind kind, lldb::SBValueList scope)
- : kind(kind), scope(scope) {}
};
struct Variables {
>From c4155cc279323f5014dba9e4495ec4bef71e0a31 Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Sun, 7 Dec 2025 16:46:39 -0500
Subject: [PATCH 18/39] Switch Variables.m_frames key to use thread_id and
frame_id as key
This addresses @ashgti feedback by making the frame key unique across
threads.
---
.../lldb-dap/Handler/ScopesRequestHandler.cpp | 3 +-
lldb/tools/lldb-dap/Variables.cpp | 43 ++++++++++---------
lldb/tools/lldb-dap/Variables.h | 15 ++++---
3 files changed, 31 insertions(+), 30 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
index 0f0c80383bf2d..5a016265f770f 100644
--- a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
@@ -36,9 +36,8 @@ ScopesRequestHandler::Run(const ScopesArguments &args) const {
frame.GetThread().SetSelectedFrame(frame.GetFrameID());
}
- uint32_t frame_id = frame.GetFrameID();
std::vector<protocol::Scope> scopes =
- dap.variables.ReadyFrame(frame_id, frame);
+ dap.variables.ReadyFrame(args.frameId, frame);
return ScopesResponseBody{std::move(scopes)};
}
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 8f0c48f4e1ca6..3096c27267139 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -58,9 +58,9 @@ lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
}
ScopeKind scope_kind = iter->second.first;
- uint32_t frame_id = iter->second.second;
+ uint64_t dap_frame_id = iter->second.second;
- auto frame_iter = m_frames.find(frame_id);
+ auto frame_iter = m_frames.find(dap_frame_id);
if (frame_iter == m_frames.end()) {
return nullptr;
}
@@ -187,11 +187,11 @@ Variables::GetScopeKind(const int64_t variablesReference) {
return std::nullopt;
}
-lldb::SBValueList *Variables::GetScope(const uint32_t frame_id,
+lldb::SBValueList *Variables::GetScope(const uint64_t dap_frame_id,
const ScopeKind kind) {
- auto frame = m_frames.find(frame_id);
- if (m_frames.find(frame_id) == m_frames.end()) {
+ auto frame = m_frames.find(dap_frame_id);
+ if (m_frames.find(dap_frame_id) == m_frames.end()) {
return nullptr;
}
@@ -207,10 +207,10 @@ lldb::SBValueList *Variables::GetScope(const uint32_t frame_id,
return nullptr;
}
-std::vector<protocol::Scope> Variables::ReadyFrame(uint32_t frame_id,
+std::vector<protocol::Scope> Variables::ReadyFrame(const uint64_t dap_frame_id,
lldb::SBFrame &frame) {
- if (m_frames.find(frame_id) == m_frames.end()) {
+ if (m_frames.find(dap_frame_id) == m_frames.end()) {
auto locals = frame.GetVariables(/*arguments=*/true,
/*locals=*/true,
@@ -224,32 +224,33 @@ std::vector<protocol::Scope> Variables::ReadyFrame(uint32_t frame_id,
auto registers = frame.GetRegisters();
- m_frames.insert(
- std::make_pair(frame_id, std::make_tuple(locals, globals, registers)));
+ m_frames.insert(std::make_pair(
+ dap_frame_id, std::make_tuple(locals, globals, registers)));
}
std::vector<protocol::Scope> scopes = {};
int64_t locals_ref = GetNewVariableReference(false);
- scopes.push_back(CreateScope(ScopeKind::Locals, locals_ref,
- GetScope(frame_id, ScopeKind::Locals)->GetSize(),
- false));
+ scopes.push_back(
+ CreateScope(ScopeKind::Locals, locals_ref,
+ GetScope(dap_frame_id, ScopeKind::Locals)->GetSize(), false));
- m_scope_kinds[locals_ref] = std::make_pair(ScopeKind::Locals, frame_id);
+ m_scope_kinds[locals_ref] = std::make_pair(ScopeKind::Locals, dap_frame_id);
int64_t globals_ref = GetNewVariableReference(false);
- scopes.push_back(
- CreateScope(ScopeKind::Globals, globals_ref,
- GetScope(frame_id, ScopeKind::Globals)->GetSize(), false));
- m_scope_kinds[globals_ref] = std::make_pair(ScopeKind::Globals, frame_id);
+ scopes.push_back(CreateScope(
+ ScopeKind::Globals, globals_ref,
+ GetScope(dap_frame_id, ScopeKind::Globals)->GetSize(), false));
+ m_scope_kinds[globals_ref] = std::make_pair(ScopeKind::Globals, dap_frame_id);
int64_t registers_ref = GetNewVariableReference(false);
- scopes.push_back(
- CreateScope(ScopeKind::Registers, registers_ref,
- GetScope(frame_id, ScopeKind::Registers)->GetSize(), false));
+ scopes.push_back(CreateScope(
+ ScopeKind::Registers, registers_ref,
+ GetScope(dap_frame_id, ScopeKind::Registers)->GetSize(), false));
- m_scope_kinds[registers_ref] = std::make_pair(ScopeKind::Registers, frame_id);
+ m_scope_kinds[registers_ref] =
+ std::make_pair(ScopeKind::Registers, dap_frame_id);
return scopes;
}
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index c3ce14c1661fe..0e4c8685b6fde 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -60,7 +60,8 @@ struct Variables {
/// If \p var_ref is invalid an empty SBValue is returned.
lldb::SBValue GetVariable(int64_t var_ref) const;
- lldb::SBValueList *GetScope(const uint32_t frame_id, const ScopeKind kind);
+ lldb::SBValueList *GetScope(const uint64_t dap_frame_id,
+ const ScopeKind kind);
/// Insert a new \p variable.
/// \return variableReference assigned to this expandable variable.
@@ -71,7 +72,7 @@ struct Variables {
lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name);
/// Initialize a frame if it hasn't been already, otherwise do nothing
- std::vector<protocol::Scope> ReadyFrame(uint32_t frame_id,
+ std::vector<protocol::Scope> ReadyFrame(const uint64_t dap_frame_id,
lldb::SBFrame &frame);
std::optional<ScopeData> GetScopeKind(const int64_t variablesReference);
@@ -83,8 +84,8 @@ struct Variables {
static constexpr int64_t PermanentVariableStartIndex = (1ll << 32);
int64_t m_next_temporary_var_ref{VARREF_FIRST_VAR_IDX};
- // Variable Reference, frame_id
- std::map<int64_t, std::pair<ScopeKind, uint32_t>> m_scope_kinds;
+ // Variable Reference, dap_frame_id
+ std::map<int64_t, std::pair<ScopeKind, uint64_t>> m_scope_kinds;
/// Variables that are alive in this stop state.
/// Will be cleared when debuggee resumes.
@@ -94,9 +95,9 @@ struct Variables {
/// These are the variables evaluated from debug console REPL.
llvm::DenseMap<int64_t, lldb::SBValue> m_referencedpermanent_variables;
- /// Key = frame_id
- /// Value = (locals, globals Registers) scopes
- std::map<uint32_t,
+ /// Key = dap_frame_id (encodes both thread index ID and frame ID)
+ /// Value = (locals, globals, registers) scopes
+ std::map<uint64_t,
std::tuple<lldb::SBValueList, lldb::SBValueList, lldb::SBValueList>>
m_frames;
int64_t m_next_permanent_var_ref{PermanentVariableStartIndex};
>From a603d7c91f83a0931c79b64eda1c4b2c0a2a489a Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Sun, 7 Dec 2025 17:23:52 -0500
Subject: [PATCH 19/39] Add test_variable_id_uniqueness_simple regression test
---
.../lldb-dap/variables/TestDAP_variables.py | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
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 977d6ce9dac8a..d0b01b0e565a5 100644
--- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
+++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
@@ -834,3 +834,39 @@ def test_value_format(self):
self.assertEqual(var_pt_x["value"], "11")
var_pt_y = self.dap_server.get_local_variable_child("pt", "y", is_hex=is_hex)
self.assertEqual(var_pt_y["value"], "22")
+
+ @skipIfWindows
+ def test_variable_id_uniqueness_simple(self):
+ """
+ Simple regression test for variable ID uniqueness across frames.
+ Ensures variable IDs are not reused between different scopes/frames.
+ """
+ program = self.getBuildArtifact("a.out")
+ self.build_and_launch(program)
+ source = "main.cpp"
+
+ bp_line = line_number(source, "// breakpoint 3")
+ self.set_source_breakpoints(source, [bp_line])
+ self.continue_to_next_stop()
+
+ frames = self.get_stackFrames()
+ self.assertGreaterEqual(len(frames), 2, "Need at least 2 frames")
+
+ all_refs = set()
+
+ for i in range(min(3, len(frames))):
+ frame_id = frames[i]["id"]
+ scopes = self.dap_server.request_scopes(frame_id)["body"]["scopes"]
+
+ for scope in scopes:
+ ref = scope["variablesReference"]
+ if ref != 0:
+ self.assertNotIn(
+ ref, all_refs, f"Variable reference {ref} was reused!"
+ )
+ all_refs.add(ref)
+
+ self.assertGreater(len(all_refs), 0, "Should have found variable references")
+ for ref in all_refs:
+ response = self.dap_server.request_variables(ref)
+ self.assertTrue(response["success"], f"Failed to access reference {ref}")
>From 153b19019bc5369bd801a85f13968346d135ecb7 Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Sun, 7 Dec 2025 20:46:05 -0500
Subject: [PATCH 20/39] Fix duplicate function definition
---
.../Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index 394b228359ecd..765c93a24472b 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -374,8 +374,6 @@ def set_variable(self, varRef, name, value, id=None):
self.verify_memory_event(response["body"].get("memoryReference"))
return response
- def set_local(self, name, value, id=None):
- """Set a top level local variable only."""
def set_local(self, name, value, id=None):
"""Set a top level local variable only."""
# Get the locals scope reference dynamically
>From ce02a97a5a56a7a609e0ec1782a4e222a9952217 Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 9 Jan 2026 03:58:50 -0500
Subject: [PATCH 21/39] Move m_next_temporary_var_ref next to permanent var ref
---
lldb/tools/lldb-dap/Variables.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index 0e4c8685b6fde..27604c0193fa7 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -82,6 +82,8 @@ struct Variables {
private:
/// Variable_reference start index of permanent expandable variable.
static constexpr int64_t PermanentVariableStartIndex = (1ll << 32);
+
+ int64_t m_next_permanent_var_ref{PermanentVariableStartIndex};
int64_t m_next_temporary_var_ref{VARREF_FIRST_VAR_IDX};
// Variable Reference, dap_frame_id
@@ -100,7 +102,6 @@ struct Variables {
std::map<uint64_t,
std::tuple<lldb::SBValueList, lldb::SBValueList, lldb::SBValueList>>
m_frames;
- int64_t m_next_permanent_var_ref{PermanentVariableStartIndex};
};
} // namespace lldb_dap
>From 02005af223ea9db138a4b041d8d4c25c33d2cd6c Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 9 Jan 2026 04:01:49 -0500
Subject: [PATCH 22/39] Add e prefix to scopes kind enum
---
.../Handler/VariablesRequestHandler.cpp | 6 +--
lldb/tools/lldb-dap/Variables.cpp | 51 ++++++++++---------
lldb/tools/lldb-dap/Variables.h | 10 ++--
3 files changed, 34 insertions(+), 33 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
index ca5da1e78a79f..fe84b2df4f895 100644
--- a/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
@@ -40,7 +40,7 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
int64_t num_children = 0;
std::optional<ScopeData> scope_data = dap.variables.GetScopeKind(var_ref);
- if (scope_data.has_value() && scope_data->kind == ScopeKind::Registers) {
+ if (scope_data.has_value() && scope_data->kind == eScopeKind::Registers) {
// Change the default format of any pointer sized registers in the first
// register set to be the lldb::eFormatAddressInfo so we show the pointer
@@ -62,7 +62,7 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
num_children = top_scope->GetSize();
if (num_children == 0 && scope_data &&
- scope_data->kind == ScopeKind::Locals) {
+ scope_data->kind == eScopeKind::Locals) {
// Check for an error in the SBValueList that might explain why we don't
// have locals. If we have an error display it as the sole value in the
// the locals.
@@ -98,7 +98,7 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
}
// Show return value if there is any ( in the local top frame )
- if (scope_data && scope_data->kind == ScopeKind::Locals) {
+ if (scope_data && scope_data->kind == eScopeKind::Locals) {
auto process = dap.target.GetProcess();
auto selected_thread = process.GetSelectedThread();
lldb::SBValue stop_return_value = selected_thread.GetStopReturnValue();
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 3096c27267139..1c041a52cdac1 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -19,7 +19,7 @@ using namespace lldb_dap;
namespace lldb_dap {
-protocol::Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
+protocol::Scope CreateScope(const eScopeKind kind, int64_t variablesReference,
int64_t namedVariables, bool expensive) {
protocol::Scope scope;
@@ -31,14 +31,14 @@ protocol::Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
// be expanded if we enter a function with arguments. It becomes more
// annoying when the scope has arguments, return_value and locals.
switch (kind) {
- case ScopeKind::Locals:
+ case eScopeKind::Locals:
scope.presentationHint = protocol::Scope::eScopePresentationHintLocals;
scope.name = "Locals";
break;
- case ScopeKind::Globals:
+ case eScopeKind::Globals:
scope.name = "Globals";
break;
- case ScopeKind::Registers:
+ case eScopeKind::Registers:
scope.presentationHint = protocol::Scope::eScopePresentationHintRegisters;
scope.name = "Registers";
break;
@@ -57,7 +57,7 @@ lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
return nullptr;
}
- ScopeKind scope_kind = iter->second.first;
+ eScopeKind scope_kind = iter->second.first;
uint64_t dap_frame_id = iter->second.second;
auto frame_iter = m_frames.find(dap_frame_id);
@@ -66,11 +66,11 @@ lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
}
switch (scope_kind) {
- case lldb_dap::ScopeKind::Locals:
+ case lldb_dap::eScopeKind::Locals:
return &std::get<0>(frame_iter->second);
- case lldb_dap::ScopeKind::Globals:
+ case lldb_dap::eScopeKind::Globals:
return &std::get<1>(frame_iter->second);
- case lldb_dap::ScopeKind::Registers:
+ case lldb_dap::eScopeKind::Registers:
return &std::get<2>(frame_iter->second);
}
@@ -173,13 +173,13 @@ Variables::GetScopeKind(const int64_t variablesReference) {
scope_data.kind = scope_kind_iter->second.first;
switch (scope_kind_iter->second.first) {
- case lldb_dap::ScopeKind::Locals:
+ case lldb_dap::eScopeKind::Locals:
scope_data.scope = std::get<0>(scope_iter->second);
return scope_data;
- case lldb_dap::ScopeKind::Globals:
+ case lldb_dap::eScopeKind::Globals:
scope_data.scope = std::get<1>(scope_iter->second);
return scope_data;
- case lldb_dap::ScopeKind::Registers:
+ case lldb_dap::eScopeKind::Registers:
scope_data.scope = std::get<2>(scope_iter->second);
return scope_data;
}
@@ -188,7 +188,7 @@ Variables::GetScopeKind(const int64_t variablesReference) {
}
lldb::SBValueList *Variables::GetScope(const uint64_t dap_frame_id,
- const ScopeKind kind) {
+ const eScopeKind kind) {
auto frame = m_frames.find(dap_frame_id);
if (m_frames.find(dap_frame_id) == m_frames.end()) {
@@ -196,11 +196,11 @@ lldb::SBValueList *Variables::GetScope(const uint64_t dap_frame_id,
}
switch (kind) {
- case ScopeKind::Locals:
+ case eScopeKind::Locals:
return &std::get<0>(frame->second);
- case ScopeKind::Globals:
+ case eScopeKind::Globals:
return &std::get<1>(frame->second);
- case ScopeKind::Registers:
+ case eScopeKind::Registers:
return &std::get<2>(frame->second);
}
@@ -232,25 +232,26 @@ std::vector<protocol::Scope> Variables::ReadyFrame(const uint64_t dap_frame_id,
int64_t locals_ref = GetNewVariableReference(false);
- scopes.push_back(
- CreateScope(ScopeKind::Locals, locals_ref,
- GetScope(dap_frame_id, ScopeKind::Locals)->GetSize(), false));
+ scopes.push_back(CreateScope(
+ eScopeKind::Locals, locals_ref,
+ GetScope(dap_frame_id, eScopeKind::Locals)->GetSize(), false));
- m_scope_kinds[locals_ref] = std::make_pair(ScopeKind::Locals, dap_frame_id);
+ m_scope_kinds[locals_ref] = std::make_pair(eScopeKind::Locals, dap_frame_id);
int64_t globals_ref = GetNewVariableReference(false);
scopes.push_back(CreateScope(
- ScopeKind::Globals, globals_ref,
- GetScope(dap_frame_id, ScopeKind::Globals)->GetSize(), false));
- m_scope_kinds[globals_ref] = std::make_pair(ScopeKind::Globals, dap_frame_id);
+ eScopeKind::Globals, globals_ref,
+ GetScope(dap_frame_id, eScopeKind::Globals)->GetSize(), false));
+ m_scope_kinds[globals_ref] =
+ std::make_pair(eScopeKind::Globals, dap_frame_id);
int64_t registers_ref = GetNewVariableReference(false);
scopes.push_back(CreateScope(
- ScopeKind::Registers, registers_ref,
- GetScope(dap_frame_id, ScopeKind::Registers)->GetSize(), false));
+ eScopeKind::Registers, registers_ref,
+ GetScope(dap_frame_id, eScopeKind::Registers)->GetSize(), false));
m_scope_kinds[registers_ref] =
- std::make_pair(ScopeKind::Registers, dap_frame_id);
+ std::make_pair(eScopeKind::Registers, dap_frame_id);
return scopes;
}
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index 27604c0193fa7..0d3cdb877cdef 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -20,7 +20,7 @@
namespace lldb_dap {
-enum ScopeKind { Locals, Globals, Registers };
+enum eScopeKind { Locals, Globals, Registers };
/// Creates a `protocol::Scope` struct.
///
/// \param[in] kind
@@ -37,11 +37,11 @@ enum ScopeKind { Locals, Globals, Registers };
///
/// \return
/// A `protocol::Scope`
-protocol::Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
+protocol::Scope CreateScope(const eScopeKind kind, int64_t variablesReference,
int64_t namedVariables, bool expensive);
struct ScopeData {
- ScopeKind kind;
+ eScopeKind kind;
lldb::SBValueList scope;
};
@@ -61,7 +61,7 @@ struct Variables {
lldb::SBValue GetVariable(int64_t var_ref) const;
lldb::SBValueList *GetScope(const uint64_t dap_frame_id,
- const ScopeKind kind);
+ const eScopeKind kind);
/// Insert a new \p variable.
/// \return variableReference assigned to this expandable variable.
@@ -87,7 +87,7 @@ struct Variables {
int64_t m_next_temporary_var_ref{VARREF_FIRST_VAR_IDX};
// Variable Reference, dap_frame_id
- std::map<int64_t, std::pair<ScopeKind, uint64_t>> m_scope_kinds;
+ std::map<int64_t, std::pair<eScopeKind, uint64_t>> m_scope_kinds;
/// Variables that are alive in this stop state.
/// Will be cleared when debuggee resumes.
>From c5c5baa48729d844342f25380d417fcc1a148ed8 Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 9 Jan 2026 04:05:19 -0500
Subject: [PATCH 23/39] Fix comment copy and paste error
---
lldb/tools/lldb-dap/Variables.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 1c041a52cdac1..1b4024f5bcf71 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -24,7 +24,7 @@ protocol::Scope CreateScope(const eScopeKind kind, int64_t variablesReference,
protocol::Scope scope;
// TODO: Support "arguments" and "return value" scope.
- // At the moment lldb-he arguments and return_value into the
+ // At the moment lldb-dap includes the arguments and return_value into the
// "locals" scope.
// vscode only expands the first non-expensive scope, this causes friction
// if we add the arguments above the local scope as the locals scope will not
>From 3f9ed9095aef100ef34675f14b605775083b60f8 Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 9 Jan 2026 04:23:29 -0500
Subject: [PATCH 24/39] Create a FrameScope object to store a stack frames
scopes
I also made a helper method to get the scope kind based on the
eScopeKind enum
---
lldb/tools/lldb-dap/Variables.cpp | 45 ++++++++-----------------------
lldb/tools/lldb-dap/Variables.h | 26 +++++++++++++++---
2 files changed, 33 insertions(+), 38 deletions(-)
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 1b4024f5bcf71..659d1d2eb38dd 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -10,6 +10,7 @@
#include "JSONUtils.h"
#include "Protocol/ProtocolTypes.h"
#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBValue.h"
#include "lldb/API/SBValueList.h"
#include <cstdint>
#include <optional>
@@ -65,16 +66,7 @@ lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
return nullptr;
}
- switch (scope_kind) {
- case lldb_dap::eScopeKind::Locals:
- return &std::get<0>(frame_iter->second);
- case lldb_dap::eScopeKind::Globals:
- return &std::get<1>(frame_iter->second);
- case lldb_dap::eScopeKind::Registers:
- return &std::get<2>(frame_iter->second);
- }
-
- return nullptr;
+ return frame_iter->second.GetScope(scope_kind);
}
void Variables::Clear() {
@@ -171,40 +163,25 @@ Variables::GetScopeKind(const int64_t variablesReference) {
ScopeData scope_data = ScopeData();
scope_data.kind = scope_kind_iter->second.first;
+ lldb::SBValueList *scope = scope_iter->second.GetScope(scope_data.kind);
- switch (scope_kind_iter->second.first) {
- case lldb_dap::eScopeKind::Locals:
- scope_data.scope = std::get<0>(scope_iter->second);
- return scope_data;
- case lldb_dap::eScopeKind::Globals:
- scope_data.scope = std::get<1>(scope_iter->second);
- return scope_data;
- case lldb_dap::eScopeKind::Registers:
- scope_data.scope = std::get<2>(scope_iter->second);
- return scope_data;
+ if (scope == nullptr) {
+ return std::nullopt;
}
- return std::nullopt;
+ scope_data.scope = *scope;
+ return scope_data;
}
lldb::SBValueList *Variables::GetScope(const uint64_t dap_frame_id,
const eScopeKind kind) {
auto frame = m_frames.find(dap_frame_id);
- if (m_frames.find(dap_frame_id) == m_frames.end()) {
+ if (frame == m_frames.end()) {
return nullptr;
}
- switch (kind) {
- case eScopeKind::Locals:
- return &std::get<0>(frame->second);
- case eScopeKind::Globals:
- return &std::get<1>(frame->second);
- case eScopeKind::Registers:
- return &std::get<2>(frame->second);
- }
-
- return nullptr;
+ return frame->second.GetScope(kind);
}
std::vector<protocol::Scope> Variables::ReadyFrame(const uint64_t dap_frame_id,
@@ -224,8 +201,8 @@ std::vector<protocol::Scope> Variables::ReadyFrame(const uint64_t dap_frame_id,
auto registers = frame.GetRegisters();
- m_frames.insert(std::make_pair(
- dap_frame_id, std::make_tuple(locals, globals, registers)));
+ m_frames.insert(
+ std::make_pair(dap_frame_id, FrameScopes{locals, globals, registers}));
}
std::vector<protocol::Scope> scopes = {};
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index 0d3cdb877cdef..790a601acff6e 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -45,6 +45,26 @@ struct ScopeData {
lldb::SBValueList scope;
};
+/// Stores the three scope variable lists for a single stack frame.
+struct FrameScopes {
+ lldb::SBValueList locals;
+ lldb::SBValueList globals;
+ lldb::SBValueList registers;
+
+ /// Returns a pointer to the scope corresponding to the given kind.
+ lldb::SBValueList *GetScope(eScopeKind kind) {
+ switch (kind) {
+ case eScopeKind::Locals:
+ return &locals;
+ case eScopeKind::Globals:
+ return &globals;
+ case eScopeKind::Registers:
+ return ®isters;
+ }
+ return nullptr;
+ }
+};
+
struct 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
@@ -98,10 +118,8 @@ struct Variables {
llvm::DenseMap<int64_t, lldb::SBValue> m_referencedpermanent_variables;
/// Key = dap_frame_id (encodes both thread index ID and frame ID)
- /// Value = (locals, globals, registers) scopes
- std::map<uint64_t,
- std::tuple<lldb::SBValueList, lldb::SBValueList, lldb::SBValueList>>
- m_frames;
+ /// Value = scopes for the frame (locals, globals, registers)
+ std::map<uint64_t, FrameScopes> m_frames;
};
} // namespace lldb_dap
>From 1031b10073114d05f46490d16237ffb54101388e Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 9 Jan 2026 04:34:10 -0500
Subject: [PATCH 25/39] Switch VARREF_FIRST_VAR_IDX from a #define variable to
a constexpr
This follows what `PermanentVariableStartIndex` does and is more inline
with newer c++ standards.
I renamed the variable to `TemporaryVariableStartIndex` as well
---
lldb/tools/lldb-dap/Variables.cpp | 2 +-
lldb/tools/lldb-dap/Variables.h | 9 +++++----
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 659d1d2eb38dd..3faf553000bc1 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -73,7 +73,7 @@ void Variables::Clear() {
m_referencedvariables.clear();
m_scope_kinds.clear();
m_frames.clear();
- m_next_temporary_var_ref = VARREF_FIRST_VAR_IDX;
+ m_next_temporary_var_ref = TemporaryVariableStartIndex;
}
int64_t Variables::GetNewVariableReference(bool is_permanent) {
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index 790a601acff6e..b8069198733a8 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -16,8 +16,6 @@
#include <map>
#include <utility>
-#define VARREF_FIRST_VAR_IDX (int64_t)1
-
namespace lldb_dap {
enum eScopeKind { Locals, Globals, Registers };
@@ -100,11 +98,14 @@ struct Variables {
void Clear();
private:
- /// Variable_reference start index of permanent expandable variable.
+ /// Variable reference start index of temporary variables.
+ static constexpr int64_t TemporaryVariableStartIndex = 1;
+
+ /// Variable reference start index of permanent expandable variable.
static constexpr int64_t PermanentVariableStartIndex = (1ll << 32);
int64_t m_next_permanent_var_ref{PermanentVariableStartIndex};
- int64_t m_next_temporary_var_ref{VARREF_FIRST_VAR_IDX};
+ int64_t m_next_temporary_var_ref{TemporaryVariableStartIndex};
// Variable Reference, dap_frame_id
std::map<int64_t, std::pair<eScopeKind, uint64_t>> m_scope_kinds;
>From 1ae6545f7114d33226a4aa182b83a7cab7d13dd3 Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 9 Jan 2026 04:46:05 -0500
Subject: [PATCH 26/39] Add scope lookup helper for DAP VariablesTest
---
lldb/unittests/DAP/VariablesTest.cpp | 39 ++++++++++++++++++++--------
1 file changed, 28 insertions(+), 11 deletions(-)
diff --git a/lldb/unittests/DAP/VariablesTest.cpp b/lldb/unittests/DAP/VariablesTest.cpp
index 8857d420ced20..0263586cadc53 100644
--- a/lldb/unittests/DAP/VariablesTest.cpp
+++ b/lldb/unittests/DAP/VariablesTest.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "Variables.h"
+#include "Protocol/ProtocolTypes.h"
#include "lldb/API/SBFrame.h"
#include "lldb/API/SBValue.h"
#include "lldb/API/SBValueList.h"
@@ -18,6 +19,15 @@ class VariablesTest : public ::testing::Test {
protected:
enum : bool { Permanent = true, Temporary = false };
Variables vars;
+
+ static const protocol::Scope *
+ FindScope(const std::vector<protocol::Scope> &scopes, llvm::StringRef name) {
+ for (const auto &scope : scopes) {
+ if (scope.name == name)
+ return &scope;
+ }
+ return nullptr;
+ }
};
TEST_F(VariablesTest, GetNewVariableReference_UniqueAndRanges) {
@@ -70,16 +80,22 @@ TEST_F(VariablesTest, GetTopLevelScope_ReturnsCorrectScope) {
lldb::SBFrame frame;
uint32_t frame_id = 0;
- vars.ReadyFrame(frame_id, frame);
+ std::vector<protocol::Scope> scopes = vars.ReadyFrame(frame_id, frame);
+
+ const protocol::Scope *locals_scope = FindScope(scopes, "Locals");
+ const protocol::Scope *globals_scope = FindScope(scopes, "Globals");
+ const protocol::Scope *registers_scope = FindScope(scopes, "Registers");
- int64_t next_variable_ref = vars.GetNewVariableReference(false);
+ ASSERT_NE(locals_scope, nullptr);
+ ASSERT_NE(globals_scope, nullptr);
+ ASSERT_NE(registers_scope, nullptr);
- EXPECT_EQ(vars.GetTopLevelScope(next_variable_ref - 3),
- vars.GetScope(frame_id, lldb_dap::ScopeKind::Locals));
- EXPECT_EQ(vars.GetTopLevelScope(next_variable_ref - 2),
- vars.GetScope(frame_id, lldb_dap::ScopeKind::Globals));
- EXPECT_EQ(vars.GetTopLevelScope(next_variable_ref - 1),
- vars.GetScope(frame_id, lldb_dap::ScopeKind::Registers));
+ EXPECT_EQ(vars.GetTopLevelScope(locals_scope->variablesReference),
+ vars.GetScope(frame_id, eScopeKind::Locals));
+ EXPECT_EQ(vars.GetTopLevelScope(globals_scope->variablesReference),
+ vars.GetScope(frame_id, eScopeKind::Globals));
+ EXPECT_EQ(vars.GetTopLevelScope(registers_scope->variablesReference),
+ vars.GetScope(frame_id, eScopeKind::Registers));
EXPECT_EQ(vars.GetTopLevelScope(9999), nullptr);
}
@@ -87,11 +103,12 @@ TEST_F(VariablesTest, FindVariable_LocalsByName) {
lldb::SBFrame frame;
uint32_t frame_id = 0;
- vars.ReadyFrame(frame_id, frame);
+ std::vector<protocol::Scope> scopes = vars.ReadyFrame(frame_id, frame);
- int64_t locals_ref = vars.GetNewVariableReference(false);
+ const protocol::Scope *locals_scope = FindScope(scopes, "Locals");
+ ASSERT_NE(locals_scope, nullptr);
- lldb::SBValue found = vars.FindVariable(locals_ref - 1, "");
+ lldb::SBValue found = vars.FindVariable(locals_scope->variablesReference, "");
lldb::SBValue dummy;
EXPECT_EQ(found.IsValid(), dummy.IsValid());
>From 74e8e46c6623e33affc7473ee9b53946d69bc572 Mon Sep 17 00:00:00 2001
From: Anthony Eid <56899983+Anthony-Eid at users.noreply.github.com>
Date: Fri, 9 Jan 2026 16:13:18 -0500
Subject: [PATCH 27/39] Update lldb/tools/lldb-dap/Variables.cpp
Co-authored-by: Jonas Devlieghere <jonas at devlieghere.com>
---
lldb/tools/lldb-dap/Variables.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 3faf553000bc1..301a4ca9eb64e 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -62,9 +62,8 @@ lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
uint64_t dap_frame_id = iter->second.second;
auto frame_iter = m_frames.find(dap_frame_id);
- if (frame_iter == m_frames.end()) {
- return nullptr;
- }
+ if (frame_iter == m_frames.end())
+return nullptr;
return frame_iter->second.GetScope(scope_kind);
}
>From 70af7984c4fc347fff82c3bd37ba518d0bcb4eab Mon Sep 17 00:00:00 2001
From: Anthony Eid <56899983+Anthony-Eid at users.noreply.github.com>
Date: Fri, 9 Jan 2026 16:13:37 -0500
Subject: [PATCH 28/39] Update lldb/tools/lldb-dap/Variables.cpp
Co-authored-by: Jonas Devlieghere <jonas at devlieghere.com>
---
lldb/tools/lldb-dap/Variables.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 301a4ca9eb64e..b71ede58d23d1 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -28,7 +28,7 @@ protocol::Scope CreateScope(const eScopeKind kind, int64_t variablesReference,
// At the moment lldb-dap includes the arguments and return_value into the
// "locals" scope.
// vscode only expands the first non-expensive scope, this causes friction
- // if we add the arguments above the local scope as the locals scope will not
+ // if we add the arguments above the local scope, as the locals scope will not
// be expanded if we enter a function with arguments. It becomes more
// annoying when the scope has arguments, return_value and locals.
switch (kind) {
>From cae84f518b27bb8260d2dc67db5bd77539bdf0c4 Mon Sep 17 00:00:00 2001
From: Anthony Eid <56899983+Anthony-Eid at users.noreply.github.com>
Date: Fri, 9 Jan 2026 16:14:03 -0500
Subject: [PATCH 29/39] Update lldb/tools/lldb-dap/Variables.cpp
Co-authored-by: Jonas Devlieghere <jonas at devlieghere.com>
---
lldb/tools/lldb-dap/Variables.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index b71ede58d23d1..db82b4ed19b52 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -54,9 +54,8 @@ protocol::Scope CreateScope(const eScopeKind kind, int64_t variablesReference,
lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
auto iter = m_scope_kinds.find(variablesReference);
- if (iter == m_scope_kinds.end()) {
+ if (iter == m_scope_kinds.end())
return nullptr;
- }
eScopeKind scope_kind = iter->second.first;
uint64_t dap_frame_id = iter->second.second;
>From 7ea1f9deedfb3420394941441d18c6635022762f Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 9 Jan 2026 05:36:22 -0500
Subject: [PATCH 30/39] Explicitly add optional include to variable.h
---
lldb/tools/lldb-dap/Variables.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index b8069198733a8..0d11cc8450779 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -14,6 +14,7 @@
#include "lldb/API/SBValueList.h"
#include "llvm/ADT/DenseMap.h"
#include <map>
+#include <optional>
#include <utility>
namespace lldb_dap {
>From 3d844d1a7a3fa6dbdd6e56994090917e2f2f3f56 Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 9 Jan 2026 16:16:13 -0500
Subject: [PATCH 31/39] Add llvm unreachable
---
lldb/tools/lldb-dap/Variables.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index 0d11cc8450779..c7c75f0ae2fdc 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -60,7 +60,8 @@ struct FrameScopes {
case eScopeKind::Registers:
return ®isters;
}
- return nullptr;
+
+ llvm_unreachable("unknown scope kind");
}
};
>From cd90407e8f276c03eca99d51c37937dcb0c58393 Mon Sep 17 00:00:00 2001
From: Anthony Eid <56899983+Anthony-Eid at users.noreply.github.com>
Date: Fri, 9 Jan 2026 16:16:59 -0500
Subject: [PATCH 32/39] Update lldb/tools/lldb-dap/Variables.cpp
Co-authored-by: Jonas Devlieghere <jonas at devlieghere.com>
---
lldb/tools/lldb-dap/Variables.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index db82b4ed19b52..bffd0369cd915 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -27,7 +27,7 @@ protocol::Scope CreateScope(const eScopeKind kind, int64_t variablesReference,
// TODO: Support "arguments" and "return value" scope.
// At the moment lldb-dap includes the arguments and return_value into the
// "locals" scope.
- // vscode only expands the first non-expensive scope, this causes friction
+ // VS Code only expands the first non-expensive scope. This causes friction
// if we add the arguments above the local scope, as the locals scope will not
// be expanded if we enter a function with arguments. It becomes more
// annoying when the scope has arguments, return_value and locals.
>From 6d680eecd827daf5fbc593eb9b2e9fac50b836c0 Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 9 Jan 2026 16:18:09 -0500
Subject: [PATCH 33/39] Move scope field initialization closer to func args
---
lldb/tools/lldb-dap/Variables.cpp | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index db82b4ed19b52..1512b29e04274 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -23,6 +23,9 @@ namespace lldb_dap {
protocol::Scope CreateScope(const eScopeKind kind, int64_t variablesReference,
int64_t namedVariables, bool expensive) {
protocol::Scope scope;
+ scope.variablesReference = variablesReference;
+ scope.namedVariables = namedVariables;
+ scope.expensive = expensive;
// TODO: Support "arguments" and "return value" scope.
// At the moment lldb-dap includes the arguments and return_value into the
@@ -45,16 +48,12 @@ protocol::Scope CreateScope(const eScopeKind kind, int64_t variablesReference,
break;
}
- scope.variablesReference = variablesReference;
- scope.namedVariables = namedVariables;
- scope.expensive = expensive;
-
return scope;
}
lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
auto iter = m_scope_kinds.find(variablesReference);
- if (iter == m_scope_kinds.end())
+ if (iter == m_scope_kinds.end())
return nullptr;
eScopeKind scope_kind = iter->second.first;
@@ -62,7 +61,7 @@ lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
auto frame_iter = m_frames.find(dap_frame_id);
if (frame_iter == m_frames.end())
-return nullptr;
+ return nullptr;
return frame_iter->second.GetScope(scope_kind);
}
>From 23f21e07ae944aff43784486edc9db580d5ffeea Mon Sep 17 00:00:00 2001
From: Anthony Eid <56899983+Anthony-Eid at users.noreply.github.com>
Date: Tue, 13 Jan 2026 15:11:38 -0500
Subject: [PATCH 34/39] Update lldb/tools/lldb-dap/Variables.cpp
Co-authored-by: Ebuka Ezike <yerimyah1 at gmail.com>
---
lldb/tools/lldb-dap/Variables.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index bffd0369cd915..00181aa5c6664 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -217,8 +217,7 @@ std::vector<protocol::Scope> Variables::ReadyFrame(const uint64_t dap_frame_id,
scopes.push_back(CreateScope(
eScopeKind::Globals, globals_ref,
GetScope(dap_frame_id, eScopeKind::Globals)->GetSize(), false));
- m_scope_kinds[globals_ref] =
- std::make_pair(eScopeKind::Globals, dap_frame_id);
+ m_scope_kinds.try_emplace(globals_ref, eScopeKind::Globals, dap_frame_id);
int64_t registers_ref = GetNewVariableReference(false);
scopes.push_back(CreateScope(
>From aaed972a765d76e3c4e5a8ce7472dd6748c3a729 Mon Sep 17 00:00:00 2001
From: Anthony Eid <56899983+Anthony-Eid at users.noreply.github.com>
Date: Tue, 13 Jan 2026 15:12:19 -0500
Subject: [PATCH 35/39] Update lldb/tools/lldb-dap/Variables.cpp
Co-authored-by: Ebuka Ezike <yerimyah1 at gmail.com>
---
lldb/tools/lldb-dap/Variables.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 00181aa5c6664..13a91f2fd4353 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -154,8 +154,8 @@ Variables::GetScopeKind(const int64_t variablesReference) {
return std::nullopt;
}
- auto scope_iter = m_frames.find(scope_kind_iter->second.second);
- if (scope_iter == m_frames.end()) {
+ auto frames_iter = m_frames.find(scope_kind_iter->second.second);
+ if (frames_iter == m_frames.end()) {
return std::nullopt;
}
>From c054399a62d711ddaa84d56f936951881842d8ce Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Thu, 29 Jan 2026 23:04:40 -0500
Subject: [PATCH 36/39] Rename ScopeKind and add unsigned typing
---
.../Handler/VariablesRequestHandler.cpp | 6 ++--
lldb/tools/lldb-dap/Variables.cpp | 35 +++++++++----------
lldb/tools/lldb-dap/Variables.h | 22 +++++++-----
lldb/unittests/DAP/VariablesTest.cpp | 6 ++--
4 files changed, 36 insertions(+), 33 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
index 7a6245ff79eed..d31858643f640 100644
--- a/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
@@ -38,7 +38,7 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
int64_t num_children = 0;
std::optional<ScopeData> scope_data = dap.variables.GetScopeKind(var_ref);
- if (scope_data.has_value() && scope_data->kind == eScopeKind::Registers) {
+ if (scope_data.has_value() && scope_data->kind == eScopeKindRegisters) {
// Change the default format of any pointer sized registers in the first
// register set to be the lldb::eFormatAddressInfo so we show the pointer
@@ -60,7 +60,7 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
num_children = top_scope->GetSize();
if (num_children == 0 && scope_data &&
- scope_data->kind == eScopeKind::Locals) {
+ scope_data->kind == eScopeKindLocals) {
// Check for an error in the SBValueList that might explain why we don't
// have locals. If we have an error display it as the sole value in the
// the locals.
@@ -96,7 +96,7 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
}
// Show return value if there is any ( in the local top frame )
- if (scope_data && scope_data->kind == eScopeKind::Locals) {
+ if (scope_data && scope_data->kind == eScopeKindLocals) {
auto process = dap.target.GetProcess();
auto selected_thread = process.GetSelectedThread();
lldb::SBValue stop_return_value = selected_thread.GetStopReturnValue();
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 1512b29e04274..d13aebc3c42b6 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -20,7 +20,7 @@ using namespace lldb_dap;
namespace lldb_dap {
-protocol::Scope CreateScope(const eScopeKind kind, int64_t variablesReference,
+protocol::Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
int64_t namedVariables, bool expensive) {
protocol::Scope scope;
scope.variablesReference = variablesReference;
@@ -35,14 +35,14 @@ protocol::Scope CreateScope(const eScopeKind kind, int64_t variablesReference,
// be expanded if we enter a function with arguments. It becomes more
// annoying when the scope has arguments, return_value and locals.
switch (kind) {
- case eScopeKind::Locals:
+ case eScopeKindLocals:
scope.presentationHint = protocol::Scope::eScopePresentationHintLocals;
scope.name = "Locals";
break;
- case eScopeKind::Globals:
+ case eScopeKindGlobals:
scope.name = "Globals";
break;
- case eScopeKind::Registers:
+ case eScopeKindRegisters:
scope.presentationHint = protocol::Scope::eScopePresentationHintRegisters;
scope.name = "Registers";
break;
@@ -56,7 +56,7 @@ lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
if (iter == m_scope_kinds.end())
return nullptr;
- eScopeKind scope_kind = iter->second.first;
+ ScopeKind scope_kind = iter->second.first;
uint64_t dap_frame_id = iter->second.second;
auto frame_iter = m_frames.find(dap_frame_id);
@@ -171,7 +171,7 @@ Variables::GetScopeKind(const int64_t variablesReference) {
}
lldb::SBValueList *Variables::GetScope(const uint64_t dap_frame_id,
- const eScopeKind kind) {
+ const ScopeKind kind) {
auto frame = m_frames.find(dap_frame_id);
if (frame == m_frames.end()) {
@@ -206,26 +206,25 @@ std::vector<protocol::Scope> Variables::ReadyFrame(const uint64_t dap_frame_id,
int64_t locals_ref = GetNewVariableReference(false);
- scopes.push_back(CreateScope(
- eScopeKind::Locals, locals_ref,
- GetScope(dap_frame_id, eScopeKind::Locals)->GetSize(), false));
+ scopes.push_back(
+ CreateScope(eScopeKindLocals, locals_ref,
+ GetScope(dap_frame_id, eScopeKindLocals)->GetSize(), false));
- m_scope_kinds[locals_ref] = std::make_pair(eScopeKind::Locals, dap_frame_id);
+ m_scope_kinds[locals_ref] = std::make_pair(eScopeKindLocals, dap_frame_id);
int64_t globals_ref = GetNewVariableReference(false);
- scopes.push_back(CreateScope(
- eScopeKind::Globals, globals_ref,
- GetScope(dap_frame_id, eScopeKind::Globals)->GetSize(), false));
- m_scope_kinds[globals_ref] =
- std::make_pair(eScopeKind::Globals, dap_frame_id);
+ scopes.push_back(
+ CreateScope(eScopeKindGlobals, globals_ref,
+ GetScope(dap_frame_id, eScopeKindGlobals)->GetSize(), false));
+ m_scope_kinds[globals_ref] = std::make_pair(eScopeKindGlobals, dap_frame_id);
int64_t registers_ref = GetNewVariableReference(false);
scopes.push_back(CreateScope(
- eScopeKind::Registers, registers_ref,
- GetScope(dap_frame_id, eScopeKind::Registers)->GetSize(), false));
+ eScopeKindRegisters, registers_ref,
+ GetScope(dap_frame_id, eScopeKindRegisters)->GetSize(), false));
m_scope_kinds[registers_ref] =
- std::make_pair(eScopeKind::Registers, dap_frame_id);
+ std::make_pair(eScopeKindRegisters, dap_frame_id);
return scopes;
}
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index c7c75f0ae2fdc..f2c4d1a3de6ec 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -19,7 +19,11 @@
namespace lldb_dap {
-enum eScopeKind { Locals, Globals, Registers };
+enum ScopeKind : unsigned {
+ eScopeKindLocals,
+ eScopeKindGlobals,
+ eScopeKindRegisters
+};
/// Creates a `protocol::Scope` struct.
///
/// \param[in] kind
@@ -36,11 +40,11 @@ enum eScopeKind { Locals, Globals, Registers };
///
/// \return
/// A `protocol::Scope`
-protocol::Scope CreateScope(const eScopeKind kind, int64_t variablesReference,
+protocol::Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
int64_t namedVariables, bool expensive);
struct ScopeData {
- eScopeKind kind;
+ ScopeKind kind;
lldb::SBValueList scope;
};
@@ -51,13 +55,13 @@ struct FrameScopes {
lldb::SBValueList registers;
/// Returns a pointer to the scope corresponding to the given kind.
- lldb::SBValueList *GetScope(eScopeKind kind) {
+ lldb::SBValueList *GetScope(ScopeKind kind) {
switch (kind) {
- case eScopeKind::Locals:
+ case eScopeKindLocals:
return &locals;
- case eScopeKind::Globals:
+ case eScopeKindGlobals:
return &globals;
- case eScopeKind::Registers:
+ case eScopeKindRegisters:
return ®isters;
}
@@ -81,7 +85,7 @@ struct Variables {
lldb::SBValue GetVariable(int64_t var_ref) const;
lldb::SBValueList *GetScope(const uint64_t dap_frame_id,
- const eScopeKind kind);
+ const ScopeKind kind);
/// Insert a new \p variable.
/// \return variableReference assigned to this expandable variable.
@@ -110,7 +114,7 @@ struct Variables {
int64_t m_next_temporary_var_ref{TemporaryVariableStartIndex};
// Variable Reference, dap_frame_id
- std::map<int64_t, std::pair<eScopeKind, uint64_t>> m_scope_kinds;
+ std::map<int64_t, std::pair<ScopeKind, uint64_t>> m_scope_kinds;
/// Variables that are alive in this stop state.
/// Will be cleared when debuggee resumes.
diff --git a/lldb/unittests/DAP/VariablesTest.cpp b/lldb/unittests/DAP/VariablesTest.cpp
index 0263586cadc53..c6c6b6e02b169 100644
--- a/lldb/unittests/DAP/VariablesTest.cpp
+++ b/lldb/unittests/DAP/VariablesTest.cpp
@@ -91,11 +91,11 @@ TEST_F(VariablesTest, GetTopLevelScope_ReturnsCorrectScope) {
ASSERT_NE(registers_scope, nullptr);
EXPECT_EQ(vars.GetTopLevelScope(locals_scope->variablesReference),
- vars.GetScope(frame_id, eScopeKind::Locals));
+ vars.GetScope(frame_id, eScopeKindLocals));
EXPECT_EQ(vars.GetTopLevelScope(globals_scope->variablesReference),
- vars.GetScope(frame_id, eScopeKind::Globals));
+ vars.GetScope(frame_id, eScopeKindGlobals));
EXPECT_EQ(vars.GetTopLevelScope(registers_scope->variablesReference),
- vars.GetScope(frame_id, eScopeKind::Registers));
+ vars.GetScope(frame_id, eScopeKindRegisters));
EXPECT_EQ(vars.GetTopLevelScope(9999), nullptr);
}
>From 7924902a53864737407524cc9ce517b611d8c23d Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 30 Jan 2026 05:08:07 -0500
Subject: [PATCH 37/39] rename ReadyFrame to CreateScopes
---
lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp | 2 +-
lldb/tools/lldb-dap/Variables.cpp | 4 ++--
lldb/tools/lldb-dap/Variables.h | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
index 5a016265f770f..251711ca62406 100644
--- a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
@@ -37,7 +37,7 @@ ScopesRequestHandler::Run(const ScopesArguments &args) const {
}
std::vector<protocol::Scope> scopes =
- dap.variables.ReadyFrame(args.frameId, frame);
+ dap.variables.CreateScopes(args.frameId, frame);
return ScopesResponseBody{std::move(scopes)};
}
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 84e7f5642e4b8..77799b546afd1 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -181,8 +181,8 @@ lldb::SBValueList *Variables::GetScope(const uint64_t dap_frame_id,
return frame->second.GetScope(kind);
}
-std::vector<protocol::Scope> Variables::ReadyFrame(const uint64_t dap_frame_id,
- lldb::SBFrame &frame) {
+std::vector<protocol::Scope>
+Variables::CreateScopes(const uint64_t dap_frame_id, lldb::SBFrame &frame) {
if (m_frames.find(dap_frame_id) == m_frames.end()) {
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index f2c4d1a3de6ec..867c6c82312bf 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -96,8 +96,8 @@ struct Variables {
lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name);
/// Initialize a frame if it hasn't been already, otherwise do nothing
- std::vector<protocol::Scope> ReadyFrame(const uint64_t dap_frame_id,
- lldb::SBFrame &frame);
+ std::vector<protocol::Scope> CreateScopes(const uint64_t dap_frame_id,
+ lldb::SBFrame &frame);
std::optional<ScopeData> GetScopeKind(const int64_t variablesReference);
/// Clear all scope variables and non-permanent expandable variables.
>From 324f7f0beb7d1edc8094181aa8b48045f06c7375 Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 30 Jan 2026 05:14:05 -0500
Subject: [PATCH 38/39] Make GetTopLevelScope return an optional<ScopeData>
---
.../Handler/VariablesRequestHandler.cpp | 17 +++---
lldb/tools/lldb-dap/Variables.cpp | 55 +++++++------------
lldb/tools/lldb-dap/Variables.h | 3 +-
3 files changed, 29 insertions(+), 46 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
index d31858643f640..ee7cb525d9c29 100644
--- a/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp
@@ -31,14 +31,14 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
std::vector<Variable> variables;
- if (lldb::SBValueList *top_scope = dap.variables.GetTopLevelScope(var_ref)) {
+ std::optional<ScopeData> scope_data = dap.variables.GetTopLevelScope(var_ref);
+ if (scope_data) {
// 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;
- std::optional<ScopeData> scope_data = dap.variables.GetScopeKind(var_ref);
- if (scope_data.has_value() && scope_data->kind == eScopeKindRegisters) {
+ if (scope_data->kind == eScopeKindRegisters) {
// Change the default format of any pointer sized registers in the first
// register set to be the lldb::eFormatAddressInfo so we show the pointer
@@ -58,16 +58,15 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
}
}
- num_children = top_scope->GetSize();
- if (num_children == 0 && scope_data &&
- scope_data->kind == eScopeKindLocals) {
+ num_children = scope_data->scope.GetSize();
+ if (num_children == 0 && scope_data->kind == eScopeKindLocals) {
// Check for an error in the SBValueList that might explain why we don't
// have locals. If we have an error display it as the sole value in the
// the locals.
// "error" owns the error string so we must keep it alive as long as we
// want to use the returns "const char *"
- lldb::SBError error = top_scope->GetError();
+ lldb::SBError error = scope_data->scope.GetError();
const char *var_err = error.GetCString();
if (var_err) {
// Create a fake variable named "error" to explain why variables were
@@ -89,7 +88,7 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
// We first find out which variable names are duplicated
std::map<llvm::StringRef, int> variable_name_counts;
for (auto i = start_idx; i < end_idx; ++i) {
- lldb::SBValue variable = top_scope->GetValueAtIndex(i);
+ lldb::SBValue variable = scope_data->scope.GetValueAtIndex(i);
if (!variable.IsValid())
break;
variable_name_counts[GetNonNullVariableName(variable)]++;
@@ -120,7 +119,7 @@ VariablesRequestHandler::Run(const VariablesArguments &arguments) const {
// Now we construct the result with unique display variable names
for (auto i = start_idx; i < end_idx; ++i) {
- lldb::SBValue variable = top_scope->GetValueAtIndex(i);
+ lldb::SBValue variable = scope_data->scope.GetValueAtIndex(i);
if (!variable.IsValid())
break;
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 77799b546afd1..28d0888e3634a 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -51,19 +51,27 @@ protocol::Scope CreateScope(const ScopeKind kind, int64_t variablesReference,
return scope;
}
-lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
- auto iter = m_scope_kinds.find(variablesReference);
- if (iter == m_scope_kinds.end())
- return nullptr;
+std::optional<ScopeData>
+Variables::GetTopLevelScope(int64_t variablesReference) {
+ auto scope_kind_iter = m_scope_kinds.find(variablesReference);
+ if (scope_kind_iter == m_scope_kinds.end())
+ return std::nullopt;
- ScopeKind scope_kind = iter->second.first;
- uint64_t dap_frame_id = iter->second.second;
+ ScopeKind scope_kind = scope_kind_iter->second.first;
+ uint64_t dap_frame_id = scope_kind_iter->second.second;
auto frame_iter = m_frames.find(dap_frame_id);
if (frame_iter == m_frames.end())
- return nullptr;
+ return std::nullopt;
- return frame_iter->second.GetScope(scope_kind);
+ lldb::SBValueList *scope = frame_iter->second.GetScope(scope_kind);
+ if (scope == nullptr)
+ return std::nullopt;
+
+ ScopeData scope_data;
+ scope_data.kind = scope_kind;
+ scope_data.scope = *scope;
+ return scope_data;
}
void Variables::Clear() {
@@ -108,15 +116,16 @@ int64_t Variables::InsertVariable(lldb::SBValue variable, bool is_permanent) {
lldb::SBValue Variables::FindVariable(uint64_t variablesReference,
llvm::StringRef name) {
lldb::SBValue variable;
- if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) {
+ if (std::optional<ScopeData> scope_data =
+ GetTopLevelScope(variablesReference)) {
bool is_duplicated_variable_name = name.contains(" @");
// variablesReference is one of our scopes, not an actual variable it is
// asking for a variable in locals or globals or registers
- int64_t end_idx = top_scope->GetSize();
+ int64_t end_idx = scope_data->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);
+ lldb::SBValue curr_variable = scope_data->scope.GetValueAtIndex(i);
std::string variable_name = CreateUniqueVariableNameForDisplay(
curr_variable, is_duplicated_variable_name);
if (variable_name == name) {
@@ -146,30 +155,6 @@ lldb::SBValue Variables::FindVariable(uint64_t variablesReference,
return variable;
}
-std::optional<ScopeData>
-Variables::GetScopeKind(const int64_t variablesReference) {
- auto scope_kind_iter = m_scope_kinds.find(variablesReference);
- if (scope_kind_iter == m_scope_kinds.end()) {
- return std::nullopt;
- }
-
- auto frames_iter = m_frames.find(scope_kind_iter->second.second);
- if (frames_iter == m_frames.end()) {
- return std::nullopt;
- }
-
- ScopeData scope_data = ScopeData();
- scope_data.kind = scope_kind_iter->second.first;
- lldb::SBValueList *scope = frames_iter->second.GetScope(scope_data.kind);
-
- if (scope == nullptr) {
- return std::nullopt;
- }
-
- scope_data.scope = *scope;
- return scope_data;
-}
-
lldb::SBValueList *Variables::GetScope(const uint64_t dap_frame_id,
const ScopeKind kind) {
diff --git a/lldb/tools/lldb-dap/Variables.h b/lldb/tools/lldb-dap/Variables.h
index 867c6c82312bf..9814cabc7036c 100644
--- a/lldb/tools/lldb-dap/Variables.h
+++ b/lldb/tools/lldb-dap/Variables.h
@@ -91,14 +91,13 @@ struct Variables {
/// \return variableReference assigned to this expandable variable.
int64_t InsertVariable(lldb::SBValue variable, bool is_permanent);
- lldb::SBValueList *GetTopLevelScope(int64_t variablesReference);
+ std::optional<ScopeData> GetTopLevelScope(int64_t variablesReference);
lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name);
/// Initialize a frame if it hasn't been already, otherwise do nothing
std::vector<protocol::Scope> CreateScopes(const uint64_t dap_frame_id,
lldb::SBFrame &frame);
- std::optional<ScopeData> GetScopeKind(const int64_t variablesReference);
/// Clear all scope variables and non-permanent expandable variables.
void Clear();
>From cacb4c23ea9fa4ffc70828ff8ee47f905534f657 Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 30 Jan 2026 05:17:39 -0500
Subject: [PATCH 39/39] Clean up CreateScope function
---
lldb/tools/lldb-dap/Variables.cpp | 44 +++++++++++--------------------
1 file changed, 16 insertions(+), 28 deletions(-)
diff --git a/lldb/tools/lldb-dap/Variables.cpp b/lldb/tools/lldb-dap/Variables.cpp
index 28d0888e3634a..6d49113243799 100644
--- a/lldb/tools/lldb-dap/Variables.cpp
+++ b/lldb/tools/lldb-dap/Variables.cpp
@@ -168,9 +168,8 @@ lldb::SBValueList *Variables::GetScope(const uint64_t dap_frame_id,
std::vector<protocol::Scope>
Variables::CreateScopes(const uint64_t dap_frame_id, lldb::SBFrame &frame) {
-
- if (m_frames.find(dap_frame_id) == m_frames.end()) {
-
+ auto iter = m_frames.find(dap_frame_id);
+ if (iter == m_frames.end()) {
auto locals = frame.GetVariables(/*arguments=*/true,
/*locals=*/true,
/*statics=*/false,
@@ -183,35 +182,24 @@ Variables::CreateScopes(const uint64_t dap_frame_id, lldb::SBFrame &frame) {
auto registers = frame.GetRegisters();
- m_frames.insert(
- std::make_pair(dap_frame_id, FrameScopes{locals, globals, registers}));
+ iter =
+ m_frames.emplace(dap_frame_id, FrameScopes{locals, globals, registers})
+ .first;
}
- std::vector<protocol::Scope> scopes = {};
-
- int64_t locals_ref = GetNewVariableReference(false);
-
- scopes.push_back(
- CreateScope(eScopeKindLocals, locals_ref,
- GetScope(dap_frame_id, eScopeKindLocals)->GetSize(), false));
-
- m_scope_kinds[locals_ref] = std::make_pair(eScopeKindLocals, dap_frame_id);
-
- int64_t globals_ref = GetNewVariableReference(false);
- scopes.push_back(
- CreateScope(eScopeKindGlobals, globals_ref,
- GetScope(dap_frame_id, eScopeKindGlobals)->GetSize(), false));
- m_scope_kinds[globals_ref] = std::make_pair(eScopeKindGlobals, dap_frame_id);
-
- int64_t registers_ref = GetNewVariableReference(false);
- scopes.push_back(CreateScope(
- eScopeKindRegisters, registers_ref,
- GetScope(dap_frame_id, eScopeKindRegisters)->GetSize(), false));
+ const FrameScopes &frame_scopes = iter->second;
- m_scope_kinds[registers_ref] =
- std::make_pair(eScopeKindRegisters, dap_frame_id);
+ auto create_scope = [&](ScopeKind kind, uint32_t size) {
+ int64_t ref = GetNewVariableReference(false);
+ m_scope_kinds.try_emplace(ref, kind, dap_frame_id);
+ return CreateScope(kind, ref, size, false);
+ };
- return scopes;
+ return {
+ create_scope(eScopeKindLocals, frame_scopes.locals.GetSize()),
+ create_scope(eScopeKindGlobals, frame_scopes.globals.GetSize()),
+ create_scope(eScopeKindRegisters, frame_scopes.registers.GetSize()),
+ };
}
} // namespace lldb_dap
More information about the lldb-commits
mailing list