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 <yerimy...@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 <yerimy...@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 <yerimy...@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 <yerimy...@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); +} _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits