https://github.com/ashgti updated 
https://github.com/llvm/llvm-project/pull/129155

>From 5ec870632250a2bf59341c2699a6a93b31c155ba Mon Sep 17 00:00:00 2001
From: John Harrison <harj...@google.com>
Date: Thu, 27 Feb 2025 15:27:41 -0800
Subject: [PATCH 1/2] [lldb-dap] Creating a new set of types for handling DAP
 messages.

This adds a new Protocol.{h,cpp} for defining structured types that represent 
Debug Adapter Protocol messages.

This adds static types to define well structure messages for the protocol. This 
iteration includes only the basic `Event`, `Request` and `Response` types.

In a follow-up patch I plan on adding more types as need to allow for 
incrementally migrating raw `llvm::json::Value` usage to well defined types.
---
 lldb/tools/lldb-dap/CMakeLists.txt |   1 +
 lldb/tools/lldb-dap/Protocol.cpp   | 191 +++++++++++++++++++++++++++++
 lldb/tools/lldb-dap/Protocol.h     | 187 ++++++++++++++++++++++++++++
 3 files changed, 379 insertions(+)
 create mode 100644 lldb/tools/lldb-dap/Protocol.cpp
 create mode 100644 lldb/tools/lldb-dap/Protocol.h

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt 
b/lldb/tools/lldb-dap/CMakeLists.txt
index 8b3c520ec4360..9a2d604f4d573 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -35,6 +35,7 @@ add_lldb_tool(lldb-dap
   ProgressEvent.cpp
   RunInTerminal.cpp
   SourceBreakpoint.cpp
+  Protocol.cpp
   Watchpoint.cpp
 
   Handler/ResponseHandler.cpp
diff --git a/lldb/tools/lldb-dap/Protocol.cpp b/lldb/tools/lldb-dap/Protocol.cpp
new file mode 100644
index 0000000000000..71c4dc080a5b1
--- /dev/null
+++ b/lldb/tools/lldb-dap/Protocol.cpp
@@ -0,0 +1,191 @@
+//===-- Protocol.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.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/JSON.h"
+#include <optional>
+#include <utility>
+
+namespace llvm {
+namespace json {
+bool fromJSON(const llvm::json::Value &Params, llvm::json::Value &V,
+              llvm::json::Path P) {
+  V = std::move(Params);
+  return true;
+}
+} // namespace json
+} // namespace llvm
+
+namespace lldb_dap {
+namespace protocol {
+
+enum class MessageType { request, response, event };
+
+bool fromJSON(const llvm::json::Value &Params, MessageType &M,
+              llvm::json::Path P) {
+  auto rawType = Params.getAsString();
+  if (!rawType) {
+    P.report("expected a string");
+    return false;
+  }
+  std::optional<MessageType> type =
+      llvm::StringSwitch<std::optional<MessageType>>(*rawType)
+          .Case("request", MessageType::request)
+          .Case("response", MessageType::response)
+          .Case("event", MessageType::event)
+          .Default(std::nullopt);
+  if (!type) {
+    P.report("unexpected value");
+    return false;
+  }
+  M = *type;
+  return true;
+}
+
+llvm::json::Value toJSON(const Request &R) {
+  llvm::json::Object Result{
+      {"type", "request"},
+      {"seq", R.seq},
+      {"command", R.command},
+  };
+  if (R.rawArguments)
+    Result.insert({"arguments", R.rawArguments});
+  return std::move(Result);
+}
+
+bool fromJSON(llvm::json::Value const &Params, Request &R, llvm::json::Path P) 
{
+  llvm::json::ObjectMapper O(Params, P);
+  MessageType type;
+  if (!O.map("type", type)) {
+    return false;
+  }
+  if (type != MessageType::request) {
+    P.field("type").report("expected to be 'request'");
+    return false;
+  }
+
+  return O && O.map("command", R.command) && O.map("seq", R.seq) &&
+         O.map("arguments", R.rawArguments);
+}
+
+llvm::json::Value toJSON(const Response &R) {
+  llvm::json::Object Result{{"type", "response"},
+                            {"req", 0},
+                            {"command", R.command},
+                            {"request_seq", R.request_seq},
+                            {"success", R.success}};
+
+  if (R.message)
+    Result.insert({"message", R.message});
+  if (R.rawBody)
+    Result.insert({"body", R.rawBody});
+
+  return std::move(Result);
+}
+
+bool fromJSON(llvm::json::Value const &Params, Response &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  MessageType type;
+  if (!O.map("type", type)) {
+    return false;
+  }
+  if (type != MessageType::response) {
+    P.field("type").report("expected to be 'response'");
+    return false;
+  }
+  return O && O.map("command", R.command) &&
+         O.map("request_seq", R.request_seq) && O.map("success", R.success) &&
+         O.mapOptional("message", R.message) &&
+         O.mapOptional("body", R.rawBody);
+}
+
+llvm::json::Value toJSON(const Event &E) {
+  llvm::json::Object Result{
+      {"type", "event"},
+      {"seq", 0},
+      {"event", E.event},
+  };
+  if (E.rawBody)
+    Result.insert({"body", E.rawBody});
+  if (E.statistics)
+    Result.insert({"statistics", E.statistics});
+  return std::move(Result);
+}
+
+bool fromJSON(llvm::json::Value const &Params, Event &E, llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  MessageType type;
+  if (!O.map("type", type)) {
+    return false;
+  }
+  if (type != MessageType::event) {
+    P.field("type").report("expected to be 'event'");
+    return false;
+  }
+
+  return O && O.map("event", E.event) && O.mapOptional("body", E.rawBody) &&
+         O.mapOptional("statistics", E.statistics);
+}
+
+bool fromJSON(const llvm::json::Value &Params, ProtocolMessage &PM,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  if (!O)
+    return false;
+
+  MessageType type;
+  if (!O.map("type", type))
+    return false;
+
+  switch (type) {
+  case MessageType::request: {
+    Request req;
+    if (!fromJSON(Params, req, P)) {
+      return false;
+    }
+    PM = std::move(req);
+    return true;
+  }
+  case MessageType::response: {
+    Response resp;
+    if (!fromJSON(Params, resp, P)) {
+      return false;
+    }
+    PM = std::move(resp);
+    return true;
+  }
+  case MessageType::event:
+    Event evt;
+    if (!fromJSON(Params, evt, P)) {
+      return false;
+    }
+    PM = std::move(evt);
+    return true;
+  }
+  llvm_unreachable("Unsupported protocol message");
+}
+
+llvm::json::Value toJSON(const ProtocolMessage &PM) {
+  if (auto const *Req = std::get_if<Request>(&PM)) {
+    return toJSON(*Req);
+  }
+  if (auto const *Resp = std::get_if<Response>(&PM)) {
+    return toJSON(*Resp);
+  }
+  if (auto const *Evt = std::get_if<Event>(&PM)) {
+    return toJSON(*Evt);
+  }
+  llvm_unreachable("Unsupported protocol message");
+}
+
+} // namespace protocol
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Protocol.h b/lldb/tools/lldb-dap/Protocol.h
new file mode 100644
index 0000000000000..0d38f8b249094
--- /dev/null
+++ b/lldb/tools/lldb-dap/Protocol.h
@@ -0,0 +1,187 @@
+//===-- 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 <cstddef>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <variant>
+
+namespace lldb_dap {
+namespace 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;
+
+  /// lldb-dap specific extension on the 'terminated' event specifically.
+  std::optional<llvm::json::Value> statistics;
+};
+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 {
+  int64_t request_seq;
+  bool success;
+  std::string command;
+  std::optional<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 &);
+
+// A void response body for any response without a specific value.
+using VoidResponseBody = std::nullptr_t;
+
+// "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 ProtocolMessage = std::variant<Request, Response, Event>;
+bool fromJSON(const llvm::json::Value &, ProtocolMessage &, llvm::json::Path);
+llvm::json::Value toJSON(const ProtocolMessage &);
+
+} // namespace protocol
+} // namespace lldb_dap
+
+#endif

>From b171e5e821f57ae31bcbbc1b81388358182c56ca Mon Sep 17 00:00:00 2001
From: John Harrison <harj...@google.com>
Date: Fri, 28 Feb 2025 09:32:49 -0800
Subject: [PATCH 2/2] Addressing style comments and reviewer feedback.

---
 lldb/tools/lldb-dap/Protocol.cpp | 166 +++++++++++++++++++------------
 lldb/tools/lldb-dap/Protocol.h   |   6 +-
 2 files changed, 106 insertions(+), 66 deletions(-)

diff --git a/lldb/tools/lldb-dap/Protocol.cpp b/lldb/tools/lldb-dap/Protocol.cpp
index 71c4dc080a5b1..d7f527b1a7d22 100644
--- a/lldb/tools/lldb-dap/Protocol.cpp
+++ b/lldb/tools/lldb-dap/Protocol.cpp
@@ -14,131 +14,184 @@
 #include <optional>
 #include <utility>
 
-namespace llvm {
-namespace json {
-bool fromJSON(const llvm::json::Value &Params, llvm::json::Value &V,
-              llvm::json::Path P) {
-  V = std::move(Params);
+using namespace llvm;
+
+static bool mapRaw(const json::Value &Params, StringLiteral Prop,
+                   std::optional<json::Value> &V, json::Path P) {
+  const auto *O = Params.getAsObject();
+  if (!O) {
+    P.report("expected object");
+    return false;
+  }
+  if (const json::Value *E = O->get(Prop))
+    V = std::move(Params);
   return true;
 }
-} // namespace json
-} // namespace llvm
 
 namespace lldb_dap {
 namespace protocol {
 
 enum class MessageType { request, response, event };
 
-bool fromJSON(const llvm::json::Value &Params, MessageType &M,
-              llvm::json::Path P) {
+bool fromJSON(const json::Value &Params, MessageType &M, json::Path P) {
   auto rawType = Params.getAsString();
   if (!rawType) {
     P.report("expected a string");
     return false;
   }
   std::optional<MessageType> type =
-      llvm::StringSwitch<std::optional<MessageType>>(*rawType)
+      StringSwitch<std::optional<MessageType>>(*rawType)
           .Case("request", MessageType::request)
           .Case("response", MessageType::response)
           .Case("event", MessageType::event)
           .Default(std::nullopt);
   if (!type) {
-    P.report("unexpected value");
+    P.report("unexpected value, expected 'request', 'response' or 'event'");
     return false;
   }
   M = *type;
   return true;
 }
 
-llvm::json::Value toJSON(const Request &R) {
-  llvm::json::Object Result{
+json::Value toJSON(const Request &R) {
+  json::Object Result{
       {"type", "request"},
       {"seq", R.seq},
       {"command", R.command},
   };
+
   if (R.rawArguments)
     Result.insert({"arguments", R.rawArguments});
+
   return std::move(Result);
 }
 
-bool fromJSON(llvm::json::Value const &Params, Request &R, llvm::json::Path P) 
{
-  llvm::json::ObjectMapper O(Params, P);
+bool fromJSON(json::Value const &Params, Request &R, json::Path P) {
+  json::ObjectMapper O(Params, P);
+  if (!O)
+    return false;
+
   MessageType type;
-  if (!O.map("type", type)) {
+  if (!O.map("type", type) || !O.map("command", R.command) ||
+      !O.map("seq", R.seq))
     return false;
-  }
+
   if (type != MessageType::request) {
     P.field("type").report("expected to be 'request'");
     return false;
   }
 
-  return O && O.map("command", R.command) && O.map("seq", R.seq) &&
-         O.map("arguments", R.rawArguments);
+  if (R.command.empty()) {
+    P.field("command").report("expected to not be ''");
+    return false;
+  }
+
+  if (!R.seq) {
+    P.field("seq").report("expected to not be '0'");
+    return false;
+  }
+
+  return mapRaw(Params, "arguments", R.rawArguments, P);
 }
 
-llvm::json::Value toJSON(const Response &R) {
-  llvm::json::Object Result{{"type", "response"},
-                            {"req", 0},
-                            {"command", R.command},
-                            {"request_seq", R.request_seq},
-                            {"success", R.success}};
+json::Value toJSON(const Response &R) {
+  json::Object Result{{"type", "response"},
+                      {"req", 0},
+                      {"command", R.command},
+                      {"request_seq", R.request_seq},
+                      {"success", R.success}};
 
   if (R.message)
     Result.insert({"message", R.message});
+
   if (R.rawBody)
     Result.insert({"body", R.rawBody});
 
   return std::move(Result);
 }
 
-bool fromJSON(llvm::json::Value const &Params, Response &R,
-              llvm::json::Path P) {
-  llvm::json::ObjectMapper O(Params, P);
+bool fromJSON(json::Value const &Params, Response &R, json::Path P) {
+  json::ObjectMapper O(Params, P);
+  if (!O)
+    return false;
+
   MessageType type;
-  if (!O.map("type", type)) {
+  int64_t seq;
+  if (!O.map("type", type) || !O.map("seq", seq) ||
+      !O.map("command", R.command) || !O.map("request_seq", R.request_seq))
     return false;
-  }
+
   if (type != MessageType::response) {
     P.field("type").report("expected to be 'response'");
     return false;
   }
-  return O && O.map("command", R.command) &&
-         O.map("request_seq", R.request_seq) && O.map("success", R.success) &&
-         O.mapOptional("message", R.message) &&
-         O.mapOptional("body", R.rawBody);
+
+  if (seq != 0) {
+    P.field("seq").report("expected to be '0'");
+    return false;
+  }
+
+  if (R.command.empty()) {
+    P.field("command").report("expected to not be ''");
+    return false;
+  }
+
+  if (R.request_seq == 0) {
+    P.field("request_seq").report("expected to not be '0'");
+    return false;
+  }
+
+  return O.map("success", R.success) && O.mapOptional("message", R.message) &&
+         mapRaw(Params, "body", R.rawBody, P);
 }
 
-llvm::json::Value toJSON(const Event &E) {
-  llvm::json::Object Result{
+json::Value toJSON(const Event &E) {
+  json::Object Result{
       {"type", "event"},
       {"seq", 0},
       {"event", E.event},
   };
+
   if (E.rawBody)
     Result.insert({"body", E.rawBody});
+
   if (E.statistics)
     Result.insert({"statistics", E.statistics});
+
   return std::move(Result);
 }
 
-bool fromJSON(llvm::json::Value const &Params, Event &E, llvm::json::Path P) {
-  llvm::json::ObjectMapper O(Params, P);
+bool fromJSON(json::Value const &Params, Event &E, json::Path P) {
+  json::ObjectMapper O(Params, P);
+  if (!O)
+    return false;
+
   MessageType type;
-  if (!O.map("type", type)) {
+  int64_t seq;
+  if (!O.map("type", type) || !O.map("seq", seq) || O.map("event", E.event))
     return false;
-  }
+
   if (type != MessageType::event) {
     P.field("type").report("expected to be 'event'");
     return false;
   }
 
-  return O && O.map("event", E.event) && O.mapOptional("body", E.rawBody) &&
-         O.mapOptional("statistics", E.statistics);
+  if (seq != 0) {
+    P.field("seq").report("expected to be '0'");
+    return false;
+  }
+
+  if (E.event.empty()) {
+    P.field("event").report("expected to not be ''");
+    return false;
+  }
+
+  return mapRaw(Params, "body", E.rawBody, P) &&
+         mapRaw(Params, "statistics", E.statistics, P);
 }
 
-bool fromJSON(const llvm::json::Value &Params, ProtocolMessage &PM,
-              llvm::json::Path P) {
-  llvm::json::ObjectMapper O(Params, P);
+bool fromJSON(const json::Value &Params, Message &PM, json::Path P) {
+  json::ObjectMapper O(Params, P);
   if (!O)
     return false;
 
@@ -149,42 +202,29 @@ bool fromJSON(const llvm::json::Value &Params, 
ProtocolMessage &PM,
   switch (type) {
   case MessageType::request: {
     Request req;
-    if (!fromJSON(Params, req, P)) {
+    if (!fromJSON(Params, req, P))
       return false;
-    }
     PM = std::move(req);
     return true;
   }
   case MessageType::response: {
     Response resp;
-    if (!fromJSON(Params, resp, P)) {
+    if (!fromJSON(Params, resp, P))
       return false;
-    }
     PM = std::move(resp);
     return true;
   }
   case MessageType::event:
     Event evt;
-    if (!fromJSON(Params, evt, P)) {
+    if (!fromJSON(Params, evt, P))
       return false;
-    }
     PM = std::move(evt);
     return true;
   }
-  llvm_unreachable("Unsupported protocol message");
 }
 
-llvm::json::Value toJSON(const ProtocolMessage &PM) {
-  if (auto const *Req = std::get_if<Request>(&PM)) {
-    return toJSON(*Req);
-  }
-  if (auto const *Resp = std::get_if<Response>(&PM)) {
-    return toJSON(*Resp);
-  }
-  if (auto const *Evt = std::get_if<Event>(&PM)) {
-    return toJSON(*Evt);
-  }
-  llvm_unreachable("Unsupported protocol message");
+json::Value toJSON(const Message &M) {
+  return std::visit([](auto &M) { return toJSON(M); }, M);
 }
 
 } // namespace protocol
diff --git a/lldb/tools/lldb-dap/Protocol.h b/lldb/tools/lldb-dap/Protocol.h
index 0d38f8b249094..0318161fd4eb6 100644
--- a/lldb/tools/lldb-dap/Protocol.h
+++ b/lldb/tools/lldb-dap/Protocol.h
@@ -177,9 +177,9 @@ using VoidResponseBody = std::nullptr_t;
 //   },
 //   "required": [ "seq", "type" ]
 // },
-using ProtocolMessage = std::variant<Request, Response, Event>;
-bool fromJSON(const llvm::json::Value &, ProtocolMessage &, llvm::json::Path);
-llvm::json::Value toJSON(const ProtocolMessage &);
+using Message = std::variant<Request, Response, Event>;
+bool fromJSON(const llvm::json::Value &, Message &, llvm::json::Path);
+llvm::json::Value toJSON(const Message &);
 
 } // namespace protocol
 } // namespace lldb_dap

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to