[Lldb-commits] [lldb] [lldb][lldb-dap] Migrate ScopesRequest to structured types (PR #138116)
Ebuka Ezike via lldb-commits
lldb-commits at lists.llvm.org
Tue May 13 04:04:24 PDT 2025
https://github.com/da-viper updated https://github.com/llvm/llvm-project/pull/138116
>From 559eb1a020e94c09ccfeda0e4cb5e16df145d4aa Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Tue, 29 Apr 2025 18:19:18 +0100
Subject: [PATCH 1/4] [lldb][lldb-dap] Migrate 'Scopes' to structured types.
---
lldb/tools/lldb-dap/DAP.cpp | 11 --
lldb/tools/lldb-dap/DAP.h | 2 -
lldb/tools/lldb-dap/Handler/RequestHandler.h | 10 +-
.../lldb-dap/Handler/ScopesRequestHandler.cpp | 121 ++++++++----------
lldb/tools/lldb-dap/JSONUtils.h | 21 ---
.../lldb-dap/Protocol/ProtocolRequests.cpp | 14 ++
.../lldb-dap/Protocol/ProtocolRequests.h | 13 ++
.../tools/lldb-dap/Protocol/ProtocolTypes.cpp | 71 ++++++++--
lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 81 +++++++++++-
9 files changed, 224 insertions(+), 120 deletions(-)
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 4feca1253be20..baaf9950b79db 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -559,17 +559,6 @@ lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
return GetLLDBFrame(frame_id);
}
-llvm::json::Value DAP::CreateTopLevelScopes() {
- llvm::json::Array scopes;
- scopes.emplace_back(
- CreateScope("Locals", VARREF_LOCALS, variables.locals.GetSize(), false));
- scopes.emplace_back(CreateScope("Globals", VARREF_GLOBALS,
- variables.globals.GetSize(), false));
- scopes.emplace_back(CreateScope("Registers", VARREF_REGS,
- variables.registers.GetSize(), false));
- return llvm::json::Value(std::move(scopes));
-}
-
ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression,
bool partial_expression) {
// Check for the escape hatch prefix.
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index c2e4c2dea582e..f66cb40451484 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -285,8 +285,6 @@ struct DAP {
lldb::SBFrame GetLLDBFrame(uint64_t frame_id);
lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments);
- llvm::json::Value CreateTopLevelScopes();
-
void PopulateExceptionBreakpoints();
/// Attempt to determine if an expression is a variable expression or
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index b0002440cf72e..eaebaf6619bbd 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -452,11 +452,15 @@ class PauseRequestHandler : public LegacyRequestHandler {
void operator()(const llvm::json::Object &request) const override;
};
-class ScopesRequestHandler : public LegacyRequestHandler {
+class ScopesRequestHandler final
+ : public RequestHandler<protocol::ScopesArguments,
+ llvm::Expected<protocol::ScopesResponseBody>> {
public:
- using LegacyRequestHandler::LegacyRequestHandler;
+ using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "scopes"; }
- void operator()(const llvm::json::Object &request) const override;
+
+ llvm::Expected<protocol::ScopesResponseBody>
+ Run(const protocol::ScopesArguments &args) const override;
};
class SetVariableRequestHandler final
diff --git a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
index 7d1608f59f9a4..d9dd29f7269f2 100644
--- a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
@@ -7,69 +7,55 @@
//===----------------------------------------------------------------------===//
#include "DAP.h"
-#include "EventHelper.h"
-#include "JSONUtils.h"
#include "RequestHandler.h"
+using namespace lldb_dap::protocol;
namespace lldb_dap {
-// "ScopesRequest": {
-// "allOf": [ { "$ref": "#/definitions/Request" }, {
-// "type": "object",
-// "description": "Scopes request; value of command field is 'scopes'. The
-// request returns the variable scopes for a given stackframe ID.",
-// "properties": {
-// "command": {
-// "type": "string",
-// "enum": [ "scopes" ]
-// },
-// "arguments": {
-// "$ref": "#/definitions/ScopesArguments"
-// }
-// },
-// "required": [ "command", "arguments" ]
-// }]
-// },
-// "ScopesArguments": {
-// "type": "object",
-// "description": "Arguments for 'scopes' request.",
-// "properties": {
-// "frameId": {
-// "type": "integer",
-// "description": "Retrieve the scopes for this stackframe."
-// }
-// },
-// "required": [ "frameId" ]
-// },
-// "ScopesResponse": {
-// "allOf": [ { "$ref": "#/definitions/Response" }, {
-// "type": "object",
-// "description": "Response to 'scopes' request.",
-// "properties": {
-// "body": {
-// "type": "object",
-// "properties": {
-// "scopes": {
-// "type": "array",
-// "items": {
-// "$ref": "#/definitions/Scope"
-// },
-// "description": "The scopes of the stackframe. If the array has
-// length zero, there are no scopes available."
-// }
-// },
-// "required": [ "scopes" ]
-// }
-// },
-// "required": [ "body" ]
-// }]
-// }
-void ScopesRequestHandler::operator()(const llvm::json::Object &request) const {
- llvm::json::Object response;
- FillResponse(request, response);
- llvm::json::Object body;
- const auto *arguments = request.getObject("arguments");
- lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
+/// 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 llvm::StringRef name, 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
+ // "locals" scope. add presentation hint;
+ // vscode only expands the first non-expensive scope, this causes friction
+ // as the locals scope will not be expanded. It becomes more annoying when
+ // the scope has arguments, return_value and locals.
+ if (variablesReference == VARREF_LOCALS)
+ scope.presentationHint = Scope::ePresentationHintLocals;
+ else if (variablesReference == VARREF_REGS)
+ scope.presentationHint = Scope::ePresentationHintRegisters;
+
+ 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);
+
// As the user selects different stack frames in the GUI, a "scopes" request
// will be sent to the DAP. This is the only way we know that the user has
// selected a frame in a thread. There are no other notifications that are
@@ -78,9 +64,9 @@ void ScopesRequestHandler::operator()(const llvm::json::Object &request) const {
// are sent, this allows users to type commands in the debugger console
// with a backtick character to run lldb commands and these lldb commands
// will now have the right context selected as they are run. If the user
- // types "`bt" into the debugger console and we had another thread selected
+ // types "`bt" into the debugger console, and we had another thread selected
// in the LLDB library, we would show the wrong thing to the user. If the
- // users switches threads with a lldb command like "`thread select 14", the
+ // users switch threads with a lldb command like "`thread select 14", the
// GUI will not update as there are no "event" notification packets that
// allow us to change the currently selected thread or frame in the GUI that
// I am aware of.
@@ -88,7 +74,6 @@ void ScopesRequestHandler::operator()(const llvm::json::Object &request) const {
frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
frame.GetThread().SetSelectedFrame(frame.GetFrameID());
}
-
dap.variables.locals = frame.GetVariables(/*arguments=*/true,
/*locals=*/true,
/*statics=*/false,
@@ -98,9 +83,15 @@ void ScopesRequestHandler::operator()(const llvm::json::Object &request) const {
/*statics=*/true,
/*in_scope_only=*/true);
dap.variables.registers = frame.GetRegisters();
- body.try_emplace("scopes", dap.CreateTopLevelScopes());
- response.try_emplace("body", std::move(body));
- dap.SendJSON(llvm::json::Value(std::move(response)));
+
+ 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)};
+
+ return ScopesResponseBody{std::move(scopes)};
}
} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index 9c4dd0584bd21..783f291338d8c 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -238,27 +238,6 @@ llvm::json::Object CreateEventObject(const llvm::StringRef event_name);
protocol::ExceptionBreakpointsFilter
CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp);
-/// Create a "Scope" JSON object as described in the debug adapter definition.
-///
-/// \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 "Scope" JSON object with that follows the formal JSON
-/// definition outlined by Microsoft.
-llvm::json::Value CreateScope(const llvm::StringRef name,
- int64_t variablesReference,
- int64_t namedVariables, bool expensive);
-
/// Create a "Source" JSON object as described in the debug adapter definition.
///
/// \param[in] file
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index 316e146d43a0f..7efab87d39986 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -335,6 +335,20 @@ llvm::json::Value toJSON(const SetVariableResponseBody &SVR) {
return llvm::json::Value(std::move(Body));
}
+bool fromJSON(const llvm::json::Value &Params, ScopesArguments &SCA,
+ llvm::json::Path P) {
+ json::ObjectMapper O(Params, P);
+ return O && O.map("frameId", SCA.frameId);
+}
+
+llvm::json::Value toJSON(const ScopesResponseBody &SCR) {
+ llvm::json::Array scopes;
+ for (const Scope &scope : SCR.scopes) {
+ scopes.emplace_back(toJSON(scope));
+ }
+
+ return llvm::json::Object{{"scopes", std::move(scopes)}};
+}
bool fromJSON(const json::Value &Params, SourceArguments &SA, json::Path P) {
json::ObjectMapper O(Params, P);
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index c6456b4113320..79739d8fc4309 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -439,6 +439,19 @@ struct SetVariableResponseBody {
};
llvm::json::Value toJSON(const SetVariableResponseBody &);
+struct ScopesArguments {
+ /// Retrieve the scopes for the stack frame identified by `frameId`. The
+ /// `frameId` must have been obtained in the current suspended state. See
+ /// 'Lifetime of Object References' in the Overview section for details.
+ uint64_t frameId;
+};
+bool fromJSON(const llvm::json::Value &, ScopesArguments &, llvm::json::Path);
+
+struct ScopesResponseBody {
+ std::vector<Scope> scopes;
+};
+llvm::json::Value toJSON(const ScopesResponseBody &);
+
/// Arguments for `source` request.
struct SourceArguments {
/// Specifies the source content to load. Either `source.path` or
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index c9cab350f9f12..74f8f749abe4c 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -16,17 +16,18 @@ using namespace llvm;
namespace lldb_dap::protocol {
-bool fromJSON(const json::Value &Params, PresentationHint &PH, json::Path P) {
+bool fromJSON(const json::Value &Params, Source::PresentationHint &PH,
+ json::Path P) {
auto rawHint = Params.getAsString();
if (!rawHint) {
P.report("expected a string");
return false;
}
- std::optional<PresentationHint> hint =
- StringSwitch<std::optional<PresentationHint>>(*rawHint)
- .Case("normal", ePresentationHintNormal)
- .Case("emphasize", ePresentationHintEmphasize)
- .Case("deemphasize", ePresentationHintDeemphasize)
+ std::optional<Source::PresentationHint> hint =
+ StringSwitch<std::optional<Source::PresentationHint>>(*rawHint)
+ .Case("normal", Source::ePresentationHintNormal)
+ .Case("emphasize", Source::ePresentationHintEmphasize)
+ .Case("deemphasize", Source::ePresentationHintDeemphasize)
.Default(std::nullopt);
if (!hint) {
P.report("unexpected value");
@@ -42,14 +43,13 @@ bool fromJSON(const json::Value &Params, Source &S, json::Path P) {
O.map("presentationHint", S.presentationHint) &&
O.map("sourceReference", S.sourceReference);
}
-
-llvm::json::Value toJSON(PresentationHint hint) {
+llvm::json::Value toJSON(Source::PresentationHint hint) {
switch (hint) {
- case ePresentationHintNormal:
+ case Source::ePresentationHintNormal:
return "normal";
- case ePresentationHintEmphasize:
+ case Source::ePresentationHintEmphasize:
return "emphasize";
- case ePresentationHintDeemphasize:
+ case Source::ePresentationHintDeemphasize:
return "deemphasize";
}
llvm_unreachable("unhandled presentation hint.");
@@ -269,6 +269,55 @@ json::Value toJSON(const Capabilities &C) {
return result;
}
+llvm::json::Value toJSON(const Scope &SC) {
+ llvm::json::Object result{{"name", SC.name},
+ {"variablesReference", SC.variablesReference},
+ {"expensive", SC.expensive}};
+
+ if (SC.presentationHint.has_value()) {
+ llvm::StringRef presentationHint;
+ switch (*SC.presentationHint) {
+ case Scope::ePresentationHintArguments:
+ presentationHint = "arguments";
+ break;
+ case Scope::ePresentationHintLocals:
+ presentationHint = "locals";
+ break;
+ case Scope::ePresentationHintRegisters:
+ presentationHint = "registers";
+ break;
+ case Scope::ePresentationHintReturnValue:
+ presentationHint = "returnValue";
+ break;
+ }
+
+ result.insert({"presentationHint", presentationHint});
+ }
+
+ if (SC.namedVariables.has_value())
+ result.insert({"namedVariables", SC.namedVariables});
+
+ if (SC.indexedVariables.has_value())
+ result.insert({"indexedVariables", SC.indexedVariables});
+
+ if (SC.source.has_value())
+ result.insert({"source", SC.source});
+
+ if (SC.line.has_value())
+ result.insert({"line", SC.line});
+
+ if (SC.column.has_value())
+ result.insert({"column", SC.column});
+
+ if (SC.endLine.has_value())
+ result.insert({"endLine", SC.endLine});
+
+ if (SC.endColumn.has_value())
+ result.insert({"endColumn", SC.endColumn});
+
+ return result;
+}
+
bool fromJSON(const llvm::json::Value &Params, SteppingGranularity &SG,
llvm::json::Path P) {
auto raw_granularity = Params.getAsString();
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index d1e86b0897675..d1070d37e3ba3 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -271,17 +271,16 @@ struct Capabilities {
};
llvm::json::Value toJSON(const Capabilities &);
-enum PresentationHint : unsigned {
- ePresentationHintNormal,
- ePresentationHintEmphasize,
- ePresentationHintDeemphasize,
-};
-llvm::json::Value toJSON(PresentationHint hint);
-
/// A `Source` is a descriptor for source code. It is returned from the debug
/// adapter as part of a `StackFrame` and it is used by clients when specifying
/// breakpoints.
struct Source {
+ enum PresentationHint : unsigned {
+ ePresentationHintNormal,
+ ePresentationHintEmphasize,
+ ePresentationHintDeemphasize,
+ };
+
/// The short name of the source. Every source returned from the debug adapter
/// has a name. When sending a source to the debug adapter this name is
/// optional.
@@ -305,9 +304,77 @@ struct Source {
// unsupported keys: origin, sources, adapterData, checksums
};
+llvm::json::Value toJSON(Source::PresentationHint);
bool fromJSON(const llvm::json::Value &, Source &, llvm::json::Path);
llvm::json::Value toJSON(const Source &);
+/// A `Scope` is a named container for variables. Optionally a scope can map to
+/// a source or a range within a source.
+struct Scope {
+ enum PresentationHint : unsigned {
+ ePresentationHintArguments,
+ ePresentationHintLocals,
+ ePresentationHintRegisters,
+ ePresentationHintReturnValue
+ };
+ /// Name of the scope such as 'Arguments', 'Locals', or 'Registers'. This
+ /// string is shown in the UI as is and can be translated.
+ ////
+ std::string name;
+
+ /// A hint for how to present this scope in the UI. If this attribute is
+ /// missing, the scope is shown with a generic UI.
+ /// Values:
+ /// 'arguments': Scope contains method arguments.
+ /// 'locals': Scope contains local variables.
+ /// 'registers': Scope contains registers. Only a single `registers` scope
+ /// should be returned from a `scopes` request.
+ /// 'returnValue': Scope contains one or more return values.
+ /// etc.
+ std::optional<PresentationHint> presentationHint;
+
+ /// The variables of this scope can be retrieved by passing the value of
+ /// `variablesReference` to the `variables` request as long as execution
+ /// remains suspended. See 'Lifetime of Object References' in the Overview
+ /// section for details.
+ ////
+ uint64_t variablesReference;
+
+ /// The number of named variables in this scope.
+ /// The client can use this information to present the variables in a paged UI
+ /// and fetch them in chunks.
+ std::optional<uint64_t> namedVariables;
+
+ /// The number of indexed variables in this scope.
+ /// The client can use this information to present the variables in a paged UI
+ /// and fetch them in chunks.
+ std::optional<uint64_t> indexedVariables;
+
+ /// The source for this scope.
+ std::optional<Source> source;
+
+ /// If true, the number of variables in this scope is large or expensive to
+ /// retrieve.
+ bool expensive;
+
+ /// The start line of the range covered by this scope.
+ std::optional<uint64_t> line;
+
+ /// Start position of the range covered by the scope. It is measured in UTF-16
+ /// code units and the client capability `columnsStartAt1` determines whether
+ /// it is 0- or 1-based.
+ std::optional<uint64_t> column;
+
+ /// The end line of the range covered by this scope.
+ std::optional<uint64_t> endLine;
+
+ /// End position of the range covered by the scope. It is measured in UTF-16
+ /// code units and the client capability `columnsStartAt1` determines whether
+ /// it is 0- or 1-based.
+ std::optional<uint64_t> endColumn;
+};
+llvm::json::Value toJSON(const Scope &);
+
/// The granularity of one `step` in the stepping requests `next`, `stepIn`,
/// `stepOut` and `stepBack`.
enum SteppingGranularity : unsigned {
>From 2165bfd9f949c50df123a7e0ac12efb5e31592e2 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Mon, 12 May 2025 20:33:33 +0100
Subject: [PATCH 2/4] [lldb][lldb-dap] review changes
---
lldb/tools/lldb-dap/Protocol/ProtocolRequests.h | 2 +-
lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 6 ++++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index 79739d8fc4309..f8d6b6a8581ea 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -443,7 +443,7 @@ struct ScopesArguments {
/// Retrieve the scopes for the stack frame identified by `frameId`. The
/// `frameId` must have been obtained in the current suspended state. See
/// 'Lifetime of Object References' in the Overview section for details.
- uint64_t frameId;
+ uint64_t frameId = LLDB_INVALID_FRAME_ID;
};
bool fromJSON(const llvm::json::Value &, ScopesArguments &, llvm::json::Path);
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index d1070d37e3ba3..668c3d7682f3f 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -27,6 +27,8 @@
#include <optional>
#include <string>
+#define LLDB_DAP_INVALID_VARRERF UINT64_MAX
+
namespace lldb_dap::protocol {
/// An `ExceptionBreakpointsFilter` is shown in the UI as an filter option for
@@ -338,7 +340,7 @@ struct Scope {
/// remains suspended. See 'Lifetime of Object References' in the Overview
/// section for details.
////
- uint64_t variablesReference;
+ uint64_t variablesReference = LLDB_DAP_INVALID_VARRERF;
/// The number of named variables in this scope.
/// The client can use this information to present the variables in a paged UI
@@ -355,7 +357,7 @@ struct Scope {
/// If true, the number of variables in this scope is large or expensive to
/// retrieve.
- bool expensive;
+ bool expensive = false;
/// The start line of the range covered by this scope.
std::optional<uint64_t> line;
>From c3bffb542617bef10bb2cc814af9d8ec47ff12be Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Mon, 12 May 2025 20:36:21 +0100
Subject: [PATCH 3/4] [lldb][lldb-dap] review changes
---
lldb/tools/lldb-dap/DAP.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index f66cb40451484..9065995f5d722 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -283,6 +283,8 @@ struct DAP {
lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments);
lldb::SBFrame GetLLDBFrame(uint64_t frame_id);
+ /// TODO: remove this function when we finish migrating to the
+ /// new protocol types.
lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments);
void PopulateExceptionBreakpoints();
>From 6fcf3c0089beccecd148676a0f3444d33f95d7c8 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Tue, 13 May 2025 12:03:02 +0100
Subject: [PATCH 4/4] [lldb][lldb-dap] add review changes
---
.../lldb-dap/Handler/ScopesRequestHandler.cpp | 4 +-
.../tools/lldb-dap/Protocol/ProtocolTypes.cpp | 56 +++++++++++++++----
lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 17 +++---
lldb/unittests/DAP/ProtocolTypesTest.cpp | 43 +++++++++++++-
4 files changed, 100 insertions(+), 20 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
index d9dd29f7269f2..e8ae25469d82f 100644
--- a/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp
@@ -41,9 +41,9 @@ static Scope CreateScope(const llvm::StringRef name, int64_t variablesReference,
// as the locals scope will not be expanded. It becomes more annoying when
// the scope has arguments, return_value and locals.
if (variablesReference == VARREF_LOCALS)
- scope.presentationHint = Scope::ePresentationHintLocals;
+ scope.presentationHint = Scope::eScopePresentationHintLocals;
else if (variablesReference == VARREF_REGS)
- scope.presentationHint = Scope::ePresentationHintRegisters;
+ scope.presentationHint = Scope::eScopePresentationHintRegisters;
scope.variablesReference = variablesReference;
scope.namedVariables = namedVariables;
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index 74f8f749abe4c..09d383311a242 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -25,9 +25,9 @@ bool fromJSON(const json::Value &Params, Source::PresentationHint &PH,
}
std::optional<Source::PresentationHint> hint =
StringSwitch<std::optional<Source::PresentationHint>>(*rawHint)
- .Case("normal", Source::ePresentationHintNormal)
- .Case("emphasize", Source::ePresentationHintEmphasize)
- .Case("deemphasize", Source::ePresentationHintDeemphasize)
+ .Case("normal", Source::eSourcePresentationHintNormal)
+ .Case("emphasize", Source::eSourcePresentationHintEmphasize)
+ .Case("deemphasize", Source::eSourcePresentationHintDeemphasize)
.Default(std::nullopt);
if (!hint) {
P.report("unexpected value");
@@ -43,13 +43,14 @@ bool fromJSON(const json::Value &Params, Source &S, json::Path P) {
O.map("presentationHint", S.presentationHint) &&
O.map("sourceReference", S.sourceReference);
}
+
llvm::json::Value toJSON(Source::PresentationHint hint) {
switch (hint) {
- case Source::ePresentationHintNormal:
+ case Source::eSourcePresentationHintNormal:
return "normal";
- case Source::ePresentationHintEmphasize:
+ case Source::eSourcePresentationHintEmphasize:
return "emphasize";
- case Source::ePresentationHintDeemphasize:
+ case Source::eSourcePresentationHintDeemphasize:
return "deemphasize";
}
llvm_unreachable("unhandled presentation hint.");
@@ -269,6 +270,41 @@ json::Value toJSON(const Capabilities &C) {
return result;
}
+bool fromJSON(const json::Value &Params, Scope::PresentationHint &PH,
+ json::Path P) {
+ auto rawHint = Params.getAsString();
+ if (!rawHint) {
+ P.report("expected a string");
+ return false;
+ }
+ const std::optional<Scope::PresentationHint> hint =
+ StringSwitch<std::optional<Scope::PresentationHint>>(*rawHint)
+ .Case("arguments", Scope::eScopePresentationHintArguments)
+ .Case("locals", Scope::eScopePresentationHintLocals)
+ .Case("registers", Scope::eScopePresentationHintRegisters)
+ .Case("returnValue", Scope::eScopePresentationHintReturnValue)
+ .Default(std::nullopt);
+ if (!hint) {
+ P.report("unexpected value");
+ return false;
+ }
+ PH = *hint;
+ return true;
+}
+
+bool fromJSON(const json::Value &Params, Scope &S, json::Path P) {
+ json::ObjectMapper O(Params, P);
+ return O && O.map("name", S.name) &&
+ O.mapOptional("presentationHint", S.presentationHint) &&
+ O.map("variablesReference", S.variablesReference) &&
+ O.mapOptional("namedVariables", S.namedVariables) &&
+ O.map("indexedVariables", S.indexedVariables) &&
+ O.mapOptional("source", S.source) && O.map("expensive", S.expensive) &&
+ O.mapOptional("line", S.line) && O.mapOptional("column", S.column) &&
+ O.mapOptional("endLine", S.endLine) &&
+ O.mapOptional("endColumn", S.endColumn);
+}
+
llvm::json::Value toJSON(const Scope &SC) {
llvm::json::Object result{{"name", SC.name},
{"variablesReference", SC.variablesReference},
@@ -277,16 +313,16 @@ llvm::json::Value toJSON(const Scope &SC) {
if (SC.presentationHint.has_value()) {
llvm::StringRef presentationHint;
switch (*SC.presentationHint) {
- case Scope::ePresentationHintArguments:
+ case Scope::eScopePresentationHintArguments:
presentationHint = "arguments";
break;
- case Scope::ePresentationHintLocals:
+ case Scope::eScopePresentationHintLocals:
presentationHint = "locals";
break;
- case Scope::ePresentationHintRegisters:
+ case Scope::eScopePresentationHintRegisters:
presentationHint = "registers";
break;
- case Scope::ePresentationHintReturnValue:
+ case Scope::eScopePresentationHintReturnValue:
presentationHint = "returnValue";
break;
}
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index 668c3d7682f3f..b42c2699b6248 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -278,9 +278,9 @@ llvm::json::Value toJSON(const Capabilities &);
/// breakpoints.
struct Source {
enum PresentationHint : unsigned {
- ePresentationHintNormal,
- ePresentationHintEmphasize,
- ePresentationHintDeemphasize,
+ eSourcePresentationHintNormal,
+ eSourcePresentationHintEmphasize,
+ eSourcePresentationHintDeemphasize,
};
/// The short name of the source. Every source returned from the debug adapter
@@ -314,10 +314,10 @@ llvm::json::Value toJSON(const Source &);
/// a source or a range within a source.
struct Scope {
enum PresentationHint : unsigned {
- ePresentationHintArguments,
- ePresentationHintLocals,
- ePresentationHintRegisters,
- ePresentationHintReturnValue
+ eScopePresentationHintArguments,
+ eScopePresentationHintLocals,
+ eScopePresentationHintRegisters,
+ eScopePresentationHintReturnValue
};
/// Name of the scope such as 'Arguments', 'Locals', or 'Registers'. This
/// string is shown in the UI as is and can be translated.
@@ -375,6 +375,9 @@ struct Scope {
/// it is 0- or 1-based.
std::optional<uint64_t> endColumn;
};
+bool fromJSON(const llvm::json::Value &Params, Scope::PresentationHint &PH,
+ llvm::json::Path);
+bool fromJSON(const llvm::json::Value &, Scope &, llvm::json::Path);
llvm::json::Value toJSON(const Scope &);
/// The granularity of one `step` in the stepping requests `next`, `stepIn`,
diff --git a/lldb/unittests/DAP/ProtocolTypesTest.cpp b/lldb/unittests/DAP/ProtocolTypesTest.cpp
index fa46816ca4a10..f330beef6e2a5 100644
--- a/lldb/unittests/DAP/ProtocolTypesTest.cpp
+++ b/lldb/unittests/DAP/ProtocolTypesTest.cpp
@@ -50,7 +50,7 @@ TEST(ProtocolTypesTest, Source) {
source.name = "testName";
source.path = "/path/to/source";
source.sourceReference = 12345;
- source.presentationHint = ePresentationHintEmphasize;
+ source.presentationHint = Source::eSourcePresentationHintEmphasize;
llvm::Expected<Source> deserialized_source = roundtrip(source);
ASSERT_THAT_EXPECTED(deserialized_source, llvm::Succeeded());
@@ -60,3 +60,44 @@ TEST(ProtocolTypesTest, Source) {
EXPECT_EQ(source.sourceReference, deserialized_source->sourceReference);
EXPECT_EQ(source.presentationHint, deserialized_source->presentationHint);
}
+
+TEST(ProtocolTypesTest, Scope) {
+ Scope scope;
+ scope.name = "Locals";
+ scope.presentationHint = Scope::eScopePresentationHintLocals;
+ scope.variablesReference = 1;
+ scope.namedVariables = 2;
+ scope.indexedVariables = std::nullopt;
+ scope.expensive = false;
+ scope.line = 2;
+ scope.column = 3;
+ scope.endLine = 10;
+ scope.endColumn = 20;
+
+ scope.source =
+ Source{.name = "testName",
+ .path = "/path/to/source",
+ .sourceReference = 12345,
+ .presentationHint = Source::eSourcePresentationHintNormal};
+
+ llvm::Expected<Scope> deserialized_scope = roundtrip(scope);
+ ASSERT_THAT_EXPECTED(deserialized_scope, llvm::Succeeded());
+ EXPECT_EQ(scope.name, deserialized_scope->name);
+ EXPECT_EQ(scope.presentationHint, deserialized_scope->presentationHint);
+ EXPECT_EQ(scope.variablesReference, deserialized_scope->variablesReference);
+ EXPECT_EQ(scope.namedVariables, deserialized_scope->namedVariables);
+ EXPECT_EQ(scope.indexedVariables, deserialized_scope->indexedVariables);
+ EXPECT_EQ(scope.expensive, deserialized_scope->expensive);
+ EXPECT_EQ(scope.line, deserialized_scope->line);
+ EXPECT_EQ(scope.column, deserialized_scope->column);
+ EXPECT_EQ(scope.endLine, deserialized_scope->endLine);
+ EXPECT_EQ(scope.endColumn, deserialized_scope->endColumn);
+
+ EXPECT_THAT(deserialized_scope->source.has_value(), true);
+ const Source &source = scope.source.value();
+ const Source &deserialized_source = deserialized_scope->source.value();
+
+ EXPECT_EQ(source.path, deserialized_source.path);
+ EXPECT_EQ(source.sourceReference, deserialized_source.sourceReference);
+ EXPECT_EQ(source.presentationHint, deserialized_source.presentationHint);
+}
More information about the lldb-commits
mailing list