[Lldb-commits] [lldb] fbb8929 - [lldb-dap] Updating RequestHandler to encode/decode arguments and response. (#130090)

via lldb-commits lldb-commits at lists.llvm.org
Mon Mar 17 10:13:15 PDT 2025


Author: John Harrison
Date: 2025-03-17T10:13:11-07:00
New Revision: fbb8929c9d15fdc0001205ee4a7b42a78edc5213

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

LOG: [lldb-dap] Updating RequestHandler to encode/decode arguments and response. (#130090)

This is a work in progress refactor to add explicit types instead of
generic 'llvm::json::Value' types to the DAP protocol.

This updates RequestHandler to have take the type of the arguments and
response body for serialization for requests.

The 'source' and 'disconnect' request is updated to show how the new
flow
works and includes serialization handling for optional arguments and
'void'
responses.

This is built on top of #130026

---------

Co-authored-by: Adrian Vogelsgesang <adrian.vogelsgesang at tum.de>

Added: 
    lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
    lldb/tools/lldb-dap/Protocol/ProtocolBase.h
    lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
    lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
    lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
    lldb/tools/lldb-dap/Protocol/ProtocolTypes.h

Modified: 
    lldb/tools/lldb-dap/CMakeLists.txt
    lldb/tools/lldb-dap/DAP.cpp
    lldb/tools/lldb-dap/DAP.h
    lldb/tools/lldb-dap/DAPForward.h
    lldb/tools/lldb-dap/Handler/DisconnectRequestHandler.cpp
    lldb/tools/lldb-dap/Handler/RequestHandler.cpp
    lldb/tools/lldb-dap/Handler/RequestHandler.h
    lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
    lldb/tools/lldb-dap/LLDBUtils.cpp
    lldb/tools/lldb-dap/LLDBUtils.h
    lldb/tools/lldb-dap/Transport.cpp
    lldb/tools/lldb-dap/Transport.h
    lldb/tools/lldb-dap/lldb-dap.cpp

Removed: 
    lldb/tools/lldb-dap/Protocol.cpp
    lldb/tools/lldb-dap/Protocol.h


################################################################################
diff  --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 8a76cb58dbcab..adad75a79fa7a 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -32,7 +32,6 @@ add_lldb_tool(lldb-dap
   LLDBUtils.cpp
   OutputRedirector.cpp
   ProgressEvent.cpp
-  Protocol.cpp
   RunInTerminal.cpp
   SourceBreakpoint.cpp
   Transport.cpp
@@ -74,6 +73,10 @@ add_lldb_tool(lldb-dap
   Handler/TestGetTargetBreakpointsRequestHandler.cpp
   Handler/ThreadsRequestHandler.cpp
   Handler/VariablesRequestHandler.cpp
+  
+  Protocol/ProtocolBase.cpp
+  Protocol/ProtocolTypes.cpp
+  Protocol/ProtocolRequests.cpp
 
   LINK_LIBS
     liblldb

diff  --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 2a8fe8e24c242..a1e2187288768 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -8,10 +8,12 @@
 
 #include "DAP.h"
 #include "DAPLog.h"
+#include "Handler/RequestHandler.h"
 #include "Handler/ResponseHandler.h"
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
 #include "OutputRedirector.h"
+#include "Protocol/ProtocolBase.h"
 #include "Transport.h"
 #include "lldb/API/SBBreakpoint.h"
 #include "lldb/API/SBCommandInterpreter.h"
@@ -25,6 +27,7 @@
 #include "lldb/lldb-defines.h"
 #include "lldb/lldb-enumerations.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
@@ -41,6 +44,7 @@
 #include <fstream>
 #include <memory>
 #include <mutex>
+#include <string>
 #include <utility>
 
 #if defined(_WIN32)
@@ -233,6 +237,10 @@ void DAP::SendJSON(const llvm::json::Value &json) {
                   transport.GetClientName());
     return;
   }
+  Send(message);
+}
+
+void DAP::Send(const protocol::Message &message) {
   if (llvm::Error err = transport.Write(message))
     DAP_LOG_ERROR(log, std::move(err), "({1}) write failed: {0}",
                   transport.GetClientName());
@@ -665,31 +673,23 @@ void DAP::SetTarget(const lldb::SBTarget target) {
 }
 
 bool DAP::HandleObject(const protocol::Message &M) {
-  // FIXME: Directly handle `Message` instead of serializing to JSON.
-  llvm::json::Value v = toJSON(M);
-  llvm::json::Object object = *v.getAsObject();
-  const auto packet_type = GetString(object, "type");
-  if (packet_type == "request") {
-    const auto command = GetString(object, "command").value_or("");
-
-    auto new_handler_pos = request_handlers.find(command);
-    if (new_handler_pos != request_handlers.end()) {
-      (*new_handler_pos->second)(object);
+  if (const auto *req = std::get_if<protocol::Request>(&M)) {
+    auto handler_pos = request_handlers.find(req->command);
+    if (handler_pos != request_handlers.end()) {
+      (*handler_pos->second)(*req);
       return true; // Success
     }
 
     DAP_LOG(log, "({0}) error: unhandled command '{1}'",
-            transport.GetClientName(), command);
+            transport.GetClientName(), req->command);
     return false; // Fail
   }
 
-  if (packet_type == "response") {
-    auto id = GetInteger<int64_t>(object, "request_seq").value_or(0);
-
+  if (const auto *resp = std::get_if<protocol::Response>(&M)) {
     std::unique_ptr<ResponseHandler> response_handler;
     {
       std::lock_guard<std::mutex> locker(call_mutex);
-      auto inflight = inflight_reverse_requests.find(id);
+      auto inflight = inflight_reverse_requests.find(resp->request_seq);
       if (inflight != inflight_reverse_requests.end()) {
         response_handler = std::move(inflight->second);
         inflight_reverse_requests.erase(inflight);
@@ -697,19 +697,32 @@ bool DAP::HandleObject(const protocol::Message &M) {
     }
 
     if (!response_handler)
-      response_handler = std::make_unique<UnknownResponseHandler>("", id);
+      response_handler =
+          std::make_unique<UnknownResponseHandler>("", resp->request_seq);
 
     // Result should be given, use null if not.
-    if (GetBoolean(object, "success").value_or(false)) {
-      llvm::json::Value Result = nullptr;
-      if (auto *B = object.get("body"))
-        Result = std::move(*B);
-      (*response_handler)(Result);
+    if (resp->success) {
+      (*response_handler)(resp->body);
     } else {
-      llvm::StringRef message = GetString(object, "message").value_or("");
-      if (message.empty()) {
-        message = "Unknown error, response failed";
+      llvm::StringRef message = "Unknown error, response failed";
+      if (resp->message) {
+        message =
+            std::visit(llvm::makeVisitor(
+                           [](const std::string &message) -> llvm::StringRef {
+                             return message;
+                           },
+                           [](const protocol::Response::Message &message)
+                               -> llvm::StringRef {
+                             switch (message) {
+                             case protocol::Response::Message::cancelled:
+                               return "cancelled";
+                             case protocol::Response::Message::notStopped:
+                               return "notStopped";
+                             }
+                           }),
+                       *resp->message);
       }
+
       (*response_handler)(llvm::createStringError(
           std::error_code(-1, std::generic_category()), message));
     }
@@ -717,6 +730,8 @@ bool DAP::HandleObject(const protocol::Message &M) {
     return true;
   }
 
+  DAP_LOG(log, "Unsupported protocol message");
+
   return false;
 }
 
@@ -730,9 +745,9 @@ void DAP::SendTerminatedEvent() {
   });
 }
 
-lldb::SBError DAP::Disconnect() { return Disconnect(is_attach); }
+llvm::Error DAP::Disconnect() { return Disconnect(is_attach); }
 
-lldb::SBError DAP::Disconnect(bool terminateDebuggee) {
+llvm::Error DAP::Disconnect(bool terminateDebuggee) {
   lldb::SBError error;
   lldb::SBProcess process = target.GetProcess();
   auto state = process.GetState();
@@ -760,7 +775,7 @@ lldb::SBError DAP::Disconnect(bool terminateDebuggee) {
 
   disconnecting = true;
 
-  return error;
+  return ToError(error);
 }
 
 llvm::Error DAP::Loop() {

diff  --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index db3473b7c7027..4c57f9fef3d89 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -12,12 +12,10 @@
 #include "DAPForward.h"
 #include "ExceptionBreakpoint.h"
 #include "FunctionBreakpoint.h"
-#include "Handler/RequestHandler.h"
-#include "Handler/ResponseHandler.h"
 #include "InstructionBreakpoint.h"
 #include "OutputRedirector.h"
 #include "ProgressEvent.h"
-#include "Protocol.h"
+#include "Protocol/ProtocolBase.h"
 #include "SourceBreakpoint.h"
 #include "Transport.h"
 #include "lldb/API/SBBroadcaster.h"
@@ -187,7 +185,7 @@ struct DAP {
   // the old process here so we can detect this case and keep running.
   lldb::pid_t restarting_process_id;
   bool configuration_done_sent;
-  llvm::StringMap<std::unique_ptr<RequestHandler>> request_handlers;
+  llvm::StringMap<std::unique_ptr<BaseRequestHandler>> request_handlers;
   bool waiting_for_run_in_terminal;
   ProgressEventReporter progress_event_reporter;
   // Keep track of the last stop thread index IDs as threads won't go away
@@ -245,9 +243,11 @@ struct DAP {
   /// Stop event handler threads.
   void StopEventHandlers();
 
-  // Serialize the JSON value into a string and send the JSON packet to
-  // the "out" stream.
+  /// Serialize the JSON value into a string and send the JSON packet to the
+  /// "out" stream.
   void SendJSON(const llvm::json::Value &json);
+  /// Send the given message to the client
+  void Send(const protocol::Message &message);
 
   void SendOutput(OutputType o, const llvm::StringRef output);
 
@@ -324,10 +324,10 @@ struct DAP {
   bool HandleObject(const protocol::Message &M);
 
   /// Disconnect the DAP session.
-  lldb::SBError Disconnect();
+  llvm::Error Disconnect();
 
   /// Disconnect the DAP session and optionally terminate the debuggee.
-  lldb::SBError Disconnect(bool terminateDebuggee);
+  llvm::Error Disconnect(bool terminateDebuggee);
 
   /// Send a "terminated" event to indicate the process is done being debugged.
   void SendTerminatedEvent();

diff  --git a/lldb/tools/lldb-dap/DAPForward.h b/lldb/tools/lldb-dap/DAPForward.h
index 0196d83dcd6a9..667aef23abd0f 100644
--- a/lldb/tools/lldb-dap/DAPForward.h
+++ b/lldb/tools/lldb-dap/DAPForward.h
@@ -19,6 +19,8 @@ struct SourceBreakpoint;
 struct Watchpoint;
 struct InstructionBreakpoint;
 struct DAP;
+class BaseRequestHandler;
+class ResponseHandler;
 } // namespace lldb_dap
 
 namespace lldb {

diff  --git a/lldb/tools/lldb-dap/Handler/DisconnectRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DisconnectRequestHandler.cpp
index b8f3404874e91..f12fecfb2ff65 100644
--- a/lldb/tools/lldb-dap/Handler/DisconnectRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/DisconnectRequestHandler.cpp
@@ -7,70 +7,27 @@
 //===----------------------------------------------------------------------===//
 
 #include "DAP.h"
-#include "EventHelper.h"
-#include "JSONUtils.h"
+#include "Protocol/ProtocolRequests.h"
 #include "RequestHandler.h"
+#include "llvm/Support/Error.h"
+#include <optional>
+
+using namespace llvm;
+using namespace lldb_dap::protocol;
 
 namespace lldb_dap {
 
-// "DisconnectRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Disconnect request; value of command field is
-//                     'disconnect'.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "disconnect" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/DisconnectArguments"
-//       }
-//     },
-//     "required": [ "command" ]
-//   }]
-// },
-// "DisconnectArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'disconnect' request.",
-//   "properties": {
-//     "terminateDebuggee": {
-//       "type": "boolean",
-//       "description": "Indicates whether the debuggee should be terminated
-//                       when the debugger is disconnected. If unspecified,
-//                       the debug adapter is free to do whatever it thinks
-//                       is best. A client can only rely on this attribute
-//                       being properly honored if a debug adapter returns
-//                       true for the 'supportTerminateDebuggee' capability."
-//     },
-//     "restart": {
-//       "type": "boolean",
-//       "description": "Indicates whether the debuggee should be restart
-//                       the process."
-//     }
-//   }
-// },
-// "DisconnectResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'disconnect' request. This is just an
-//                     acknowledgement, so no body field is required."
-//   }]
-// }
-void DisconnectRequestHandler::operator()(
-    const llvm::json::Object &request) const {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
+/// Disconnect request; value of command field is 'disconnect'.
+Expected<DisconnectResponse> DisconnectRequestHandler::Run(
+    const std::optional<DisconnectArguments> &arguments) const {
+  bool terminateDebuggee = dap.is_attach ? false : true;
 
-  bool defaultTerminateDebuggee = dap.is_attach ? false : true;
-  bool terminateDebuggee = GetBoolean(arguments, "terminateDebuggee")
-                               .value_or(defaultTerminateDebuggee);
+  if (arguments && arguments->terminateDebuggee)
+    terminateDebuggee = *arguments->terminateDebuggee;
 
-  lldb::SBError error = dap.Disconnect(terminateDebuggee);
-  if (error.Fail())
-    EmplaceSafeString(response, "error", error.GetCString());
+  if (Error error = dap.Disconnect(terminateDebuggee))
+    return error;
 
-  dap.SendJSON(llvm::json::Value(std::move(response)));
+  return DisconnectResponse();
 }
 } // namespace lldb_dap

diff  --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index 2e661329edea1..60c82649938d6 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -6,8 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "RequestHandler.h"
+#include "Handler/RequestHandler.h"
 #include "DAP.h"
+#include "Handler/ResponseHandler.h"
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
 #include "RunInTerminal.h"
@@ -45,7 +46,7 @@ static uint32_t SetLaunchFlag(uint32_t flags, const llvm::json::Object *obj,
 
 // Both attach and launch take either a sourcePath or a sourceMap
 // argument (or neither), from which we need to set the target.source-map.
-void RequestHandler::SetSourceMapFromArguments(
+void BaseRequestHandler::SetSourceMapFromArguments(
     const llvm::json::Object &arguments) const {
   const char *sourceMapHelp =
       "source must be be an array of two-element arrays, "
@@ -159,7 +160,7 @@ static llvm::Error RunInTerminal(DAP &dap,
 }
 
 lldb::SBError
-RequestHandler::LaunchProcess(const llvm::json::Object &request) const {
+BaseRequestHandler::LaunchProcess(const llvm::json::Object &request) const {
   lldb::SBError error;
   const auto *arguments = request.getObject("arguments");
   auto launchCommands = GetStrings(arguments, "launchCommands");
@@ -228,13 +229,13 @@ RequestHandler::LaunchProcess(const llvm::json::Object &request) const {
   return error;
 }
 
-void RequestHandler::PrintWelcomeMessage() const {
+void BaseRequestHandler::PrintWelcomeMessage() const {
 #ifdef LLDB_DAP_WELCOME_MESSAGE
   dap.SendOutput(OutputType::Console, LLDB_DAP_WELCOME_MESSAGE);
 #endif
 }
 
-bool RequestHandler::HasInstructionGranularity(
+bool BaseRequestHandler::HasInstructionGranularity(
     const llvm::json::Object &arguments) const {
   if (std::optional<llvm::StringRef> value = arguments.getString("granularity"))
     return value == "instruction";

diff  --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index b44367518bcb9..c9bcf15933c33 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -9,26 +9,41 @@
 #ifndef LLDB_TOOLS_LLDB_DAP_HANDLER_HANDLER_H
 #define LLDB_TOOLS_LLDB_DAP_HANDLER_HANDLER_H
 
+#include "DAP.h"
+#include "DAPLog.h"
+#include "Protocol/ProtocolBase.h"
+#include "Protocol/ProtocolRequests.h"
 #include "lldb/API/SBError.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/JSON.h"
+#include <optional>
+#include <type_traits>
+
+template <typename T> struct is_optional : std::false_type {};
+
+template <typename T> struct is_optional<std::optional<T>> : std::true_type {};
+
+template <typename T>
+inline constexpr bool is_optional_v = is_optional<T>::value;
 
 namespace lldb_dap {
 struct DAP;
 
-class RequestHandler {
+/// Base class for request handlers. Do not extend this directly: Extend
+/// the RequestHandler template subclass instead.
+class BaseRequestHandler {
 public:
-  RequestHandler(DAP &dap) : dap(dap) {}
+  BaseRequestHandler(DAP &dap) : dap(dap) {}
 
-  /// RequestHandler are not copyable.
+  /// BaseRequestHandler are not copyable.
   /// @{
-  RequestHandler(const RequestHandler &) = delete;
-  RequestHandler &operator=(const RequestHandler &) = delete;
+  BaseRequestHandler(const BaseRequestHandler &) = delete;
+  BaseRequestHandler &operator=(const BaseRequestHandler &) = delete;
   /// @}
 
-  virtual ~RequestHandler() = default;
+  virtual ~BaseRequestHandler() = default;
 
-  virtual void operator()(const llvm::json::Object &request) const = 0;
+  virtual void operator()(const protocol::Request &request) const = 0;
 
 protected:
   /// Helpers used by multiple request handlers.
@@ -57,235 +72,311 @@ class RequestHandler {
   DAP &dap;
 };
 
-class AttachRequestHandler : public RequestHandler {
-public:
-  using RequestHandler::RequestHandler;
+/// FIXME: Migrate callers to typed RequestHandler for improved type handling.
+class LegacyRequestHandler : public BaseRequestHandler {
+  using BaseRequestHandler::BaseRequestHandler;
+  virtual void operator()(const llvm::json::Object &request) const = 0;
+  void operator()(const protocol::Request &request) const override {
+    auto req = toJSON(request);
+    (*this)(*req.getAsObject());
+  }
+};
+
+/// Base class for handling DAP requests. Handlers should declare their
+/// arguments and response body types like:
+///
+/// class MyRequestHandler : public RequestHandler<Arguments, ResponseBody> {
+///   ....
+/// };
+template <typename Args, typename Body>
+class RequestHandler : public BaseRequestHandler {
+  using BaseRequestHandler::BaseRequestHandler;
+
+  void operator()(const protocol::Request &request) const override {
+    protocol::Response response;
+    response.request_seq = request.seq;
+    response.command = request.command;
+
+    if (!is_optional_v<Args> && !request.arguments) {
+      DAP_LOG(dap.log,
+              "({0}) malformed request {1}, expected arguments but got none",
+              dap.transport.GetClientName(), request.command);
+      response.success = false;
+      response.message = llvm::formatv("arguments required for command '{0}' "
+                                       "but none received",
+                                       request.command)
+                             .str();
+      dap.Send(response);
+      return;
+    }
+
+    Args arguments;
+    llvm::json::Path::Root root;
+    if (request.arguments && !fromJSON(request.arguments, arguments, root)) {
+      std::string parse_failure;
+      llvm::raw_string_ostream OS(parse_failure);
+      root.printErrorContext(request.arguments, OS);
+      response.success = false;
+      response.message = parse_failure;
+      dap.Send(response);
+      return;
+    }
+
+    auto body = Run(arguments);
+    // FIXME: Add a dedicated DAPError for enhanced errors that are
+    // user-visibile.
+    if (auto Err = body.takeError()) {
+      response.success = false;
+      // FIXME: Build ErrorMessage based on error details instead of using the
+      // 'message' field.
+      response.message = llvm::toString(std::move(Err));
+    } else {
+      response.success = true;
+      if constexpr (!std::is_same_v<Body, std::monostate>)
+        response.body = std::move(*body);
+    }
+
+    dap.Send(response);
+  };
+
+  virtual llvm::Expected<Body> Run(const Args &) const = 0;
+};
+
+class AttachRequestHandler : public LegacyRequestHandler {
+public:
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "attach"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class BreakpointLocationsRequestHandler : public RequestHandler {
+class BreakpointLocationsRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "breakpointLocations"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class CompletionsRequestHandler : public RequestHandler {
+class CompletionsRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "completions"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ContinueRequestHandler : public RequestHandler {
+class ContinueRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "continue"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ConfigurationDoneRequestHandler : public RequestHandler {
+class ConfigurationDoneRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "configurationDone"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class DisconnectRequestHandler : public RequestHandler {
+class DisconnectRequestHandler
+    : public RequestHandler<std::optional<protocol::DisconnectArguments>,
+                            protocol::DisconnectResponse> {
 public:
   using RequestHandler::RequestHandler;
   static llvm::StringLiteral getCommand() { return "disconnect"; }
-  void operator()(const llvm::json::Object &request) const override;
+  llvm::Expected<protocol::DisconnectResponse>
+  Run(const std::optional<protocol::DisconnectArguments> &args) const override;
 };
 
-class EvaluateRequestHandler : public RequestHandler {
+class EvaluateRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "evaluate"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ExceptionInfoRequestHandler : public RequestHandler {
+class ExceptionInfoRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "exceptionInfo"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class InitializeRequestHandler : public RequestHandler {
+class InitializeRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "initialize"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class LaunchRequestHandler : public RequestHandler {
+class LaunchRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "launch"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class RestartRequestHandler : public RequestHandler {
+class RestartRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "restart"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class NextRequestHandler : public RequestHandler {
+class NextRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "next"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class StepInRequestHandler : public RequestHandler {
+class StepInRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "stepIn"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class StepInTargetsRequestHandler : public RequestHandler {
+class StepInTargetsRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "stepInTargets"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class StepOutRequestHandler : public RequestHandler {
+class StepOutRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "stepOut"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SetBreakpointsRequestHandler : public RequestHandler {
+class SetBreakpointsRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "setBreakpoints"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SetExceptionBreakpointsRequestHandler : public RequestHandler {
+class SetExceptionBreakpointsRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "setExceptionBreakpoints"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SetFunctionBreakpointsRequestHandler : public RequestHandler {
+class SetFunctionBreakpointsRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "setFunctionBreakpoints"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class DataBreakpointInfoRequestHandler : public RequestHandler {
+class DataBreakpointInfoRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "dataBreakpointInfo"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SetDataBreakpointsRequestHandler : public RequestHandler {
+class SetDataBreakpointsRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "setDataBreakpoints"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SetInstructionBreakpointsRequestHandler : public RequestHandler {
+class SetInstructionBreakpointsRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() {
     return "setInstructionBreakpoints";
   }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class CompileUnitsRequestHandler : public RequestHandler {
+class CompileUnitsRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "compileUnits"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ModulesRequestHandler : public RequestHandler {
+class ModulesRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "modules"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class PauseRequestHandler : public RequestHandler {
+class PauseRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "pause"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ScopesRequestHandler : public RequestHandler {
+class ScopesRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "scopes"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SetVariableRequestHandler : public RequestHandler {
+class SetVariableRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "setVariable"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class SourceRequestHandler : public RequestHandler {
+class SourceRequestHandler
+    : public RequestHandler<protocol::SourceArguments,
+                            protocol::SourceResponseBody> {
 public:
   using RequestHandler::RequestHandler;
   static llvm::StringLiteral getCommand() { return "source"; }
-  void operator()(const llvm::json::Object &request) const override;
+  llvm::Expected<protocol::SourceResponseBody>
+  Run(const protocol::SourceArguments &args) const override;
 };
 
-class StackTraceRequestHandler : public RequestHandler {
+class StackTraceRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "stackTrace"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ThreadsRequestHandler : public RequestHandler {
+class ThreadsRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "threads"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class VariablesRequestHandler : public RequestHandler {
+class VariablesRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "variables"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class LocationsRequestHandler : public RequestHandler {
+class LocationsRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "locations"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class DisassembleRequestHandler : public RequestHandler {
+class DisassembleRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "disassemble"; }
   void operator()(const llvm::json::Object &request) const override;
 };
 
-class ReadMemoryRequestHandler : public RequestHandler {
+class ReadMemoryRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() { return "readMemory"; }
   void operator()(const llvm::json::Object &request) const override;
 };
@@ -294,9 +385,9 @@ class ReadMemoryRequestHandler : public RequestHandler {
 /// currently set in the target. This helps us to test "setBreakpoints" and
 /// "setFunctionBreakpoints" requests to verify we have the correct set of
 /// breakpoints currently set in LLDB.
-class TestGetTargetBreakpointsRequestHandler : public RequestHandler {
+class TestGetTargetBreakpointsRequestHandler : public LegacyRequestHandler {
 public:
-  using RequestHandler::RequestHandler;
+  using LegacyRequestHandler::LegacyRequestHandler;
   static llvm::StringLiteral getCommand() {
     return "_testGetTargetBreakpoints";
   }

diff  --git a/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
index 493543b395fd1..57cfb09692990 100644
--- a/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
@@ -7,115 +7,46 @@
 //===----------------------------------------------------------------------===//
 
 #include "DAP.h"
-#include "EventHelper.h"
-#include "JSONUtils.h"
+#include "Handler/RequestHandler.h"
 #include "LLDBUtils.h"
-#include "RequestHandler.h"
+#include "Protocol/ProtocolRequests.h"
+#include "Protocol/ProtocolTypes.h"
 #include "lldb/API/SBFrame.h"
 #include "lldb/API/SBInstructionList.h"
 #include "lldb/API/SBProcess.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBTarget.h"
 #include "lldb/API/SBThread.h"
-#include "llvm/Support/JSON.h"
+#include "llvm/Support/Error.h"
 
 namespace lldb_dap {
 
-// "SourceRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Source request; value of command field is 'source'. The
-//     request retrieves the source code for a given source reference.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "source" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/SourceArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "SourceArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'source' request.",
-//   "properties": {
-//     "source": {
-//       "$ref": "#/definitions/Source",
-//       "description": "Specifies the source content to load. Either
-//       source.path or source.sourceReference must be specified."
-//     },
-//     "sourceReference": {
-//       "type": "integer",
-//       "description": "The reference to the source. This is the same as
-//       source.sourceReference. This is provided for backward compatibility
-//       since old backends do not understand the 'source' attribute."
-//     }
-//   },
-//   "required": [ "sourceReference" ]
-// },
-// "SourceResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'source' request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "content": {
-//             "type": "string",
-//             "description": "Content of the source reference."
-//           },
-//           "mimeType": {
-//             "type": "string",
-//             "description": "Optional content type (mime type) of the source."
-//           }
-//         },
-//         "required": [ "content" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void SourceRequestHandler::operator()(const llvm::json::Object &request) const {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  const auto *source = arguments->getObject("source");
-  llvm::json::Object body;
-  const auto source_ref =
-      GetInteger<uint64_t>(source, "sourceReference")
-          .value_or(
-              GetInteger<uint64_t>(arguments, "sourceReference").value_or(0));
+/// Source request; value of command field is 'source'. The request retrieves
+/// the source code for a given source reference.
+llvm::Expected<protocol::SourceResponseBody>
+SourceRequestHandler::Run(const protocol::SourceArguments &args) const {
+  const auto source =
+      args.source->sourceReference.value_or(args.sourceReference);
 
-  if (source_ref) {
-    lldb::SBProcess process = dap.target.GetProcess();
-    // Upper 32 bits is the thread index ID
-    lldb::SBThread thread =
-        process.GetThreadByIndexID(GetLLDBThreadIndexID(source_ref));
-    // Lower 32 bits is the frame index
-    lldb::SBFrame frame = thread.GetFrameAtIndex(GetLLDBFrameID(source_ref));
-    if (!frame.IsValid()) {
-      response["success"] = false;
-      response["message"] = "source not found";
-    } else {
-      lldb::SBInstructionList insts =
-          frame.GetSymbol().GetInstructions(dap.target);
-      lldb::SBStream stream;
-      insts.GetDescription(stream);
-      body["content"] = stream.GetData();
-      body["mimeType"] = "text/x-lldb.disassembly";
-      response.try_emplace("body", std::move(body));
-    }
-  } else {
-    response["success"] = false;
-    response["message"] =
-        "invalid arguments, expected source.sourceReference to be set";
-  }
+  if (!source)
+    return llvm::createStringError(
+        "invalid arguments, expected source.sourceReference to be set");
 
-  dap.SendJSON(llvm::json::Value(std::move(response)));
+  lldb::SBProcess process = dap.target.GetProcess();
+  // Upper 32 bits is the thread index ID
+  lldb::SBThread thread =
+      process.GetThreadByIndexID(GetLLDBThreadIndexID(source));
+  // Lower 32 bits is the frame index
+  lldb::SBFrame frame = thread.GetFrameAtIndex(GetLLDBFrameID(source));
+  if (!frame.IsValid())
+    return llvm::createStringError("source not found");
+
+  lldb::SBInstructionList insts = frame.GetSymbol().GetInstructions(dap.target);
+  lldb::SBStream stream;
+  insts.GetDescription(stream);
+
+  return protocol::SourceResponseBody{/*content=*/stream.GetData(),
+                                      /*mimeType=*/"text/x-lldb.disassembly"};
 }
 
 } // namespace lldb_dap

diff  --git a/lldb/tools/lldb-dap/LLDBUtils.cpp b/lldb/tools/lldb-dap/LLDBUtils.cpp
index 16ca3d779dfea..a27beff0b030d 100644
--- a/lldb/tools/lldb-dap/LLDBUtils.cpp
+++ b/lldb/tools/lldb-dap/LLDBUtils.cpp
@@ -163,4 +163,13 @@ GetEnvironmentFromArguments(const llvm::json::Object &arguments) {
   return envs;
 }
 
+llvm::Error ToError(const lldb::SBError &error) {
+  if (error.Success())
+    return llvm::Error::success();
+
+  return llvm::createStringError(
+      std::error_code(error.GetError(), std::generic_category()),
+      error.GetCString());
+}
+
 } // namespace lldb_dap

diff  --git a/lldb/tools/lldb-dap/LLDBUtils.h b/lldb/tools/lldb-dap/LLDBUtils.h
index a9e13bb3678da..2c57847303cb3 100644
--- a/lldb/tools/lldb-dap/LLDBUtils.h
+++ b/lldb/tools/lldb-dap/LLDBUtils.h
@@ -12,8 +12,10 @@
 #include "DAPForward.h"
 #include "lldb/API/SBDebugger.h"
 #include "lldb/API/SBEnvironment.h"
+#include "lldb/API/SBError.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/raw_ostream.h"
 #include <string>
@@ -154,6 +156,9 @@ uint32_t GetLLDBFrameID(uint64_t dap_frame_id);
 lldb::SBEnvironment
 GetEnvironmentFromArguments(const llvm::json::Object &arguments);
 
+/// Take ownership of the stored error.
+llvm::Error ToError(const lldb::SBError &error);
+
 } // namespace lldb_dap
 
 #endif

diff  --git a/lldb/tools/lldb-dap/Protocol.h b/lldb/tools/lldb-dap/Protocol.h
deleted file mode 100644
index a9a5532fa6bfa..0000000000000
--- a/lldb/tools/lldb-dap/Protocol.h
+++ /dev/null
@@ -1,245 +0,0 @@
-//===-- Protocol.h --------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file contains POD structs based on the DAP specification at
-// https://microsoft.github.io/debug-adapter-protocol/specification
-//
-// This is not meant to be a complete implementation, new interfaces are added
-// when they're needed.
-//
-// Each struct has a toJSON and fromJSON function, that converts between
-// the struct and a JSON representation. (See JSON.h)
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_H
-#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_H
-
-#include "llvm/Support/JSON.h"
-#include <cstdint>
-#include <optional>
-#include <string>
-#include <variant>
-
-namespace lldb_dap::protocol {
-
-// MARK: Base Protocol
-
-// "Request": {
-//   "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
-//     "type": "object",
-//     "description": "A client or debug adapter initiated request.",
-//     "properties": {
-//       "type": {
-//         "type": "string",
-//         "enum": [ "request" ]
-//       },
-//       "command": {
-//         "type": "string",
-//         "description": "The command to execute."
-//       },
-//       "arguments": {
-//         "type": [ "array", "boolean", "integer", "null", "number" , "object",
-//         "string" ], "description": "Object containing arguments for the
-//         command."
-//       }
-//     },
-//     "required": [ "type", "command" ]
-//   }]
-// },
-struct Request {
-  int64_t seq;
-  std::string command;
-  std::optional<llvm::json::Value> rawArguments;
-};
-llvm::json::Value toJSON(const Request &);
-bool fromJSON(const llvm::json::Value &, Request &, llvm::json::Path);
-
-// "Event": {
-//   "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
-//     "type": "object",
-//     "description": "A debug adapter initiated event.",
-//     "properties": {
-//       "type": {
-//         "type": "string",
-//         "enum": [ "event" ]
-//       },
-//       "event": {
-//         "type": "string",
-//         "description": "Type of event."
-//       },
-//       "body": {
-//         "type": [ "array", "boolean", "integer", "null", "number" , "object",
-//         "string" ], "description": "Event-specific information."
-//       }
-//     },
-//     "required": [ "type", "event" ]
-//   }]
-// },
-struct Event {
-  std::string event;
-  std::optional<llvm::json::Value> rawBody;
-};
-llvm::json::Value toJSON(const Event &);
-bool fromJSON(const llvm::json::Value &, Event &, llvm::json::Path);
-
-// "Response" : {
-//   "allOf" : [
-//     {"$ref" : "#/definitions/ProtocolMessage"}, {
-//       "type" : "object",
-//       "description" : "Response for a request.",
-//       "properties" : {
-//         "type" : {"type" : "string", "enum" : ["response"]},
-//         "request_seq" : {
-//           "type" : "integer",
-//           "description" : "Sequence number of the corresponding request."
-//         },
-//         "success" : {
-//           "type" : "boolean",
-//           "description" :
-//               "Outcome of the request.\nIf true, the request was successful "
-//               "and the `body` attribute may contain the result of the "
-//               "request.\nIf the value is false, the attribute `message` "
-//               "contains the error in short form and the `body` may contain "
-//               "additional information (see `ErrorResponse.body.error`)."
-//         },
-//         "command" :
-//             {"type" : "string", "description" : "The command requested."},
-//         "message" : {
-//           "type" : "string",
-//           "description" :
-//               "Contains the raw error in short form if `success` is "
-//               "false.\nThis raw error might be interpreted by the client and
-//               " "is not shown in the UI.\nSome predefined values exist.",
-//           "_enum" : [ "cancelled", "notStopped" ],
-//           "enumDescriptions" : [
-//             "the request was cancelled.",
-//             "the request may be retried once the adapter is in a 'stopped'"
-//             "state."
-//           ]
-//         },
-//         "body" : {
-//           "type" : [
-//             "array", "boolean", "integer", "null", "number", "object",
-//             "string"
-//           ],
-//           "description" : "Contains request result if success is true and "
-//                           "error details if success is false."
-//         }
-//       },
-//       "required" : [ "type", "request_seq", "success", "command" ]
-//     }
-//   ]
-// }
-struct Response {
-  enum class Message {
-    cancelled,
-    notStopped,
-  };
-
-  int64_t request_seq;
-  std::string command;
-  bool success;
-  // FIXME: Migrate usage of fallback string to ErrorMessage
-  std::optional<std::variant<Message, std::string>> message;
-  std::optional<llvm::json::Value> rawBody;
-};
-bool fromJSON(const llvm::json::Value &, Response &, llvm::json::Path);
-llvm::json::Value toJSON(const Response &);
-
-// "Message": {
-//   "type": "object",
-//   "description": "A structured message object. Used to return errors from
-//   requests.", "properties": {
-//     "id": {
-//       "type": "integer",
-//       "description": "Unique (within a debug adapter implementation)
-//       identifier for the message. The purpose of these error IDs is to help
-//       extension authors that have the requirement that every user visible
-//       error message needs a corresponding error number, so that users or
-//       customer support can find information about the specific error more
-//       easily."
-//     },
-//     "format": {
-//       "type": "string",
-//       "description": "A format string for the message. Embedded variables
-//       have the form `{name}`.\nIf variable name starts with an underscore
-//       character, the variable does not contain user data (PII) and can be
-//       safely used for telemetry purposes."
-//     },
-//     "variables": {
-//       "type": "object",
-//       "description": "An object used as a dictionary for looking up the
-//       variables in the format string.", "additionalProperties": {
-//         "type": "string",
-//         "description": "All dictionary values must be strings."
-//       }
-//     },
-//     "sendTelemetry": {
-//       "type": "boolean",
-//       "description": "If true send to telemetry."
-//     },
-//     "showUser": {
-//       "type": "boolean",
-//       "description": "If true show user."
-//     },
-//     "url": {
-//       "type": "string",
-//       "description": "A url where additional information about this message
-//       can be found."
-//     },
-//     "urlLabel": {
-//       "type": "string",
-//       "description": "A label that is presented to the user as the UI for
-//       opening the url."
-//     }
-//   },
-//   "required": [ "id", "format" ]
-// },
-struct ErrorMessage {
-  uint64_t id;
-  std::string format;
-  std::optional<std::map<std::string, std::string>> variables;
-  bool sendTelemetry;
-  bool showUser;
-  std::optional<std::string> url;
-  std::optional<std::string> urlLabel;
-};
-bool fromJSON(const llvm::json::Value &, ErrorMessage &, llvm::json::Path);
-llvm::json::Value toJSON(const ErrorMessage &);
-
-// "ProtocolMessage": {
-//   "type": "object",
-//   "title": "Base Protocol",
-//   "description": "Base class of requests, responses, and events.",
-//   "properties": {
-//     "seq": {
-//       "type": "integer",
-//       "description": "Sequence number of the message (also known as
-//       message ID). The `seq` for the first message sent by a client or
-//       debug adapter is 1, and for each subsequent message is 1 greater
-//       than the previous message sent by that actor. `seq` can be used to
-//       order requests, responses, and events, and to associate requests
-//       with their corresponding responses. For protocol messages of type
-//       `request` the sequence number can be used to cancel the request."
-//     },
-//     "type": {
-//       "type": "string",
-//       "description": "Message type.",
-//       "_enum": [ "request", "response", "event" ]
-//     }
-//   },
-//   "required": [ "seq", "type" ]
-// },
-using Message = std::variant<Request, Response, Event>;
-bool fromJSON(const llvm::json::Value &, Message &, llvm::json::Path);
-llvm::json::Value toJSON(const Message &);
-
-} // namespace lldb_dap::protocol
-
-#endif

diff  --git a/lldb/tools/lldb-dap/Protocol.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
similarity index 93%
rename from lldb/tools/lldb-dap/Protocol.cpp
rename to lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
index b516c0cb19ebf..f30517715194f 100644
--- a/lldb/tools/lldb-dap/Protocol.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
@@ -1,4 +1,4 @@
-//===-- Protocol.cpp ------------------------------------------------------===//
+//===-- ProtocolBase.cpp --------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "Protocol.h"
+#include "Protocol/ProtocolBase.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -29,8 +29,7 @@ static bool mapRaw(const json::Value &Params, StringLiteral Prop,
   return true;
 }
 
-namespace lldb_dap {
-namespace protocol {
+namespace lldb_dap::protocol {
 
 enum class MessageType { request, response, event };
 
@@ -61,8 +60,8 @@ json::Value toJSON(const Request &R) {
       {"command", R.command},
   };
 
-  if (R.rawArguments)
-    Result.insert({"arguments", R.rawArguments});
+  if (R.arguments)
+    Result.insert({"arguments", R.arguments});
 
   return std::move(Result);
 }
@@ -92,7 +91,7 @@ bool fromJSON(json::Value const &Params, Request &R, json::Path P) {
     return false;
   }
 
-  return mapRaw(Params, "arguments", R.rawArguments, P);
+  return mapRaw(Params, "arguments", R.arguments, P);
 }
 
 json::Value toJSON(const Response &R) {
@@ -119,8 +118,8 @@ json::Value toJSON(const Response &R) {
     }
   }
 
-  if (R.rawBody)
-    Result.insert({"body", R.rawBody});
+  if (R.body)
+    Result.insert({"body", R.body});
 
   return std::move(Result);
 }
@@ -176,7 +175,7 @@ bool fromJSON(json::Value const &Params, Response &R, json::Path P) {
   }
 
   return O.map("success", R.success) && O.mapOptional("message", R.message) &&
-         mapRaw(Params, "body", R.rawBody, P);
+         mapRaw(Params, "body", R.body, P);
 }
 
 json::Value toJSON(const ErrorMessage &EM) {
@@ -216,8 +215,8 @@ json::Value toJSON(const Event &E) {
       {"event", E.event},
   };
 
-  if (E.rawBody)
-    Result.insert({"body", E.rawBody});
+  if (E.body)
+    Result.insert({"body", E.body});
 
   return std::move(Result);
 }
@@ -247,7 +246,7 @@ bool fromJSON(json::Value const &Params, Event &E, json::Path P) {
     return false;
   }
 
-  return mapRaw(Params, "body", E.rawBody, P);
+  return mapRaw(Params, "body", E.body, P);
 }
 
 bool fromJSON(const json::Value &Params, Message &PM, json::Path P) {
@@ -287,5 +286,4 @@ json::Value toJSON(const Message &M) {
   return std::visit([](auto &M) { return toJSON(M); }, M);
 }
 
-} // namespace protocol
-} // namespace lldb_dap
+} // namespace lldb_dap::protocol

diff  --git a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
new file mode 100644
index 0000000000000..e10a903b80aaa
--- /dev/null
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
@@ -0,0 +1,149 @@
+//===-- ProtocolBase.h ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains POD structs based on the DAP specification at
+// https://microsoft.github.io/debug-adapter-protocol/specification
+//
+// This is not meant to be a complete implementation, new interfaces are added
+// when they're needed.
+//
+// Each struct has a toJSON and fromJSON function, that converts between
+// the struct and a JSON representation. (See JSON.h)
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_H
+#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_H
+
+#include "llvm/Support/JSON.h"
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <variant>
+
+namespace lldb_dap::protocol {
+
+// MARK: Base Protocol
+
+/// A client or debug adapter initiated request.
+struct Request {
+  /// Sequence number of the message (also known as message ID). The `seq` for
+  /// the first message sent by a client or debug adapter is 1, and for each
+  /// subsequent message is 1 greater than the previous message sent by that
+  /// actor. `seq` can be used to order requests, responses, and events, and to
+  /// associate requests with their corresponding responses. For protocol
+  /// messages of type `request` the sequence number can be used to cancel the
+  /// request.
+  int64_t seq;
+
+  /// The command to execute.
+  std::string command;
+
+  /// Object containing arguments for the command.
+  ///
+  /// Request handlers are expected to validate the arguments, which is handled
+  /// by `RequestHandler`.
+  std::optional<llvm::json::Value> arguments;
+};
+llvm::json::Value toJSON(const Request &);
+bool fromJSON(const llvm::json::Value &, Request &, llvm::json::Path);
+
+/// A debug adapter initiated event.
+struct Event {
+  /// Type of event.
+  std::string event;
+
+  /// Event-specific information.
+  std::optional<llvm::json::Value> body;
+};
+llvm::json::Value toJSON(const Event &);
+bool fromJSON(const llvm::json::Value &, Event &, llvm::json::Path);
+
+/// Response for a request.
+struct Response {
+  enum class Message {
+    /// The request was cancelled
+    cancelled,
+    /// The request may be retried once the adapter is in a 'stopped' state
+    notStopped,
+  };
+
+  /// Sequence number of the corresponding request.
+  int64_t request_seq;
+
+  /// The command requested.
+  std::string command;
+
+  /// Outcome of the request. If true, the request was successful and the `body`
+  /// attribute may contain the result of the request. If the value is false,
+  /// the attribute `message` contains the error in short form and the `body`
+  /// may contain additional information (see `ErrorMessage`).
+  bool success;
+
+  // FIXME: Migrate usage of fallback string to ErrorMessage
+
+  /// Contains the raw error in short form if `success` is false. This raw error
+  /// might be interpreted by the client and is not shown in the UI. Some
+  /// predefined values exist.
+  std::optional<std::variant<Message, std::string>> message;
+
+  /// Contains request result if success is true and error details if success is
+  /// false.
+  ///
+  /// Request handlers are expected to build an appropriate body, see
+  /// `RequestHandler`.
+  std::optional<llvm::json::Value> body;
+};
+bool fromJSON(const llvm::json::Value &, Response &, llvm::json::Path);
+llvm::json::Value toJSON(const Response &);
+
+/// A structured message object. Used to return errors from requests.
+struct ErrorMessage {
+  /// Unique (within a debug adapter implementation) identifier for the message.
+  /// The purpose of these error IDs is to help extension authors that have the
+  /// requirement that every user visible error message needs a corresponding
+  /// error number, so that users or customer support can find information about
+  /// the specific error more easily.
+  uint64_t id;
+
+  /// A format string for the message. Embedded variables have the form
+  /// `{name}`. If variable name starts with an underscore character, the
+  /// variable does not contain user data (PII) and can be safely used for
+  /// telemetry purposes.
+  std::string format;
+
+  /// An object used as a dictionary for looking up the variables in the format
+  /// string.
+  std::optional<std::map<std::string, std::string>> variables;
+
+  /// If true send to telemetry.
+  bool sendTelemetry;
+
+  /// If true show user.
+  bool showUser;
+
+  /// A url where additional information about this message can be found.
+  std::optional<std::string> url;
+
+  /// A label that is presented to the user as the UI for opening the url.
+  std::optional<std::string> urlLabel;
+};
+bool fromJSON(const llvm::json::Value &, ErrorMessage &, llvm::json::Path);
+llvm::json::Value toJSON(const ErrorMessage &);
+
+/// An individual protocol message of requests, responses, and events.
+using Message = std::variant<Request, Response, Event>;
+bool fromJSON(const llvm::json::Value &, Message &, llvm::json::Path);
+llvm::json::Value toJSON(const Message &);
+
+/// This is just an acknowledgement, so no body field is required.
+using VoidResponse = std::monostate;
+
+} // namespace lldb_dap::protocol
+
+#endif

diff  --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
new file mode 100644
index 0000000000000..5cc5429227439
--- /dev/null
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -0,0 +1,41 @@
+//===-- ProtocolRequests.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Protocol/ProtocolRequests.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
+#include <utility>
+
+using namespace llvm;
+
+namespace lldb_dap::protocol {
+
+bool fromJSON(const json::Value &Params, DisconnectArguments &DA,
+              json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.mapOptional("restart", DA.restart) &&
+         O.mapOptional("terminateDebuggee", DA.terminateDebuggee) &&
+         O.mapOptional("suspendDebuggee", DA.suspendDebuggee);
+}
+
+bool fromJSON(const json::Value &Params, SourceArguments &SA, json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.mapOptional("source", SA.source) &&
+         O.map("sourceReference", SA.sourceReference);
+}
+
+json::Value toJSON(const SourceResponseBody &SA) {
+  json::Object Result{{"content", SA.content}};
+
+  if (SA.mimeType)
+    Result.insert({"mimeType", SA.mimeType});
+
+  return std::move(Result);
+}
+
+} // namespace lldb_dap::protocol

diff  --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
new file mode 100644
index 0000000000000..5dc4a589178d2
--- /dev/null
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -0,0 +1,82 @@
+//===-- ProtocolTypes.h ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains POD structs based on the DAP specification at
+// https://microsoft.github.io/debug-adapter-protocol/specification
+//
+// This is not meant to be a complete implementation, new interfaces are added
+// when they're needed.
+//
+// Each struct has a toJSON and fromJSON function, that converts between
+// the struct and a JSON representation. (See JSON.h)
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_REQUESTS_H
+#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_REQUESTS_H
+
+#include "Protocol/ProtocolBase.h"
+#include "Protocol/ProtocolTypes.h"
+#include "llvm/Support/JSON.h"
+#include <cstdint>
+#include <optional>
+#include <string>
+
+namespace lldb_dap::protocol {
+
+/// Arguments for `disconnect` request.
+struct DisconnectArguments {
+  /// A value of true indicates that this `disconnect` request is part of a
+  /// restart sequence.
+  std::optional<bool> restart;
+
+  /// Indicates whether the debuggee should be terminated when the debugger is
+  /// disconnected. If unspecified, the debug adapter is free to do whatever it
+  /// thinks is best. The attribute is only honored by a debug adapter if the
+  /// corresponding capability `supportTerminateDebuggee` is true.
+  std::optional<bool> terminateDebuggee;
+
+  /// Indicates whether the debuggee should stay suspended when the debugger is
+  /// disconnected. If unspecified, the debuggee should resume execution. The
+  /// attribute is only honored by a debug adapter if the corresponding
+  /// capability `supportSuspendDebuggee` is true.
+  std::optional<bool> suspendDebuggee;
+};
+bool fromJSON(const llvm::json::Value &, DisconnectArguments &,
+              llvm::json::Path);
+
+/// Response to `disconnect` request. This is just an acknowledgement, so no
+/// body field is required.
+using DisconnectResponse = VoidResponse;
+
+/// Arguments for `source` request.
+struct SourceArguments {
+  /// Specifies the source content to load. Either `source.path` or
+  /// `source.sourceReference` must be specified.
+  std::optional<Source> source;
+
+  /// The reference to the source. This is the same as `source.sourceReference`.
+  /// This is provided for backward compatibility since old clients do not
+  /// understand the `source` attribute.
+  int64_t sourceReference;
+};
+bool fromJSON(const llvm::json::Value &, SourceArguments &, llvm::json::Path);
+
+/// Response to `source` request.
+struct SourceResponseBody {
+  /// Content of the source reference.
+  std::string content;
+
+  /// Content type (MIME type) of the source.
+  std::optional<std::string> mimeType;
+};
+llvm::json::Value toJSON(const SourceResponseBody &);
+
+} // namespace lldb_dap::protocol
+
+#endif

diff  --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
new file mode 100644
index 0000000000000..efb5c3abe32bf
--- /dev/null
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -0,0 +1,47 @@
+//===-- ProtocolTypes.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Protocol/ProtocolTypes.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/JSON.h"
+#include <optional>
+
+using namespace llvm;
+
+namespace lldb_dap::protocol {
+
+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<Source::PresentationHint> hint =
+      StringSwitch<std::optional<Source::PresentationHint>>(*rawHint)
+          .Case("normal", Source::PresentationHint::normal)
+          .Case("emphasize", Source::PresentationHint::emphasize)
+          .Case("deemphasize", Source::PresentationHint::deemphasize)
+          .Default(std::nullopt);
+  if (!hint) {
+    P.report("unexpected value");
+    return false;
+  }
+  PH = *hint;
+  return true;
+}
+
+bool fromJSON(const json::Value &Params, Source &S, json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.mapOptional("name", S.name) && O.mapOptional("path", S.path) &&
+         O.mapOptional("presentationHint", S.presentationHint) &&
+         O.mapOptional("sourceReference", S.sourceReference);
+}
+
+} // namespace lldb_dap::protocol

diff  --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
new file mode 100644
index 0000000000000..b54d76cb29a77
--- /dev/null
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -0,0 +1,63 @@
+//===-- ProtocolTypes.h ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains POD structs based on the DAP specification at
+// https://microsoft.github.io/debug-adapter-protocol/specification
+//
+// This is not meant to be a complete implementation, new interfaces are added
+// when they're needed.
+//
+// Each struct has a toJSON and fromJSON function, that converts between
+// the struct and a JSON representation. (See JSON.h)
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H
+#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H
+
+#include "llvm/Support/JSON.h"
+#include <cstdint>
+#include <optional>
+#include <string>
+
+namespace lldb_dap::protocol {
+
+/// 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 class PresentationHint { normal, emphasize, deemphasize };
+
+  /// 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.
+  std::optional<std::string> name;
+
+  /// The path of the source to be shown in the UI. It is only used to locate
+  /// and load the content of the source if no `sourceReference` is specified
+  /// (or its value is 0).
+  std::optional<std::string> path;
+
+  /// If the value > 0 the contents of the source must be retrieved through the
+  /// `source` request (even if a path is specified). Since a `sourceReference`
+  /// is only valid for a session, it can not be used to persist a source. The
+  /// value should be less than or equal to 2147483647 (2^31-1).
+  std::optional<int64_t> sourceReference;
+
+  /// A hint for how to present the source in the UI. A value of `deemphasize`
+  /// can be used to indicate that the source is not available or that it is
+  /// skipped on stepping.
+  std::optional<PresentationHint> presentationHint;
+
+  // unsupported keys: origin, sources, adapterData, checksums
+};
+bool fromJSON(const llvm::json::Value &, Source &, llvm::json::Path);
+
+} // namespace lldb_dap::protocol
+
+#endif

diff  --git a/lldb/tools/lldb-dap/Transport.cpp b/lldb/tools/lldb-dap/Transport.cpp
index a721662a345eb..4500e7cf909ba 100644
--- a/lldb/tools/lldb-dap/Transport.cpp
+++ b/lldb/tools/lldb-dap/Transport.cpp
@@ -8,7 +8,7 @@
 
 #include "Transport.h"
 #include "DAPLog.h"
-#include "Protocol.h"
+#include "Protocol/ProtocolBase.h"
 #include "lldb/Utility/IOObject.h"
 #include "lldb/Utility/Status.h"
 #include "lldb/lldb-forward.h"

diff  --git a/lldb/tools/lldb-dap/Transport.h b/lldb/tools/lldb-dap/Transport.h
index 013a6c98af1ce..e77bb5dd05e65 100644
--- a/lldb/tools/lldb-dap/Transport.h
+++ b/lldb/tools/lldb-dap/Transport.h
@@ -14,7 +14,7 @@
 #ifndef LLDB_TOOLS_LLDB_DAP_TRANSPORT_H
 #define LLDB_TOOLS_LLDB_DAP_TRANSPORT_H
 
-#include "Protocol.h"
+#include "Protocol/ProtocolBase.h"
 #include "lldb/lldb-forward.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"

diff  --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index ca8b548632ff7..59e31cf8e2cc8 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -374,11 +374,11 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
   {
     std::scoped_lock<std::mutex> lock(dap_sessions_mutex);
     for (auto [sock, dap] : dap_sessions) {
-      auto error = dap->Disconnect();
-      if (error.Fail()) {
+      if (llvm::Error error = dap->Disconnect()) {
         client_failed = true;
         llvm::errs() << "DAP client " << dap->transport.GetClientName()
-                     << " disconnected failed: " << error.GetCString() << "\n";
+                     << " disconnected failed: "
+                     << llvm::toString(std::move(error)) << "\n";
       }
       // Close the socket to ensure the DAP::Loop read finishes.
       sock->Close();


        


More information about the lldb-commits mailing list