[Lldb-commits] [lldb] [llvm] [lldb-dap] migrate set breakpoint requests (PR #137448)

Ely Ronnen via lldb-commits lldb-commits at lists.llvm.org
Sat Apr 26 11:15:17 PDT 2025


https://github.com/eronnen updated https://github.com/llvm/llvm-project/pull/137448

>From 60a9aff4ce3a561cf1cefa98685ea9d014af823c Mon Sep 17 00:00:00 2001
From: Ely Ronnen <elyronnen at gmail.com>
Date: Sat, 26 Apr 2025 10:25:21 +0200
Subject: [PATCH 01/10] adding breakpoints protocol types

---
 lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 178 +++++++++++++++++++
 llvm/include/llvm/Support/JSON.h             |   8 +
 2 files changed, 186 insertions(+)

diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index 54941f24efbd9..8b193287acc19 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -322,6 +322,184 @@ enum SteppingGranularity : unsigned {
 bool fromJSON(const llvm::json::Value &, SteppingGranularity &,
               llvm::json::Path);
 
+/// Information about a breakpoint created in `setBreakpoints`, `setFunctionBreakpoints`, `setInstructionBreakpoints`, or `setDataBreakpoints` requests.
+struct Breakpoint {
+  /// A machine-readable explanation of why a breakpoint may not be verified.
+  enum class Reason : unsigned {
+    /// Indicates a breakpoint might be verified in the future, but
+    /// the adapter cannot verify it in the current state.
+    eBreakpointReasonPending,
+    /// Indicates a breakpoint was not able to be verified, and the
+    /// adapter does not believe it can be verified without intervention.
+    eBreakpointReasonFailed,
+  };
+
+  /// The identifier for the breakpoint. It is needed if breakpoint events are
+  /// used to update or remove breakpoints.
+  std::optional<int> id;
+
+  /// If true, the breakpoint could be set (but not necessarily at the desired
+  /// location).
+  bool verified;
+
+  /// A message about the state of the breakpoint.
+  /// This is shown to the user and can be used to explain why a breakpoint could
+  /// not be verified.
+  std::optional<std::string> message;
+
+  /// The source where the breakpoint is located.
+  std::optional<Source> source;
+
+  /// The start line of the actual range covered by the breakpoint.
+  std::optional<uint32_t> line;
+
+  /// Start position of the source range covered by the breakpoint. It is
+  /// measured in UTF-16 code units and the client capability `columnsStartAt1`
+  /// determines whether it is 0- or 1-based.
+  std::optional<uint32_t> column;
+
+  /// The end line of the actual range covered by the breakpoint.
+  std::optional<uint32_t> endLine;
+
+  /// End position of the source range covered by the breakpoint. It is measured
+  /// in UTF-16 code units and the client capability `columnsStartAt1` determines
+  /// whether it is 0- or 1-based.
+  /// If no end line is given, then the end column is assumed to be in the start
+  /// line.
+  std::optional<uint32_t> endColumn;
+
+  /// A memory reference to where the breakpoint is set.
+  std::optional<std::string> instructionReference;
+
+  /// The offset from the instruction reference.
+  /// This can be negative.
+  std::optional<int64_t> offset;
+
+  /// A machine-readable explanation of why a breakpoint may not be verified. If
+  /// a breakpoint is verified or a specific reason is not known, the adapter
+  /// should omit this property.
+  std::optional<Reason> reason;
+};
+llvm::json::Value toJSON(const Breakpoint &);
+
+/// Properties of a breakpoint or logpoint passed to the `setBreakpoints` request
+struct SourceBreakpoint {
+  /// The source line of the breakpoint or logpoint.
+  uint32_t line;
+
+  /// Start position within source line of the breakpoint or logpoint. It is
+  /// measured in UTF-16 code units and the client capability `columnsStartAt1`
+  /// determines whether it is 0- or 1-based.
+  std::optional<uint32_t> column;
+
+  /// The expression for conditional breakpoints.
+  /// It is only honored by a debug adapter if the corresponding capability
+  /// `supportsConditionalBreakpoints` is true.
+  std::optional<std::string> condition;
+
+  /// The expression that controls how many hits of the breakpoint are ignored.
+  /// The debug adapter is expected to interpret the expression as needed.
+  /// The attribute is only honored by a debug adapter if the corresponding
+  /// capability `supportsHitConditionalBreakpoints` is true.
+  /// If both this property and `condition` are specified, `hitCondition` should
+  /// be evaluated only if the `condition` is met, and the debug adapter should
+  /// stop only if both conditions are met.
+  std::optional<std::string> hitCondition;
+
+  /// If this attribute exists and is non-empty, the debug adapter must not
+  /// 'break' (stop)
+  /// but log the message instead. Expressions within `{}` are interpolated.
+  /// The attribute is only honored by a debug adapter if the corresponding
+  /// capability `supportsLogPoints` is true.
+  /// If either `hitCondition` or `condition` is specified, then the message
+  /// should only be logged if those conditions are met.
+  std::optional<std::string> logMessage;
+
+  /// The mode of this breakpoint. If defined, this must be one of the
+  /// `breakpointModes` the debug adapter advertised in its `Capabilities`.
+  std::optional<std::string> mode;
+};
+bool fromJSON(const llvm::json::Value &, SourceBreakpoint &,
+              llvm::json::Path);
+
+/// Properties of a breakpoint passed to the `setFunctionBreakpoints` request.
+struct FunctionBreakpoint {
+  /// The name of the function.
+  std::string name;
+
+  /// An expression for conditional breakpoints.
+  /// It is only honored by a debug adapter if the corresponding capability
+  /// `supportsConditionalBreakpoints` is true.
+  std::optional<std::string> condition;
+
+  /// An expression that controls how many hits of the breakpoint are ignored.
+  /// The debug adapter is expected to interpret the expression as needed.
+  /// The attribute is only honored by a debug adapter if the corresponding
+  /// capability `supportsHitConditionalBreakpoints` is true.
+  std::optional<std::string> hitCondition;
+};
+bool fromJSON(const llvm::json::Value &, FunctionBreakpoint &,
+              llvm::json::Path);
+
+/// This enumeration defines all possible access types for data breakpoints. Values: ‘read’, ‘write’, ‘readWrite’
+enum DataBreakpointAccessType : unsigned {
+  eDataBreakpointAccessTypeRead,
+  eDataBreakpointAccessTypeWrite,
+  eDataBreakpointAccessTypeReadWrite
+};
+bool fromJSON(const llvm::json::Value &, DataBreakpointAccessType &,
+              llvm::json::Path);
+
+/// Properties of a data breakpoint passed to the `setDataBreakpoints` request.
+struct DataBreakpointInfo {
+  /// An id representing the data. This id is returned from the
+  /// `dataBreakpointInfo` request.
+  std::string dataId;
+
+  /// The access type of the data.
+  std::optional<DataBreakpointAccessType> accessType;
+
+  /// An expression for conditional breakpoints.
+  std::optional<std::string> condition;
+
+  /// An expression that controls how many hits of the breakpoint are ignored.
+  /// The debug adapter is expected to interpret the expression as needed.
+  std::optional<std::string> hitCondition;
+};
+bool fromJSON(const llvm::json::Value &, DataBreakpointInfo &,
+              llvm::json::Path);
+
+/// Properties of a breakpoint passed to the `setInstructionBreakpoints` request
+struct InstructionBreakpoint {
+  /// The instruction reference of the breakpoint.
+  /// This should be a memory or instruction pointer reference from an
+  /// `EvaluateResponse`, `Variable`, `StackFrame`, `GotoTarget`, or
+  /// `Breakpoint`.
+  std::string instructionReference;
+
+  /// The offset from the instruction reference in bytes.
+  /// This can be negative.
+  std::optional<int64_t> offset;
+
+  /// An expression for conditional breakpoints.
+  /// It is only honored by a debug adapter if the corresponding capability
+  /// `supportsConditionalBreakpoints` is true.
+  std::optional<std::string> condition;
+
+  /// An expression that controls how many hits of the breakpoint are ignored.
+  /// The debug adapter is expected to interpret the expression as needed.
+  /// The attribute is only honored by a debug adapter if the corresponding
+  /// capability `supportsHitConditionalBreakpoints` is true.
+  std::optional<std::string> hitCondition;
+
+  /// The mode of this breakpoint. If defined, this must be one of the
+  /// `breakpointModes` the debug adapter advertised in its `Capabilities`.
+  std::optional<std::string> mode;
+};
+bool fromJSON(const llvm::json::Value &, InstructionBreakpoint &,
+              llvm::json::Path);
+
+
 } // namespace lldb_dap::protocol
 
 #endif
diff --git a/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h
index 7f7f5f6228763..f1f4f4db709dd 100644
--- a/llvm/include/llvm/Support/JSON.h
+++ b/llvm/include/llvm/Support/JSON.h
@@ -776,6 +776,14 @@ inline bool fromJSON(const Value &E, bool &Out, Path P) {
   P.report("expected boolean");
   return false;
 }
+inline bool fromJSON(const Value &E, unsigned int &Out, Path P) {
+  if (auto S = E.getAsInteger()) {
+    Out = *S;
+    return true;
+  }
+  P.report("expected integer");
+  return false;
+}
 inline bool fromJSON(const Value &E, uint64_t &Out, Path P) {
   if (auto S = E.getAsUINT64()) {
     Out = *S;

>From fd473c70526e5dad0e1fa9ad7942705522d9cb5f Mon Sep 17 00:00:00 2001
From: Ely Ronnen <elyronnen at gmail.com>
Date: Sat, 26 Apr 2025 10:45:37 +0200
Subject: [PATCH 02/10] add all breakpoint requests JSON types

---
 .../lldb-dap/Protocol/ProtocolRequests.h      | 85 +++++++++++++++++++
 lldb/tools/lldb-dap/Protocol/ProtocolTypes.h  | 60 +++++++++++++
 2 files changed, 145 insertions(+)

diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index 33f93cc38799a..ae515bd186c76 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -377,6 +377,91 @@ bool fromJSON(const llvm::json::Value &, StepOutArguments &, llvm::json::Path);
 /// body field is required.
 using StepOutResponse = VoidResponse;
 
+/// Arguments for `setBreakpoints` request.
+struct SetBreakpointsArguments {
+  /// The source location of the breakpoints; either `source.path` or
+  /// `source.sourceReference` must be specified.
+  Source source;
+
+  /// The code locations of the breakpoints.
+  std::optional<std::vector<SourceBreakpoint>> breakpoints;
+
+  /// Deprecated: The code locations of the breakpoints.
+  std::optional<std::vector<uint32_t>> lines;
+
+  /// A value of true indicates that the underlying source has been modified
+  /// which results in new breakpoint locations.
+  std::optional<bool> sourceModified;
+};
+bool fromJSON(const llvm::json::Value &, SetBreakpointsArguments &,
+              llvm::json::Path);
+
+/// Response to `setBreakpoints` request.
+/// Returned is information about each breakpoint created by this request.
+/// This includes the actual code location and whether the breakpoint could be verified.
+/// The breakpoints returned are in the same order as the elements of the breakpoints
+/// (or the deprecated lines) array in the arguments.
+struct SetBreakpointsResponseBody {
+  /// Information about the breakpoints.
+  /// The array elements are in the same order as the elements of the
+  /// `breakpoints` (or the deprecated `lines`) array in the arguments.
+  std::vector<Breakpoint> breakpoints;
+};
+llvm::json::Value toJSON(const SetBreakpointsResponseBody &);
+
+/// Arguments for `setFunctionBreakpoints` request.
+struct SetFunctionBreakpointsArguments {
+  /// The function names of the breakpoints.
+  std::vector<FunctionBreakpoint> breakpoints;
+};
+bool fromJSON(const llvm::json::Value &, SetFunctionBreakpointsArguments &,
+              llvm::json::Path);
+
+/// Response to `setFunctionBreakpoints` request.
+/// Returned is information about each breakpoint created by this request.
+struct SetFunctionBreakpointsResponseBody {
+  /// Information about the breakpoints. The array elements correspond to the
+  /// elements of the `breakpoints` array.
+  std::vector<Breakpoint> breakpoints;
+};
+llvm::json::Value toJSON(const SetFunctionBreakpointsResponseBody &);
+
+/// Arguments for `setExceptionBreakpoints` request.
+struct SetExceptionBreakpointsArguments {
+  /// Set of exception filters specified by their ID. The set of all possible
+  /// exception filters is defined by the `exceptionBreakpointFilters`
+  /// capability. The `filter` and `filterOptions` sets are additive.
+  std::vector<std::string> filters;
+
+  /// Set of exception filters and their options. The set of all possible
+  /// exception filters is defined by the `exceptionBreakpointFilters`
+  /// capability. This attribute is only honored by a debug adapter if the
+  /// corresponding capability `supportsExceptionFilterOptions` is true. The
+  /// `filter` and `filterOptions` sets are additive.
+  std::optional<std::vector<ExceptionFilterOptions>> filterOptions;
+
+  /// Configuration options for selected exceptions.
+  /// The attribute is only honored by a debug adapter if the corresponding
+  /// capability `supportsExceptionOptions` is true.
+  std::optional<std::vector<ExceptionOptions>> exceptionOptions;
+};
+
+/// Arguments for `setInstructionBreakpoints` request.
+struct SetInstructionBreakpointsArguments {
+  /// The instruction references of the breakpoints.
+  std::vector<InstructionBreakpoint> breakpoints;
+};
+bool fromJSON(const llvm::json::Value &, SetInstructionBreakpointsArguments &,
+              llvm::json::Path);
+
+/// Response to `setInstructionBreakpoints` request.
+struct SetInstructionBreakpointsResponseBody {
+  /// Information about the breakpoints. The array elements correspond to the
+  /// elements of the `breakpoints` array.
+  std::vector<Breakpoint> breakpoints;
+};
+llvm::json::Value toJSON(const SetInstructionBreakpointsResponseBody &);
+
 } // namespace lldb_dap::protocol
 
 #endif
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index 8b193287acc19..f10adbac9e41c 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -499,6 +499,66 @@ struct InstructionBreakpoint {
 bool fromJSON(const llvm::json::Value &, InstructionBreakpoint &,
               llvm::json::Path);
 
+/// An ExceptionFilterOptions is used to specify an exception filter together with a condition for the `setExceptionBreakpoints` request.
+struct ExceptionFilterOptions {
+  /// ID of an exception filter returned by the `exceptionBreakpointFilters`
+  /// capability.
+  std::string filterId;
+
+  /// An expression for conditional exceptions.
+  /// The exception breaks into the debugger if the result of the condition is
+  /// true.
+  std::optional<std::string> condition;
+
+  /// The mode of this exception breakpoint. If defined, this must be one of the
+  /// `breakpointModes` the debug adapter advertised in its `Capabilities`.
+  std::optional<std::string> mode;
+};
+bool fromJSON(const llvm::json::Value &, ExceptionFilterOptions &,
+              llvm::json::Path);
+
+/// This enumeration defines all possible conditions when a thrown exception
+/// should result in a break.
+enum ExceptionBreakMode : unsigned {
+  /// Never breaks.
+  eExceptionBreakModeNever,
+  /// Always breaks.
+  eExceptionBreakModeAlways,
+  /// Breaks when the exception is unhandled.
+  eExceptionBreakModeUnhandled,
+  /// Breaks if the exception is not handled by user code.
+  eExceptionBreakModeUserUnhandled
+};
+bool fromJSON(const llvm::json::Value &, ExceptionBreakMode &,
+              llvm::json::Path);
+
+/// An ExceptionPathSegment represents a segment in a path that is used to
+/// match leafs or nodes in a tree of exceptions.
+struct ExceptionPathSegment {
+  /// If false or missing, this segment matches the names provided. Otherwise,
+  /// it matches anything except the names provided.
+  std::optional<bool> negate;
+
+  /// Depending on the value of `negate`, the names that should match or not
+  /// match.
+  std::vector<std::string> names;
+};
+bool fromJSON(const llvm::json::Value &, ExceptionPathSegment &,
+              llvm::json::Path);
+
+/// ExceptionOptions assigns configuration options to a set of exceptions.
+struct ExceptionOptions {
+  /// A path that selects a single or multiple exceptions in a tree. If `path`
+  /// is missing, the whole tree is selected.
+  /// By convention, the first segment of the path is a category that is used
+  /// to group exceptions in the UI.
+  std::optional<std::vector<ExceptionPathSegment>> path;
+
+  /// Condition when a thrown exception should result in a break.
+  ExceptionBreakMode breakMode;
+};
+bool fromJSON(const llvm::json::Value &, ExceptionOptions &,
+              llvm::json::Path);
 
 } // namespace lldb_dap::protocol
 

>From aeb9f4684e4b6a3b6d81eb81a26d6a8ddae6acfb Mon Sep 17 00:00:00 2001
From: Ely Ronnen <elyronnen at gmail.com>
Date: Sat, 26 Apr 2025 11:12:31 +0200
Subject: [PATCH 03/10] decouple JSON from DAP Breakpoint classes

---
 lldb/tools/lldb-dap/Breakpoint.h              |  4 ++-
 lldb/tools/lldb-dap/BreakpointBase.cpp        | 10 +++---
 lldb/tools/lldb-dap/BreakpointBase.h          |  3 +-
 lldb/tools/lldb-dap/FunctionBreakpoint.cpp    |  8 ++---
 lldb/tools/lldb-dap/FunctionBreakpoint.h      |  3 +-
 lldb/tools/lldb-dap/InstructionBreakpoint.cpp | 15 ++++----
 lldb/tools/lldb-dap/InstructionBreakpoint.h   |  4 ++-
 .../lldb-dap/Protocol/ProtocolRequests.h      |  6 ++--
 lldb/tools/lldb-dap/Protocol/ProtocolTypes.h  | 34 ++++++++++---------
 lldb/tools/lldb-dap/SourceBreakpoint.cpp      | 13 ++++---
 lldb/tools/lldb-dap/SourceBreakpoint.h        |  3 +-
 11 files changed, 55 insertions(+), 48 deletions(-)

diff --git a/lldb/tools/lldb-dap/Breakpoint.h b/lldb/tools/lldb-dap/Breakpoint.h
index 580017125af44..987dbeadd4659 100644
--- a/lldb/tools/lldb-dap/Breakpoint.h
+++ b/lldb/tools/lldb-dap/Breakpoint.h
@@ -17,7 +17,9 @@ namespace lldb_dap {
 
 class Breakpoint : public BreakpointBase {
 public:
-  Breakpoint(DAP &d, const llvm::json::Object &obj) : BreakpointBase(d, obj) {}
+  Breakpoint(DAP &d, const std::optional<std::string> &condition,
+             const std::optional<std::string> &hit_condition)
+      : BreakpointBase(d, condition, hit_condition) {}
   Breakpoint(DAP &d, lldb::SBBreakpoint bp) : BreakpointBase(d), m_bp(bp) {}
 
   lldb::break_id_t GetID() const { return m_bp.GetID(); }
diff --git a/lldb/tools/lldb-dap/BreakpointBase.cpp b/lldb/tools/lldb-dap/BreakpointBase.cpp
index 331ce8efee9bc..960dc5fc5ee56 100644
--- a/lldb/tools/lldb-dap/BreakpointBase.cpp
+++ b/lldb/tools/lldb-dap/BreakpointBase.cpp
@@ -12,11 +12,11 @@
 
 using namespace lldb_dap;
 
-BreakpointBase::BreakpointBase(DAP &d, const llvm::json::Object &obj)
-    : m_dap(d),
-      m_condition(std::string(GetString(obj, "condition").value_or(""))),
-      m_hit_condition(
-          std::string(GetString(obj, "hitCondition").value_or(""))) {}
+BreakpointBase::BreakpointBase(DAP &d,
+                               const std::optional<std::string> &condition,
+                               const std::optional<std::string> &hit_condition)
+    : m_dap(d), m_condition(condition.value_or("")),
+      m_hit_condition(hit_condition.value_or("")) {}
 
 void BreakpointBase::UpdateBreakpoint(const BreakpointBase &request_bp) {
   if (m_condition != request_bp.m_condition) {
diff --git a/lldb/tools/lldb-dap/BreakpointBase.h b/lldb/tools/lldb-dap/BreakpointBase.h
index 4c13326624831..e7aef44cc9d9a 100644
--- a/lldb/tools/lldb-dap/BreakpointBase.h
+++ b/lldb/tools/lldb-dap/BreakpointBase.h
@@ -18,7 +18,8 @@ namespace lldb_dap {
 class BreakpointBase {
 public:
   explicit BreakpointBase(DAP &d) : m_dap(d) {}
-  BreakpointBase(DAP &d, const llvm::json::Object &obj);
+  BreakpointBase(DAP &d, const std::optional<std::string> &condition,
+                 const std::optional<std::string> &hit_condition);
   virtual ~BreakpointBase() = default;
 
   virtual void SetCondition() = 0;
diff --git a/lldb/tools/lldb-dap/FunctionBreakpoint.cpp b/lldb/tools/lldb-dap/FunctionBreakpoint.cpp
index d87723f7557bd..1ea9cddb9f689 100644
--- a/lldb/tools/lldb-dap/FunctionBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/FunctionBreakpoint.cpp
@@ -8,15 +8,15 @@
 
 #include "FunctionBreakpoint.h"
 #include "DAP.h"
-#include "JSONUtils.h"
 #include "lldb/API/SBMutex.h"
 #include <mutex>
 
 namespace lldb_dap {
 
-FunctionBreakpoint::FunctionBreakpoint(DAP &d, const llvm::json::Object &obj)
-    : Breakpoint(d, obj),
-      m_function_name(std::string(GetString(obj, "name").value_or(""))) {}
+FunctionBreakpoint::FunctionBreakpoint(
+    DAP &d, const protocol::FunctionBreakpoint &breakpoint)
+    : Breakpoint(d, breakpoint.condition, breakpoint.hitCondition),
+      m_function_name(breakpoint.name) {}
 
 void FunctionBreakpoint::SetBreakpoint() {
   lldb::SBMutex lock = m_dap.GetAPIMutex();
diff --git a/lldb/tools/lldb-dap/FunctionBreakpoint.h b/lldb/tools/lldb-dap/FunctionBreakpoint.h
index 7100360cd7ec1..76fbdb3e886a4 100644
--- a/lldb/tools/lldb-dap/FunctionBreakpoint.h
+++ b/lldb/tools/lldb-dap/FunctionBreakpoint.h
@@ -11,12 +11,13 @@
 
 #include "Breakpoint.h"
 #include "DAPForward.h"
+#include "Protocol/ProtocolTypes.h"
 
 namespace lldb_dap {
 
 class FunctionBreakpoint : public Breakpoint {
 public:
-  FunctionBreakpoint(DAP &dap, const llvm::json::Object &obj);
+  FunctionBreakpoint(DAP &dap, const protocol::FunctionBreakpoint &breakpoint);
 
   /// Set this breakpoint in LLDB as a new breakpoint.
   void SetBreakpoint();
diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.cpp b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp
index dfdc6319ac9e8..ddae1a8b20243 100644
--- a/lldb/tools/lldb-dap/InstructionBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp
@@ -9,20 +9,19 @@
 
 #include "InstructionBreakpoint.h"
 #include "DAP.h"
-#include "JSONUtils.h"
 #include "lldb/API/SBBreakpoint.h"
 #include "lldb/API/SBTarget.h"
 #include "llvm/ADT/StringRef.h"
 
 namespace lldb_dap {
 
-InstructionBreakpoint::InstructionBreakpoint(DAP &d,
-                                             const llvm::json::Object &obj)
-    : Breakpoint(d, obj), m_instruction_address_reference(LLDB_INVALID_ADDRESS),
-      m_offset(GetInteger<int64_t>(obj, "offset").value_or(0)) {
-  GetString(obj, "instructionReference")
-      .value_or("")
-      .getAsInteger(0, m_instruction_address_reference);
+InstructionBreakpoint::InstructionBreakpoint(
+    DAP &d, const protocol::InstructionBreakpoint &breakpoint)
+    : Breakpoint(d, breakpoint.condition, breakpoint.hitCondition),
+      m_instruction_address_reference(LLDB_INVALID_ADDRESS),
+      m_offset(breakpoint.offset.value_or(0)) {
+  llvm::StringRef instruction_reference(breakpoint.instructionReference);
+  instruction_reference.getAsInteger(0, m_instruction_address_reference);
   m_instruction_address_reference += m_offset;
 }
 
diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.h b/lldb/tools/lldb-dap/InstructionBreakpoint.h
index 6ed980e00d038..a8c8f2113e5eb 100644
--- a/lldb/tools/lldb-dap/InstructionBreakpoint.h
+++ b/lldb/tools/lldb-dap/InstructionBreakpoint.h
@@ -12,6 +12,7 @@
 
 #include "Breakpoint.h"
 #include "DAPForward.h"
+#include "Protocol/ProtocolTypes.h"
 #include "lldb/lldb-types.h"
 #include <cstdint>
 
@@ -20,7 +21,8 @@ namespace lldb_dap {
 /// Instruction Breakpoint
 class InstructionBreakpoint : public Breakpoint {
 public:
-  InstructionBreakpoint(DAP &d, const llvm::json::Object &obj);
+  InstructionBreakpoint(DAP &d,
+                        const protocol::InstructionBreakpoint &breakpoint);
 
   /// Set instruction breakpoint in LLDB as a new breakpoint.
   void SetBreakpoint();
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index ae515bd186c76..b1a395ec4ae3d 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -398,9 +398,9 @@ bool fromJSON(const llvm::json::Value &, SetBreakpointsArguments &,
 
 /// Response to `setBreakpoints` request.
 /// Returned is information about each breakpoint created by this request.
-/// This includes the actual code location and whether the breakpoint could be verified.
-/// The breakpoints returned are in the same order as the elements of the breakpoints
-/// (or the deprecated lines) array in the arguments.
+/// This includes the actual code location and whether the breakpoint could be
+/// verified. The breakpoints returned are in the same order as the elements of
+/// the breakpoints (or the deprecated lines) array in the arguments.
 struct SetBreakpointsResponseBody {
   /// Information about the breakpoints.
   /// The array elements are in the same order as the elements of the
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index f10adbac9e41c..216dcd907ae15 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -322,7 +322,9 @@ enum SteppingGranularity : unsigned {
 bool fromJSON(const llvm::json::Value &, SteppingGranularity &,
               llvm::json::Path);
 
-/// Information about a breakpoint created in `setBreakpoints`, `setFunctionBreakpoints`, `setInstructionBreakpoints`, or `setDataBreakpoints` requests.
+/// Information about a breakpoint created in `setBreakpoints`,
+/// `setFunctionBreakpoints`, `setInstructionBreakpoints`, or
+/// `setDataBreakpoints` requests.
 struct Breakpoint {
   /// A machine-readable explanation of why a breakpoint may not be verified.
   enum class Reason : unsigned {
@@ -343,8 +345,8 @@ struct Breakpoint {
   bool verified;
 
   /// A message about the state of the breakpoint.
-  /// This is shown to the user and can be used to explain why a breakpoint could
-  /// not be verified.
+  /// This is shown to the user and can be used to explain why a breakpoint
+  /// could not be verified.
   std::optional<std::string> message;
 
   /// The source where the breakpoint is located.
@@ -362,10 +364,9 @@ struct Breakpoint {
   std::optional<uint32_t> endLine;
 
   /// End position of the source range covered by the breakpoint. It is measured
-  /// in UTF-16 code units and the client capability `columnsStartAt1` determines
-  /// whether it is 0- or 1-based.
-  /// If no end line is given, then the end column is assumed to be in the start
-  /// line.
+  /// in UTF-16 code units and the client capability `columnsStartAt1`
+  /// determines whether it is 0- or 1-based. If no end line is given, then the
+  /// end column is assumed to be in the start line.
   std::optional<uint32_t> endColumn;
 
   /// A memory reference to where the breakpoint is set.
@@ -373,7 +374,7 @@ struct Breakpoint {
 
   /// The offset from the instruction reference.
   /// This can be negative.
-  std::optional<int64_t> offset;
+  std::optional<int32_t> offset;
 
   /// A machine-readable explanation of why a breakpoint may not be verified. If
   /// a breakpoint is verified or a specific reason is not known, the adapter
@@ -382,7 +383,8 @@ struct Breakpoint {
 };
 llvm::json::Value toJSON(const Breakpoint &);
 
-/// Properties of a breakpoint or logpoint passed to the `setBreakpoints` request
+/// Properties of a breakpoint or logpoint passed to the `setBreakpoints`
+/// request
 struct SourceBreakpoint {
   /// The source line of the breakpoint or logpoint.
   uint32_t line;
@@ -419,8 +421,7 @@ struct SourceBreakpoint {
   /// `breakpointModes` the debug adapter advertised in its `Capabilities`.
   std::optional<std::string> mode;
 };
-bool fromJSON(const llvm::json::Value &, SourceBreakpoint &,
-              llvm::json::Path);
+bool fromJSON(const llvm::json::Value &, SourceBreakpoint &, llvm::json::Path);
 
 /// Properties of a breakpoint passed to the `setFunctionBreakpoints` request.
 struct FunctionBreakpoint {
@@ -441,7 +442,8 @@ struct FunctionBreakpoint {
 bool fromJSON(const llvm::json::Value &, FunctionBreakpoint &,
               llvm::json::Path);
 
-/// This enumeration defines all possible access types for data breakpoints. Values: ‘read’, ‘write’, ‘readWrite’
+/// This enumeration defines all possible access types for data breakpoints.
+/// Values: ‘read’, ‘write’, ‘readWrite’
 enum DataBreakpointAccessType : unsigned {
   eDataBreakpointAccessTypeRead,
   eDataBreakpointAccessTypeWrite,
@@ -479,7 +481,7 @@ struct InstructionBreakpoint {
 
   /// The offset from the instruction reference in bytes.
   /// This can be negative.
-  std::optional<int64_t> offset;
+  std::optional<int32_t> offset;
 
   /// An expression for conditional breakpoints.
   /// It is only honored by a debug adapter if the corresponding capability
@@ -499,7 +501,8 @@ struct InstructionBreakpoint {
 bool fromJSON(const llvm::json::Value &, InstructionBreakpoint &,
               llvm::json::Path);
 
-/// An ExceptionFilterOptions is used to specify an exception filter together with a condition for the `setExceptionBreakpoints` request.
+/// An ExceptionFilterOptions is used to specify an exception filter together
+/// with a condition for the `setExceptionBreakpoints` request.
 struct ExceptionFilterOptions {
   /// ID of an exception filter returned by the `exceptionBreakpointFilters`
   /// capability.
@@ -557,8 +560,7 @@ struct ExceptionOptions {
   /// Condition when a thrown exception should result in a break.
   ExceptionBreakMode breakMode;
 };
-bool fromJSON(const llvm::json::Value &, ExceptionOptions &,
-              llvm::json::Path);
+bool fromJSON(const llvm::json::Value &, ExceptionOptions &, llvm::json::Path);
 
 } // namespace lldb_dap::protocol
 
diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
index a7e00cae36fbc..4581c995b4260 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
@@ -26,13 +26,12 @@
 
 namespace lldb_dap {
 
-SourceBreakpoint::SourceBreakpoint(DAP &dap, const llvm::json::Object &obj)
-    : Breakpoint(dap, obj),
-      m_log_message(GetString(obj, "logMessage").value_or("").str()),
-      m_line(
-          GetInteger<uint64_t>(obj, "line").value_or(LLDB_INVALID_LINE_NUMBER)),
-      m_column(GetInteger<uint64_t>(obj, "column")
-                   .value_or(LLDB_INVALID_COLUMN_NUMBER)) {}
+SourceBreakpoint::SourceBreakpoint(DAP &dap,
+                                   const protocol::SourceBreakpoint &breakpoint)
+    : Breakpoint(dap, breakpoint.condition, breakpoint.hitCondition),
+      m_log_message(breakpoint.logMessage.value_or("")),
+      m_line(breakpoint.line),
+      m_column(breakpoint.column.value_or(LLDB_INVALID_COLUMN_NUMBER)) {}
 
 void SourceBreakpoint::SetBreakpoint(const llvm::StringRef source_path) {
   lldb::SBMutex lock = m_dap.GetAPIMutex();
diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.h b/lldb/tools/lldb-dap/SourceBreakpoint.h
index d01411547d12a..5b15296f861c5 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.h
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.h
@@ -11,6 +11,7 @@
 
 #include "Breakpoint.h"
 #include "DAPForward.h"
+#include "Protocol/ProtocolTypes.h"
 #include "lldb/API/SBError.h"
 #include "llvm/ADT/StringRef.h"
 #include <cstdint>
@@ -21,7 +22,7 @@ namespace lldb_dap {
 
 class SourceBreakpoint : public Breakpoint {
 public:
-  SourceBreakpoint(DAP &d, const llvm::json::Object &obj);
+  SourceBreakpoint(DAP &d, const protocol::SourceBreakpoint &breakpoint);
 
   // Set this breakpoint in LLDB as a new breakpoint
   void SetBreakpoint(const llvm::StringRef source_path);

>From 1fe309fa7fe9586176f41b0006c380069c44fea9 Mon Sep 17 00:00:00 2001
From: Ely Ronnen <elyronnen at gmail.com>
Date: Sat, 26 Apr 2025 11:23:51 +0200
Subject: [PATCH 04/10] forgor exception breakpoint response

---
 lldb/tools/lldb-dap/BreakpointBase.h          |   2 +-
 .../lldb-dap/Protocol/ProtocolRequests.h      | 110 ++++++++++++++++++
 2 files changed, 111 insertions(+), 1 deletion(-)

diff --git a/lldb/tools/lldb-dap/BreakpointBase.h b/lldb/tools/lldb-dap/BreakpointBase.h
index e7aef44cc9d9a..44f3bfa15afc3 100644
--- a/lldb/tools/lldb-dap/BreakpointBase.h
+++ b/lldb/tools/lldb-dap/BreakpointBase.h
@@ -10,7 +10,7 @@
 #define LLDB_TOOLS_LLDB_DAP_BREAKPOINTBASE_H
 
 #include "DAPForward.h"
-#include "llvm/ADT/StringRef.h"
+#include <optional>
 #include <string>
 
 namespace lldb_dap {
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index b1a395ec4ae3d..7bcb8212575f4 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -445,6 +445,34 @@ struct SetExceptionBreakpointsArguments {
   /// capability `supportsExceptionOptions` is true.
   std::optional<std::vector<ExceptionOptions>> exceptionOptions;
 };
+bool fromJSON(const llvm::json::Value &, SetExceptionBreakpointsArguments &,
+              llvm::json::Path);
+
+/// Response to `setExceptionBreakpoints` request.
+/// The response contains an array of Breakpoint objects with information about
+/// each exception breakpoint or filter. The Breakpoint objects are in the same
+/// order as the elements of the filters, filterOptions, exceptionOptions arrays
+/// given as arguments. If both filters and filterOptions are given, the
+/// returned array must start with filters information first, followed by
+/// filterOptions information.
+/// The verified property of a Breakpoint object signals whether the exception
+/// breakpoint or filter could be successfully created and whether the condition
+/// is valid. In case of an error the message property explains the problem. The
+/// id property can be used to introduce a unique ID for the exception
+/// breakpoint or filter so that it can be updated subsequently by sending
+/// breakpoint events. For backward compatibility both the breakpoints array and
+/// the enclosing body are optional. If these elements are missing a client is
+/// not able to show problems for individual exception breakpoints or filters.
+struct SetExceptionBreakpointsResponseBody {
+  /// Information about the exception breakpoints or filters.
+  /// The breakpoints returned are in the same order as the elements of the
+  /// `filters`, `filterOptions`, `exceptionOptions` arrays in the arguments.
+  /// If both `filters` and `filterOptions` are given, the returned array must
+  /// start with `filters` information first, followed by `filterOptions`
+  /// information.
+  std::vector<Breakpoint> breakpoints;
+};
+llvm::json::Value toJSON(const SetExceptionBreakpointsResponseBody &);
 
 /// Arguments for `setInstructionBreakpoints` request.
 struct SetInstructionBreakpointsArguments {
@@ -462,6 +490,88 @@ struct SetInstructionBreakpointsResponseBody {
 };
 llvm::json::Value toJSON(const SetInstructionBreakpointsResponseBody &);
 
+/// Arguments for `dataBreakpointInfo` request.
+struct DataBreakpointInfoArguments {
+  /// Reference to the variable container if the data breakpoint is requested
+  /// for a child of the container. The `variablesReference` must have been
+  /// obtained in the current suspended state.See 'Lifetime of Object
+  /// References' in the Overview section for details.
+  std::optional<int64_t> variablesReference;
+
+  /// The name of the variable's child to obtain data breakpoint information
+  /// for. If `variablesReference` isn't specified, this can be an expression,
+  /// or an address if `asAddress` is also true.
+  std::string name;
+
+  /// When `name` is an expression, evaluate it in the scope of this stack
+  /// frame. If not specified, the expression is evaluated in the global scope.
+  /// When `asAddress` is true, the `frameId` is ignored.
+  std::optional<int64_t> frameId;
+
+  /// If specified, a debug adapter should return information for the range of
+  /// memory extending `bytes` number of bytes from the address or variable
+  /// specified by `name`. Breakpoints set using the resulting data ID should
+  /// pause on data access anywhere within that range.
+  /// Clients may set this property only if the `supportsDataBreakpointBytes`
+  /// capability is true.
+  std::optional<int64_t> bytes;
+
+  /// If `true`, the `name` is a memory address and the debugger should
+  /// interpret it as a decimal value, or hex value if it is prefixed with `0x`.
+  /// Clients may set this property only if the `supportsDataBreakpointBytes`
+  /// capability is true.
+  std::optional<bool> asAddress;
+
+  /// The mode of the desired breakpoint. If defined, this must be one of the
+  /// `breakpointModes` the debug adapter advertised in its `Capabilities`.
+  std::optional<std::string> mode;
+};
+bool fromJSON(const llvm::json::Value &, DataBreakpointInfoArguments &,
+              llvm::json::Path);
+
+/// Response to `dataBreakpointInfo` request.
+struct DataBreakpointInfoResponseBody {
+  /// An identifier for the data on which a data breakpoint can be registered
+  /// with the `setDataBreakpoints` request or null if no data breakpoint is
+  /// available. If a `variablesReference` or `frameId` is passed, the `dataId`
+  /// is valid in the current suspended state, otherwise it's valid
+  /// indefinitely. See 'Lifetime of Object References' in the Overview section
+  /// for details. Breakpoints set using the `dataId` in the
+  /// `setDataBreakpoints` request may outlive the lifetime of the associated
+  /// `dataId`.
+  std::optional<std::string> dataId;
+
+  /// UI string that describes on what data the breakpoint is set on or why a
+  /// data breakpoint is not available.
+  std::string description;
+
+  /// Attribute lists the available access types for a potential data
+  /// breakpoint. A UI client could surface this information.
+  std::optional<std::vector<DataBreakpointAccessType>> accessTypes;
+
+  /// Attribute indicates that a potential data breakpoint could be persisted
+  /// across sessions.
+  std::optional<bool> canPersist;
+};
+llvm::json::Value toJSON(const DataBreakpointInfoResponseBody &);
+
+/// Arguments for `setDataBreakpoints` request.
+struct SetDataBreakpointsArguments {
+  /// The contents of this array replaces all existing data breakpoints. An
+  /// empty array clears all data breakpoints.
+  std::vector<DataBreakpointInfo> breakpoints;
+};
+bool fromJSON(const llvm::json::Value &, SetDataBreakpointsArguments &,
+              llvm::json::Path);
+
+/// Response to `setDataBreakpoints` request.
+struct SetDataBreakpointsResponseBody {
+  /// Information about the data breakpoints. The array elements correspond to
+  /// the elements of the input argument `breakpoints` array.
+  std::vector<Breakpoint> breakpoints;
+};
+llvm::json::Value toJSON(const SetDataBreakpointsResponseBody &);
+
 } // namespace lldb_dap::protocol
 
 #endif

>From 1c18401f842cb7c25a70dc8dda7081e11630dcf0 Mon Sep 17 00:00:00 2001
From: Ely Ronnen <elyronnen at gmail.com>
Date: Sat, 26 Apr 2025 12:21:10 +0200
Subject: [PATCH 05/10] migrating breakpoint requests

---
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |  60 +++++---
 .../tools/lldb-dap/Protocol/ProtocolTypes.cpp | 141 ++++++++++++++++++
 lldb/tools/lldb-dap/Protocol/ProtocolTypes.h  |   1 +
 3 files changed, 184 insertions(+), 18 deletions(-)

diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index fa3d76ed4a125..8bfe42d9d3fde 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -339,64 +339,88 @@ class StepOutRequestHandler : public RequestHandler<protocol::StepOutArguments,
   llvm::Error Run(const protocol::StepOutArguments &args) const override;
 };
 
-class SetBreakpointsRequestHandler : public LegacyRequestHandler {
+class SetBreakpointsRequestHandler
+    : public RequestHandler<
+          protocol::SetBreakpointsArguments,
+          llvm::Expected<protocol::SetBreakpointsResponseBody>> {
 public:
-  using LegacyRequestHandler::LegacyRequestHandler;
+  using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() { return "setBreakpoints"; }
   FeatureSet GetSupportedFeatures() const override {
     return {protocol::eAdapterFeatureConditionalBreakpoints,
             protocol::eAdapterFeatureHitConditionalBreakpoints};
   }
-  void operator()(const llvm::json::Object &request) const override;
+  llvm::Expected<protocol::SetBreakpointsResponseBody>
+  Run(const protocol::SetBreakpointsArguments &args) const override;
 };
 
-class SetExceptionBreakpointsRequestHandler : public LegacyRequestHandler {
+class SetExceptionBreakpointsRequestHandler
+    : public RequestHandler<
+          protocol::SetExceptionBreakpointsArguments,
+          llvm::Expected<protocol::SetExceptionBreakpointsResponseBody>> {
 public:
-  using LegacyRequestHandler::LegacyRequestHandler;
+  using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() { return "setExceptionBreakpoints"; }
   FeatureSet GetSupportedFeatures() const override {
     return {protocol::eAdapterFeatureExceptionOptions};
   }
-  void operator()(const llvm::json::Object &request) const override;
+  llvm::Expected<protocol::SetExceptionBreakpointsResponseBody>
+  Run(const protocol::SetExceptionBreakpointsArguments &args) const override;
 };
 
-class SetFunctionBreakpointsRequestHandler : public LegacyRequestHandler {
+class SetFunctionBreakpointsRequestHandler
+    : public RequestHandler<
+          protocol::SetFunctionBreakpointsArguments,
+          llvm::Expected<protocol::SetFunctionBreakpointsResponseBody>> {
 public:
-  using LegacyRequestHandler::LegacyRequestHandler;
+  using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() { return "setFunctionBreakpoints"; }
   FeatureSet GetSupportedFeatures() const override {
     return {protocol::eAdapterFeatureFunctionBreakpoints};
   }
-  void operator()(const llvm::json::Object &request) const override;
+  llvm::Expected<protocol::SetFunctionBreakpointsResponseBody>
+  Run(const protocol::SetFunctionBreakpointsArguments &args) const override;
 };
 
-class DataBreakpointInfoRequestHandler : public LegacyRequestHandler {
+class DataBreakpointInfoRequestHandler
+    : public RequestHandler<
+          protocol::DataBreakpointInfoArguments,
+          llvm::Expected<protocol::DataBreakpointInfoResponseBody>> {
 public:
-  using LegacyRequestHandler::LegacyRequestHandler;
+  using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() { return "dataBreakpointInfo"; }
-  void operator()(const llvm::json::Object &request) const override;
+  llvm::Expected<protocol::DataBreakpointInfoResponseBody>
+  Run(const protocol::DataBreakpointInfoArguments &args) const override;
 };
 
-class SetDataBreakpointsRequestHandler : public LegacyRequestHandler {
+class SetDataBreakpointsRequestHandler
+    : public RequestHandler<
+          protocol::SetDataBreakpointsArguments,
+          llvm::Expected<protocol::SetDataBreakpointsResponseBody>> {
 public:
-  using LegacyRequestHandler::LegacyRequestHandler;
+  using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() { return "setDataBreakpoints"; }
-  void operator()(const llvm::json::Object &request) const override;
   FeatureSet GetSupportedFeatures() const override {
     return {protocol::eAdapterFeatureDataBreakpoints};
   }
+  llvm::Expected<protocol::SetDataBreakpointsResponseBody>
+  Run(const protocol::SetDataBreakpointsArguments &args) const override;
 };
 
-class SetInstructionBreakpointsRequestHandler : public LegacyRequestHandler {
+class SetInstructionBreakpointsRequestHandler
+    : public RequestHandler<
+          protocol::SetInstructionBreakpointsArguments,
+          llvm::Expected<protocol::SetInstructionBreakpointsResponseBody>> {
 public:
-  using LegacyRequestHandler::LegacyRequestHandler;
+  using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() {
     return "setInstructionBreakpoints";
   }
-  void operator()(const llvm::json::Object &request) const override;
   FeatureSet GetSupportedFeatures() const override {
     return {protocol::eAdapterFeatureInstructionBreakpoints};
   }
+  llvm::Expected<protocol::SetInstructionBreakpointsResponseBody>
+  Run(const protocol::SetInstructionBreakpointsArguments &args) const override;
 };
 
 class CompileUnitsRequestHandler : public LegacyRequestHandler {
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index e64998c4ca488..eaad009150497 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -43,6 +43,17 @@ bool fromJSON(const json::Value &Params, Source &S, json::Path P) {
          O.map("sourceReference", S.sourceReference);
 }
 
+llvm::json::Value toJSON(const Source &S) {
+  json::Object result{
+      {"name", S.name},
+      {"path", S.path},
+      {"sourceReference", S.sourceReference},
+      {"presentationHint", S.presentationHint},
+  };
+
+  return result;
+}
+
 json::Value toJSON(const ExceptionBreakpointsFilter &EBF) {
   json::Object result{{"filter", EBF.filter}, {"label", EBF.label}};
 
@@ -254,4 +265,134 @@ bool fromJSON(const llvm::json::Value &Params, SteppingGranularity &SG,
   return true;
 }
 
+json::Value toJSON(const Breakpoint &BP) {
+  json::Object result{{"verified", BP.verified}};
+
+  if (BP.id)
+    result.insert({"id", *BP.id});
+  if (BP.message)
+    result.insert({"message", *BP.message});
+  if (BP.source)
+    result.insert({"source", *BP.source});
+  if (BP.line)
+    result.insert({"line", *BP.line});
+  if (BP.column)
+    result.insert({"column", *BP.column});
+  if (BP.endLine)
+    result.insert({"endLine", *BP.endLine});
+  if (BP.endColumn)
+    result.insert({"endColumn", *BP.endColumn});
+  if (BP.instructionReference)
+    result.insert({"instructionReference", *BP.instructionReference});
+  if (BP.offset)
+    result.insert({"offset", *BP.offset});
+  if (BP.reason) {
+    switch (*BP.reason) {
+    case Breakpoint::Reason::eBreakpointReasonPending:
+      result.insert({"reason", "pending"});
+      break;
+    case Breakpoint::Reason::eBreakpointReasonFailed:
+      result.insert({"reason", "failed"});
+      break;
+    }
+  }
+
+  return result;
+}
+
+bool fromJSON(const llvm::json::Value &Params, ExceptionBreakMode &EBM,
+              llvm::json::Path P) {
+  auto rawMode = Params.getAsString();
+  if (!rawMode) {
+    P.report("expected a string");
+    return false;
+  }
+  std::optional<ExceptionBreakMode> mode =
+      StringSwitch<std::optional<ExceptionBreakMode>>(*rawMode)
+          .Case("never", eExceptionBreakModeNever)
+          .Case("always", eExceptionBreakModeAlways)
+          .Case("unhandled", eExceptionBreakModeUnhandled)
+          .Case("userUnhandled", eExceptionBreakModeUserUnhandled)
+          .Default(std::nullopt);
+  if (!mode) {
+    P.report("unexpected ExceptionBreakMode value");
+    return false;
+  }
+  EBM = *mode;
+  return true;
+}
+
+bool fromJSON(const llvm::json::Value &Params, ExceptionPathSegment &EPS,
+              llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("negate", EPS.negate) && O.map("names", EPS.names);
+}
+
+bool fromJSON(const llvm::json::Value &Params, ExceptionOptions &EO,
+              llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("path", EO.path) && O.map("breakMode", EO.breakMode);
+}
+
+bool fromJSON(const llvm::json::Value &Params, ExceptionFilterOptions &EFO,
+              llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("filterId", EFO.filterId) &&
+         O.map("condition", EFO.condition) && O.map("mode", EFO.mode);
+}
+
+bool fromJSON(const llvm::json::Value &Params, SourceBreakpoint &SB,
+              llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("line", SB.line) && O.map("column", SB.column) &&
+         O.map("condition", SB.condition) &&
+         O.map("hitCondition", SB.hitCondition) &&
+         O.map("logMessage", SB.logMessage) && O.map("mode", SB.mode);
+}
+
+bool fromJSON(const llvm::json::Value &Params, FunctionBreakpoint &FB,
+              llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("name", FB.name) && O.map("condition", FB.condition) &&
+         O.map("hitCondition", FB.hitCondition);
+}
+
+bool fromJSON(const llvm::json::Value &Params, DataBreakpointAccessType &DBAT,
+              llvm::json::Path P) {
+  auto rawAccessType = Params.getAsString();
+  if (!rawAccessType) {
+    P.report("expected a string");
+    return false;
+  }
+  std::optional<DataBreakpointAccessType> accessType =
+      StringSwitch<std::optional<DataBreakpointAccessType>>(*rawAccessType)
+          .Case("read", eDataBreakpointAccessTypeRead)
+          .Case("write", eDataBreakpointAccessTypeWrite)
+          .Case("readWrite", eDataBreakpointAccessTypeReadWrite)
+          .Default(std::nullopt);
+  if (!accessType) {
+    P.report("unexpected value, expected 'read', 'write', or 'readWrite'");
+    return false;
+  }
+  DBAT = *accessType;
+  return true;
+}
+
+bool fromJSON(const llvm::json::Value &Params, DataBreakpointInfo &DBI,
+              llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("dataId", DBI.dataId) &&
+         O.map("accessType", DBI.accessType) &&
+         O.map("condition", DBI.condition) &&
+         O.map("hitCondition", DBI.hitCondition);
+}
+
+bool fromJSON(const llvm::json::Value &Params, InstructionBreakpoint &IB,
+              llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("instructionReference", IB.instructionReference) &&
+         O.map("offset", IB.offset) && O.map("condition", IB.condition) &&
+         O.map("hitCondition", IB.hitCondition) && O.map("mode", IB.mode);
+}
+
 } // namespace lldb_dap::protocol
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index 216dcd907ae15..ff288a1be776b 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -302,6 +302,7 @@ struct Source {
   // unsupported keys: origin, sources, adapterData, checksums
 };
 bool fromJSON(const llvm::json::Value &, Source &, llvm::json::Path);
+llvm::json::Value toJSON(const Source &);
 
 /// The granularity of one `step` in the stepping requests `next`, `stepIn`,
 /// `stepOut` and `stepBack`.

>From daff2ace80af46947b163b223b6f6f247e04bdbd Mon Sep 17 00:00:00 2001
From: Ely Ronnen <elyronnen at gmail.com>
Date: Sat, 26 Apr 2025 13:20:34 +0200
Subject: [PATCH 06/10] migrate breakpoints requests handler implementations

---
 lldb/tools/lldb-dap/Breakpoint.cpp            |  22 +-
 lldb/tools/lldb-dap/Breakpoint.h              |   3 +-
 lldb/tools/lldb-dap/BreakpointBase.cpp        |   2 -
 lldb/tools/lldb-dap/BreakpointBase.h          |   3 +-
 .../Handler/SetBreakpointsRequestHandler.cpp  | 177 +++-----------
 .../SetDataBreakpointsRequestHandler.cpp      |  98 ++------
 .../SetExceptionBreakpointsRequestHandler.cpp |  71 ++----
 .../SetFunctionBreakpointsRequestHandler.cpp  | 114 ++-------
 ...etInstructionBreakpointsRequestHandler.cpp | 222 ++----------------
 lldb/tools/lldb-dap/JSONUtils.cpp             | 165 ++-----------
 lldb/tools/lldb-dap/JSONUtils.h               |  36 +--
 lldb/tools/lldb-dap/Watchpoint.cpp            |  24 +-
 lldb/tools/lldb-dap/Watchpoint.h              |   6 +-
 13 files changed, 162 insertions(+), 781 deletions(-)

diff --git a/lldb/tools/lldb-dap/Breakpoint.cpp b/lldb/tools/lldb-dap/Breakpoint.cpp
index 5679fd545d53f..26d633d1d172e 100644
--- a/lldb/tools/lldb-dap/Breakpoint.cpp
+++ b/lldb/tools/lldb-dap/Breakpoint.cpp
@@ -14,7 +14,6 @@
 #include "lldb/API/SBLineEntry.h"
 #include "lldb/API/SBMutex.h"
 #include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/JSON.h"
 #include <cstddef>
 #include <cstdint>
 #include <mutex>
@@ -30,13 +29,16 @@ void Breakpoint::SetHitCondition() {
     m_bp.SetIgnoreCount(hitCount - 1);
 }
 
-void Breakpoint::CreateJsonObject(llvm::json::Object &object) {
+protocol::Breakpoint Breakpoint::ToProtocolBreakpoint() {
+  protocol::Breakpoint breakpoint;
+
   // Each breakpoint location is treated as a separate breakpoint for VS code.
   // They don't have the notion of a single breakpoint with multiple locations.
   if (!m_bp.IsValid())
-    return;
-  object.try_emplace("verified", m_bp.GetNumResolvedLocations() > 0);
-  object.try_emplace("id", m_bp.GetID());
+    return breakpoint;
+
+  breakpoint.verified = m_bp.GetNumResolvedLocations() > 0;
+  breakpoint.id = m_bp.GetID();
   // VS Code DAP doesn't currently allow one breakpoint to have multiple
   // locations so we just report the first one. If we report all locations
   // then the IDE starts showing the wrong line numbers and locations for
@@ -60,16 +62,18 @@ void Breakpoint::CreateJsonObject(llvm::json::Object &object) {
   if (bp_addr.IsValid()) {
     std::string formatted_addr =
         "0x" + llvm::utohexstr(bp_addr.GetLoadAddress(m_bp.GetTarget()));
-    object.try_emplace("instructionReference", formatted_addr);
+    breakpoint.instructionReference = formatted_addr;
     auto line_entry = bp_addr.GetLineEntry();
     const auto line = line_entry.GetLine();
     if (line != UINT32_MAX)
-      object.try_emplace("line", line);
+      breakpoint.line = line;
     const auto column = line_entry.GetColumn();
     if (column != 0)
-      object.try_emplace("column", column);
-    object.try_emplace("source", CreateSource(line_entry));
+      breakpoint.column = column;
+    breakpoint.source = CreateSource(line_entry);
   }
+
+  return breakpoint;
 }
 
 bool Breakpoint::MatchesName(const char *name) {
diff --git a/lldb/tools/lldb-dap/Breakpoint.h b/lldb/tools/lldb-dap/Breakpoint.h
index 987dbeadd4659..4f98e5537cda5 100644
--- a/lldb/tools/lldb-dap/Breakpoint.h
+++ b/lldb/tools/lldb-dap/Breakpoint.h
@@ -26,7 +26,8 @@ class Breakpoint : public BreakpointBase {
 
   void SetCondition() override;
   void SetHitCondition() override;
-  void CreateJsonObject(llvm::json::Object &object) override;
+
+  protocol::Breakpoint ToProtocolBreakpoint() override;
 
   bool MatchesName(const char *name);
   void SetBreakpoint();
diff --git a/lldb/tools/lldb-dap/BreakpointBase.cpp b/lldb/tools/lldb-dap/BreakpointBase.cpp
index 960dc5fc5ee56..1a4da92e44d31 100644
--- a/lldb/tools/lldb-dap/BreakpointBase.cpp
+++ b/lldb/tools/lldb-dap/BreakpointBase.cpp
@@ -7,8 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "BreakpointBase.h"
-#include "JSONUtils.h"
-#include "llvm/ADT/StringRef.h"
 
 using namespace lldb_dap;
 
diff --git a/lldb/tools/lldb-dap/BreakpointBase.h b/lldb/tools/lldb-dap/BreakpointBase.h
index 44f3bfa15afc3..e9cfc112675c3 100644
--- a/lldb/tools/lldb-dap/BreakpointBase.h
+++ b/lldb/tools/lldb-dap/BreakpointBase.h
@@ -10,6 +10,7 @@
 #define LLDB_TOOLS_LLDB_DAP_BREAKPOINTBASE_H
 
 #include "DAPForward.h"
+#include "Protocol/ProtocolTypes.h"
 #include <optional>
 #include <string>
 
@@ -24,7 +25,7 @@ class BreakpointBase {
 
   virtual void SetCondition() = 0;
   virtual void SetHitCondition() = 0;
-  virtual void CreateJsonObject(llvm::json::Object &object) = 0;
+  virtual protocol::Breakpoint ToProtocolBreakpoint() = 0;
 
   void UpdateBreakpoint(const BreakpointBase &request_bp);
 
diff --git a/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp
index dc0368852101f..0fc5792d0a7e7 100644
--- a/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp
@@ -9,153 +9,49 @@
 #include "DAP.h"
 #include "EventHelper.h"
 #include "JSONUtils.h"
+#include "Protocol/ProtocolRequests.h"
 #include "RequestHandler.h"
+#include <vector>
 
 namespace lldb_dap {
 
-// "SetBreakpointsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "SetBreakpoints request; value of command field is
-//     'setBreakpoints'. Sets multiple breakpoints for a single source and
-//     clears all previous breakpoints in that source. To clear all breakpoint
-//     for a source, specify an empty array. When a breakpoint is hit, a
-//     StoppedEvent (event type 'breakpoint') is generated.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "setBreakpoints" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/SetBreakpointsArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "SetBreakpointsArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'setBreakpoints' request.",
-//   "properties": {
-//     "source": {
-//       "$ref": "#/definitions/Source",
-//       "description": "The source location of the breakpoints; either
-//       source.path or source.reference must be specified."
-//     },
-//     "breakpoints": {
-//       "type": "array",
-//       "items": {
-//         "$ref": "#/definitions/SourceBreakpoint"
-//       },
-//       "description": "The code locations of the breakpoints."
-//     },
-//     "lines": {
-//       "type": "array",
-//       "items": {
-//         "type": "integer"
-//       },
-//       "description": "Deprecated: The code locations of the breakpoints."
-//     },
-//     "sourceModified": {
-//       "type": "boolean",
-//       "description": "A value of true indicates that the underlying source
-//       has been modified which results in new breakpoint locations."
-//     }
-//   },
-//   "required": [ "source" ]
-// },
-// "SetBreakpointsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'setBreakpoints' request. Returned is
-//     information about each breakpoint created by this request. This includes
-//     the actual code location and whether the breakpoint could be verified.
-//     The breakpoints returned are in the same order as the elements of the
-//     'breakpoints' (or the deprecated 'lines') in the
-//     SetBreakpointsArguments.", "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "breakpoints": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/Breakpoint"
-//             },
-//             "description": "Information about the breakpoints. The array
-//             elements are in the same order as the elements of the
-//             'breakpoints' (or the deprecated 'lines') in the
-//             SetBreakpointsArguments."
-//           }
-//         },
-//         "required": [ "breakpoints" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// },
-// "SourceBreakpoint": {
-//   "type": "object",
-//   "description": "Properties of a breakpoint or logpoint passed to the
-//   setBreakpoints request.", "properties": {
-//     "line": {
-//       "type": "integer",
-//       "description": "The source line of the breakpoint or logpoint."
-//     },
-//     "column": {
-//       "type": "integer",
-//       "description": "An optional source column of the breakpoint."
-//     },
-//     "condition": {
-//       "type": "string",
-//       "description": "An optional expression for conditional breakpoints."
-//     },
-//     "hitCondition": {
-//       "type": "string",
-//       "description": "An optional expression that controls how many hits of
-//       the breakpoint are ignored. The backend is expected to interpret the
-//       expression as needed."
-//     },
-//     "logMessage": {
-//       "type": "string",
-//       "description": "If this attribute exists and is non-empty, the backend
-//       must not 'break' (stop) but log the message instead. Expressions within
-//       {} are interpolated."
-//     }
-//   },
-//   "required": [ "line" ]
-// }
-void SetBreakpointsRequestHandler::operator()(
-    const llvm::json::Object &request) const {
-  llvm::json::Object response;
-  lldb::SBError error;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  const auto *source = arguments->getObject("source");
-  const auto path = GetString(source, "path").value_or("");
-  const auto *breakpoints = arguments->getArray("breakpoints");
-  llvm::json::Array response_breakpoints;
+/// Sets multiple breakpoints for a single source and clears all previous
+/// breakpoints in that source. To clear all breakpoint for a source, specify an
+/// empty array. When a breakpoint is hit, a `stopped` event (with reason
+/// `breakpoint`) is generated.
+llvm::Expected<protocol::SetBreakpointsResponseBody>
+SetBreakpointsRequestHandler::Run(
+    const protocol::SetBreakpointsArguments &args) const {
+  const auto &source = args.source;
+  const auto path = source.path.value_or("");
+  std::vector<protocol::Breakpoint> response_breakpoints;
 
   // Decode the source breakpoint infos for this "setBreakpoints" request
   SourceBreakpointMap request_bps;
   // "breakpoints" may be unset, in which case we treat it the same as being set
   // to an empty array.
-  if (breakpoints) {
-    for (const auto &bp : *breakpoints) {
-      const auto *bp_obj = bp.getAsObject();
-      if (bp_obj) {
-        SourceBreakpoint src_bp(dap, *bp_obj);
-        std::pair<uint32_t, uint32_t> bp_pos(src_bp.GetLine(),
-                                             src_bp.GetColumn());
-        request_bps.try_emplace(bp_pos, src_bp);
-        const auto [iv, inserted] =
-            dap.source_breakpoints[path].try_emplace(bp_pos, src_bp);
-        // We check if this breakpoint already exists to update it
-        if (inserted)
-          iv->getSecond().SetBreakpoint(path.data());
-        else
-          iv->getSecond().UpdateBreakpoint(src_bp);
-        AppendBreakpoint(&iv->getSecond(), response_breakpoints, path,
-                         src_bp.GetLine());
-      }
+  if (args.breakpoints) {
+    for (const auto &bp : *args.breakpoints) {
+      SourceBreakpoint src_bp(dap, bp);
+      std::pair<uint32_t, uint32_t> bp_pos(src_bp.GetLine(),
+                                           src_bp.GetColumn());
+      request_bps.try_emplace(bp_pos, src_bp);
+      const auto [iv, inserted] =
+          dap.source_breakpoints[path].try_emplace(bp_pos, src_bp);
+      // We check if this breakpoint already exists to update it
+      if (inserted)
+        iv->getSecond().SetBreakpoint(path.data());
+      else
+        iv->getSecond().UpdateBreakpoint(src_bp);
+
+      protocol::Breakpoint response_bp = iv->getSecond().ToProtocolBreakpoint();
+      if (!path.empty() && !response_bp.source)
+        response_bp.source = CreateSource(path);
+      if (!response_bp.line)
+        response_bp.line = src_bp.GetLine();
+      if (!response_bp.column)
+        response_bp.column = src_bp.GetColumn();
+      response_breakpoints.push_back(response_bp);
     }
   }
 
@@ -174,10 +70,7 @@ void SetBreakpointsRequestHandler::operator()(
     }
   }
 
-  llvm::json::Object body;
-  body.try_emplace("breakpoints", std::move(response_breakpoints));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
+  return protocol::SetBreakpointsResponseBody{std::move(response_breakpoints)};
 }
 
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp
index 365c9f0d722d4..1caaa23bf06f6 100644
--- a/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp
@@ -8,90 +8,28 @@
 
 #include "DAP.h"
 #include "EventHelper.h"
-#include "JSONUtils.h"
+#include "Protocol/ProtocolRequests.h"
 #include "RequestHandler.h"
 #include "Watchpoint.h"
 #include <set>
 
 namespace lldb_dap {
 
-// "SetDataBreakpointsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Replaces all existing data breakpoints with new data
-//     breakpoints.\nTo clear all data breakpoints, specify an empty
-//     array.\nWhen a data breakpoint is hit, a `stopped` event (with reason
-//     `data breakpoint`) is generated.\nClients should only call this request
-//     if the corresponding capability `supportsDataBreakpoints` is true.",
-//     "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "setDataBreakpoints" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/SetDataBreakpointsArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "SetDataBreakpointsArguments": {
-//   "type": "object",
-//   "description": "Arguments for `setDataBreakpoints` request.",
-//   "properties": {
-//     "breakpoints": {
-//       "type": "array",
-//       "items": {
-//         "$ref": "#/definitions/DataBreakpoint"
-//       },
-//       "description": "The contents of this array replaces all existing data
-//       breakpoints. An empty array clears all data breakpoints."
-//     }
-//   },
-//   "required": [ "breakpoints" ]
-// },
-// "SetDataBreakpointsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to `setDataBreakpoints` request.\nReturned is
-//     information about each breakpoint created by this request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "breakpoints": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/Breakpoint"
-//             },
-//             "description": "Information about the data breakpoints. The array
-//             elements correspond to the elements of the input argument
-//             `breakpoints` array."
-//           }
-//         },
-//         "required": [ "breakpoints" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void SetDataBreakpointsRequestHandler::operator()(
-    const llvm::json::Object &request) const {
-  llvm::json::Object response;
-  lldb::SBError error;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  const auto *breakpoints = arguments->getArray("breakpoints");
-  llvm::json::Array response_breakpoints;
+/// Replaces all existing data breakpoints with new data breakpoints.
+/// To clear all data breakpoints, specify an empty array.
+/// When a data breakpoint is hit, a stopped event (with reason data breakpoint)
+/// is generated. Clients should only call this request if the corresponding
+/// capability supportsDataBreakpoints is true.
+llvm::Expected<protocol::SetDataBreakpointsResponseBody>
+SetDataBreakpointsRequestHandler::Run(
+    const protocol::SetDataBreakpointsArguments &args) const {
+  std::vector<protocol::Breakpoint> response_breakpoints;
+
   dap.target.DeleteAllWatchpoints();
   std::vector<Watchpoint> watchpoints;
-  if (breakpoints) {
-    for (const auto &bp : *breakpoints) {
-      const auto *bp_obj = bp.getAsObject();
-      if (bp_obj)
-        watchpoints.emplace_back(dap, *bp_obj);
-    }
-  }
+  for (const auto &bp : args.breakpoints)
+    watchpoints.emplace_back(dap, bp);
+
   // If two watchpoints start at the same address, the latter overwrite the
   // former. So, we only enable those at first-seen addresses when iterating
   // backward.
@@ -103,12 +41,10 @@ void SetDataBreakpointsRequestHandler::operator()(
     }
   }
   for (auto wp : watchpoints)
-    AppendBreakpoint(&wp, response_breakpoints);
+    response_breakpoints.push_back(wp.ToProtocolBreakpoint());
 
-  llvm::json::Object body;
-  body.try_emplace("breakpoints", std::move(response_breakpoints));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
+  return protocol::SetDataBreakpointsResponseBody{
+      std::move(response_breakpoints)};
 }
 
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp
index 09d4fea2a9a22..d6cd77912ecc6 100644
--- a/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp
@@ -14,67 +14,24 @@
 
 namespace lldb_dap {
 
-// "SetExceptionBreakpointsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "SetExceptionBreakpoints request; value of command field
-//     is 'setExceptionBreakpoints'. The request configures the debuggers
-//     response to thrown exceptions. If an exception is configured to break, a
-//     StoppedEvent is fired (event type 'exception').", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "setExceptionBreakpoints" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/SetExceptionBreakpointsArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "SetExceptionBreakpointsArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'setExceptionBreakpoints' request.",
-//   "properties": {
-//     "filters": {
-//       "type": "array",
-//       "items": {
-//         "type": "string"
-//       },
-//       "description": "IDs of checked exception options. The set of IDs is
-//       returned via the 'exceptionBreakpointFilters' capability."
-//     },
-//     "exceptionOptions": {
-//       "type": "array",
-//       "items": {
-//         "$ref": "#/definitions/ExceptionOptions"
-//       },
-//       "description": "Configuration options for selected exceptions."
-//     }
-//   },
-//   "required": [ "filters" ]
-// },
-// "SetExceptionBreakpointsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'setExceptionBreakpoints' request. This is
-//     just an acknowledgement, so no body field is required."
-//   }]
-// }
-void SetExceptionBreakpointsRequestHandler::operator()(
-    const llvm::json::Object &request) const {
-  llvm::json::Object response;
-  lldb::SBError error;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  const auto *filters = arguments->getArray("filters");
+/// The request configures the debugger’s response to thrown exceptions. Each of
+/// the `filters`, `filterOptions`, and `exceptionOptions` in the request are
+/// independent configurations to a debug adapter indicating a kind of exception
+/// to catch. An exception thrown in a program should result in a stopped event
+/// from the debug adapter (with reason exception) if any of the configured
+/// filters match. Clients should only call this request if the corresponding
+/// capability exceptionBreakpointFilters returns one or more filters.
+llvm::Expected<protocol::SetExceptionBreakpointsResponseBody>
+SetExceptionBreakpointsRequestHandler::Run(
+    const protocol::SetExceptionBreakpointsArguments &args) const {
+  std::vector<protocol::Breakpoint> response_breakpoints;
   // Keep a list of any exception breakpoint filter names that weren't set
   // so we can clear any exception breakpoints if needed.
   std::set<llvm::StringRef> unset_filters;
   for (const auto &bp : *dap.exception_breakpoints)
     unset_filters.insert(bp.GetFilter());
 
-  for (const auto &value : *filters) {
+  for (const auto &value : args.filters) {
     const auto filter = GetAsString(value);
     auto *exc_bp = dap.GetExceptionBreakpoint(std::string(filter));
     if (exc_bp) {
@@ -87,7 +44,9 @@ void SetExceptionBreakpointsRequestHandler::operator()(
     if (exc_bp)
       exc_bp->ClearBreakpoint();
   }
-  dap.SendJSON(llvm::json::Value(std::move(response)));
+
+  return protocol::SetExceptionBreakpointsResponseBody{
+      std::move(response_breakpoints)};
 }
 
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp
index c45dc0d0d6553..2d95136f9a018 100644
--- a/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp
@@ -13,111 +13,31 @@
 
 namespace lldb_dap {
 
-// "SetFunctionBreakpointsRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "SetFunctionBreakpoints request; value of command field is
-//     'setFunctionBreakpoints'. Sets multiple function breakpoints and clears
-//     all previous function breakpoints. To clear all function breakpoint,
-//     specify an empty array. When a function breakpoint is hit, a StoppedEvent
-//     (event type 'function breakpoint') is generated.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "setFunctionBreakpoints" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/SetFunctionBreakpointsArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "SetFunctionBreakpointsArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'setFunctionBreakpoints' request.",
-//   "properties": {
-//     "breakpoints": {
-//       "type": "array",
-//       "items": {
-//         "$ref": "#/definitions/FunctionBreakpoint"
-//       },
-//       "description": "The function names of the breakpoints."
-//     }
-//   },
-//   "required": [ "breakpoints" ]
-// },
-// "FunctionBreakpoint": {
-//   "type": "object",
-//   "description": "Properties of a breakpoint passed to the
-//   setFunctionBreakpoints request.", "properties": {
-//     "name": {
-//       "type": "string",
-//       "description": "The name of the function."
-//     },
-//     "condition": {
-//       "type": "string",
-//       "description": "An optional expression for conditional breakpoints."
-//     },
-//     "hitCondition": {
-//       "type": "string",
-//       "description": "An optional expression that controls how many hits of
-//       the breakpoint are ignored. The backend is expected to interpret the
-//       expression as needed."
-//     }
-//   },
-//   "required": [ "name" ]
-// },
-// "SetFunctionBreakpointsResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to 'setFunctionBreakpoints' request. Returned is
-//     information about each breakpoint created by this request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "breakpoints": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/Breakpoint"
-//             },
-//             "description": "Information about the breakpoints. The array
-//             elements correspond to the elements of the 'breakpoints' array."
-//           }
-//         },
-//         "required": [ "breakpoints" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void SetFunctionBreakpointsRequestHandler::operator()(
-    const llvm::json::Object &request) const {
-  llvm::json::Object response;
-  lldb::SBError error;
-  FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  const auto *breakpoints = arguments->getArray("breakpoints");
-  llvm::json::Array response_breakpoints;
+/// Replaces all existing function breakpoints with new function breakpoints.
+/// To clear all function breakpoints, specify an empty array.
+/// When a function breakpoint is hit, a stopped event (with reason function
+/// breakpoint) is generated. Clients should only call this request if the
+/// corresponding capability supportsFunctionBreakpoints is true.
+llvm::Expected<protocol::SetFunctionBreakpointsResponseBody>
+SetFunctionBreakpointsRequestHandler::Run(
+    const protocol::SetFunctionBreakpointsArguments &args) const {
+  std::vector<protocol::Breakpoint> response_breakpoints;
 
   // Disable any function breakpoints that aren't in this request.
   // There is no call to remove function breakpoints other than calling this
   // function with a smaller or empty "breakpoints" list.
   const auto name_iter = dap.function_breakpoints.keys();
   llvm::DenseSet<llvm::StringRef> seen(name_iter.begin(), name_iter.end());
-  for (const auto &value : *breakpoints) {
-    const auto *bp_obj = value.getAsObject();
-    if (!bp_obj)
-      continue;
-    FunctionBreakpoint fn_bp(dap, *bp_obj);
-    const auto [it, inserted] = dap.function_breakpoints.try_emplace(
-        fn_bp.GetFunctionName(), dap, *bp_obj);
+  for (const auto &fb : args.breakpoints) {
+    FunctionBreakpoint fn_bp(dap, fb);
+    const auto [it, inserted] =
+        dap.function_breakpoints.try_emplace(fn_bp.GetFunctionName(), dap, fb);
     if (inserted)
       it->second.SetBreakpoint();
     else
       it->second.UpdateBreakpoint(fn_bp);
 
-    AppendBreakpoint(&it->second, response_breakpoints);
+    response_breakpoints.push_back(it->second.ToProtocolBreakpoint());
     seen.erase(fn_bp.GetFunctionName());
   }
 
@@ -130,10 +50,8 @@ void SetFunctionBreakpointsRequestHandler::operator()(
     dap.function_breakpoints.erase(name);
   }
 
-  llvm::json::Object body;
-  body.try_emplace("breakpoints", std::move(response_breakpoints));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
+  return protocol::SetFunctionBreakpointsResponseBody{
+      std::move(response_breakpoints)};
 }
 
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp
index 4e555ad605a26..0e9553e9d029a 100644
--- a/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp
@@ -13,202 +13,16 @@
 
 namespace lldb_dap {
 
-// "SetInstructionBreakpointsRequest": {
-//   "allOf": [
-//     {"$ref": "#/definitions/Request"},
-//     {
-//       "type": "object",
-//       "description" :
-//           "Replaces all existing instruction breakpoints. Typically, "
-//           "instruction breakpoints would be set from a disassembly window. "
-//           "\nTo clear all instruction breakpoints, specify an empty "
-//           "array.\nWhen an instruction breakpoint is hit, a `stopped` event "
-//           "(with reason `instruction breakpoint`) is generated.\nClients "
-//           "should only call this request if the corresponding capability "
-//           "`supportsInstructionBreakpoints` is true.",
-//       "properties": {
-//         "command": { "type": "string", "enum": ["setInstructionBreakpoints"]
-//         }, "arguments": {"$ref":
-//         "#/definitions/SetInstructionBreakpointsArguments"}
-//       },
-//       "required": [ "command", "arguments" ]
-//     }
-//   ]
-// },
-// "SetInstructionBreakpointsArguments": {
-//   "type": "object",
-//   "description": "Arguments for `setInstructionBreakpoints` request",
-//   "properties": {
-//     "breakpoints": {
-//       "type": "array",
-//       "items": {"$ref": "#/definitions/InstructionBreakpoint"},
-//       "description": "The instruction references of the breakpoints"
-//     }
-//   },
-//   "required": ["breakpoints"]
-// },
-// "SetInstructionBreakpointsResponse": {
-//   "allOf": [
-//     {"$ref": "#/definitions/Response"},
-//     {
-//       "type": "object",
-//       "description": "Response to `setInstructionBreakpoints` request",
-//       "properties": {
-//         "body": {
-//           "type": "object",
-//           "properties": {
-//             "breakpoints": {
-//               "type": "array",
-//               "items": {"$ref": "#/definitions/Breakpoint"},
-//               "description":
-//                   "Information about the breakpoints. The array elements
-//                   " "correspond to the elements of the `breakpoints`
-//                   array."
-//             }
-//           },
-//           "required": ["breakpoints"]
-//         }
-//       },
-//       "required": ["body"]
-//     }
-//   ]
-// },
-// "InstructionBreakpoint": {
-//   "type": "object",
-//   "description": "Properties of a breakpoint passed to the "
-//                   "`setInstructionBreakpoints` request",
-//   "properties": {
-//     "instructionReference": {
-//       "type": "string",
-//       "description" :
-//           "The instruction reference of the breakpoint.\nThis should be a "
-//           "memory or instruction pointer reference from an
-//           `EvaluateResponse`, "
-//           "`Variable`, `StackFrame`, `GotoTarget`, or `Breakpoint`."
-//     },
-//     "offset": {
-//       "type": "integer",
-//       "description": "The offset from the instruction reference in "
-//                       "bytes.\nThis can be negative."
-//     },
-//     "condition": {
-//       "type": "string",
-//       "description": "An expression for conditional breakpoints.\nIt is only
-//       "
-//                       "honored by a debug adapter if the corresponding "
-//                       "capability `supportsConditionalBreakpoints` is true."
-//     },
-//     "hitCondition": {
-//       "type": "string",
-//       "description": "An expression that controls how many hits of the "
-//                       "breakpoint are ignored.\nThe debug adapter is expected
-//                       " "to interpret the expression as needed.\nThe
-//                       attribute " "is only honored by a debug adapter if the
-//                       corresponding " "capability
-//                       `supportsHitConditionalBreakpoints` is true."
-//     },
-//     "mode": {
-//       "type": "string",
-//       "description": "The mode of this breakpoint. If defined, this must be
-//       "
-//                       "one of the `breakpointModes` the debug adapter "
-//                       "advertised in its `Capabilities`."
-//     }
-//   },
-//   "required": ["instructionReference"]
-// },
-// "Breakpoint": {
-//   "type": "object",
-//   "description" :
-//       "Information about a breakpoint created in `setBreakpoints`, "
-//       "`setFunctionBreakpoints`, `setInstructionBreakpoints`, or "
-//       "`setDataBreakpoints` requests.",
-//   "properties": {
-//     "id": {
-//       "type": "integer",
-//       "description" :
-//           "The identifier for the breakpoint. It is needed if breakpoint
-//           " "events are used to update or remove breakpoints."
-//     },
-//     "verified": {
-//       "type": "boolean",
-//       "description": "If true, the breakpoint could be set (but not "
-//                       "necessarily at the desired location)."
-//     },
-//     "message": {
-//       "type": "string",
-//       "description": "A message about the state of the breakpoint.\nThis
-//       "
-//                       "is shown to the user and can be used to explain
-//                       why " "a breakpoint could not be verified."
-//     },
-//     "source": {
-//       "$ref": "#/definitions/Source",
-//       "description": "The source where the breakpoint is located."
-//     },
-//     "line": {
-//       "type": "integer",
-//       "description" :
-//           "The start line of the actual range covered by the breakpoint."
-//     },
-//     "column": {
-//       "type": "integer",
-//       "description" :
-//           "Start position of the source range covered by the breakpoint.
-//           " "It is measured in UTF-16 code units and the client
-//           capability "
-//           "`columnsStartAt1` determines whether it is 0- or 1-based."
-//     },
-//     "endLine": {
-//       "type": "integer",
-//       "description" :
-//           "The end line of the actual range covered by the breakpoint."
-//     },
-//     "endColumn": {
-//       "type": "integer",
-//       "description" :
-//           "End position of the source range covered by the breakpoint. It
-//           " "is measured in UTF-16 code units and the client capability "
-//           "`columnsStartAt1` determines whether it is 0- or 1-based.\nIf
-//           " "no end line is given, then the end column is assumed to be
-//           in " "the start line."
-//     },
-//     "instructionReference": {
-//       "type": "string",
-//       "description": "A memory reference to where the breakpoint is
-//       set."
-//     },
-//     "offset": {
-//       "type": "integer",
-//       "description": "The offset from the instruction reference.\nThis "
-//                       "can be negative."
-//     },
-//     "reason": {
-//       "type": "string",
-//       "description" :
-//           "A machine-readable explanation of why a breakpoint may not be
-//           " "verified. If a breakpoint is verified or a specific reason
-//           is " "not known, the adapter should omit this property.
-//           Possible " "values include:\n\n- `pending`: Indicates a
-//           breakpoint might be " "verified in the future, but the adapter
-//           cannot verify it in the " "current state.\n - `failed`:
-//           Indicates a breakpoint was not " "able to be verified, and the
-//           adapter does not believe it can be " "verified without
-//           intervention.",
-//       "enum": [ "pending", "failed" ]
-//     }
-//   },
-//   "required": ["verified"]
-// },
-void SetInstructionBreakpointsRequestHandler::operator()(
-    const llvm::json::Object &request) const {
-  llvm::json::Object response;
-  llvm::json::Array response_breakpoints;
-  llvm::json::Object body;
-  FillResponse(request, response);
-
-  const auto *arguments = request.getObject("arguments");
-  const auto *breakpoints = arguments->getArray("breakpoints");
+/// Replaces all existing instruction breakpoints. Typically, instruction
+/// breakpoints would be set from a disassembly window. To clear all instruction
+/// breakpoints, specify an empty array. When an instruction breakpoint is hit,
+/// a stopped event (with reason instruction breakpoint) is generated. Clients
+/// should only call this request if the corresponding capability
+/// supportsInstructionBreakpoints is true.
+llvm::Expected<protocol::SetInstructionBreakpointsResponseBody>
+SetInstructionBreakpointsRequestHandler::Run(
+    const protocol::SetInstructionBreakpointsArguments &args) const {
+  std::vector<protocol::Breakpoint> response_breakpoints;
 
   // Disable any instruction breakpoints that aren't in this request.
   // There is no call to remove instruction breakpoints other than calling this
@@ -216,19 +30,16 @@ void SetInstructionBreakpointsRequestHandler::operator()(
   llvm::DenseSet<lldb::addr_t> seen(
       llvm::from_range, llvm::make_first_range(dap.instruction_breakpoints));
 
-  for (const auto &bp : *breakpoints) {
-    const auto *bp_obj = bp.getAsObject();
-    if (!bp_obj)
-      continue;
+  for (const auto &bp : args.breakpoints) {
     // Read instruction breakpoint request.
-    InstructionBreakpoint inst_bp(dap, *bp_obj);
+    InstructionBreakpoint inst_bp(dap, bp);
     const auto [iv, inserted] = dap.instruction_breakpoints.try_emplace(
-        inst_bp.GetInstructionAddressReference(), dap, *bp_obj);
+        inst_bp.GetInstructionAddressReference(), dap, bp);
     if (inserted)
       iv->second.SetBreakpoint();
     else
       iv->second.UpdateBreakpoint(inst_bp);
-    AppendBreakpoint(&iv->second, response_breakpoints);
+    response_breakpoints.push_back(iv->second.ToProtocolBreakpoint());
     seen.erase(inst_bp.GetInstructionAddressReference());
   }
 
@@ -240,9 +51,8 @@ void SetInstructionBreakpointsRequestHandler::operator()(
     dap.instruction_breakpoints.erase(addr);
   }
 
-  body.try_emplace("breakpoints", std::move(response_breakpoints));
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
+  return protocol::SetInstructionBreakpointsResponseBody{
+      std::move(response_breakpoints)};
 }
 
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 4409cf5b27e5b..ffa3ef74866e9 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -354,68 +354,21 @@ llvm::json::Value CreateScope(const llvm::StringRef name,
   return llvm::json::Value(std::move(object));
 }
 
-// "Breakpoint": {
-//   "type": "object",
-//   "description": "Information about a Breakpoint created in setBreakpoints
-//                   or setFunctionBreakpoints.",
-//   "properties": {
-//     "id": {
-//       "type": "integer",
-//       "description": "An optional unique identifier for the breakpoint."
-//     },
-//     "verified": {
-//       "type": "boolean",
-//       "description": "If true breakpoint could be set (but not necessarily
-//                       at the desired location)."
-//     },
-//     "message": {
-//       "type": "string",
-//       "description": "An optional message about the state of the breakpoint.
-//                       This is shown to the user and can be used to explain
-//                       why a breakpoint could not be verified."
-//     },
-//     "source": {
-//       "$ref": "#/definitions/Source",
-//       "description": "The source where the breakpoint is located."
-//     },
-//     "line": {
-//       "type": "integer",
-//       "description": "The start line of the actual range covered by the
-//                       breakpoint."
-//     },
-//     "column": {
-//       "type": "integer",
-//       "description": "An optional start column of the actual range covered
-//                       by the breakpoint."
-//     },
-//     "endLine": {
-//       "type": "integer",
-//       "description": "An optional end line of the actual range covered by
-//                       the breakpoint."
-//     },
-//     "endColumn": {
-//       "type": "integer",
-//       "description": "An optional end column of the actual range covered by
-//                       the breakpoint. If no end line is given, then the end
-//                       column is assumed to be in the start line."
-//     }
-//   },
-//   "required": [ "verified" ]
-// }
-llvm::json::Value CreateBreakpoint(BreakpointBase *bp,
-                                   std::optional<llvm::StringRef> request_path,
-                                   std::optional<uint32_t> request_line,
-                                   std::optional<uint32_t> request_column) {
-  llvm::json::Object object;
-  if (request_path)
-    object.try_emplace("source", CreateSource(*request_path));
-  bp->CreateJsonObject(object);
+protocol::Breakpoint
+CreateBreakpoint(BreakpointBase *bp,
+                 std::optional<llvm::StringRef> request_path,
+                 std::optional<uint32_t> request_line,
+                 std::optional<uint32_t> request_column) {
+  protocol::Breakpoint breakpoint = bp->ToProtocolBreakpoint();
+  if (request_path && !breakpoint.source)
+    breakpoint.source = CreateSource(*request_path);
+
   // We try to add request_line as a fallback
-  if (request_line)
-    object.try_emplace("line", *request_line);
-  if (request_column)
-    object.try_emplace("column", *request_column);
-  return llvm::json::Value(std::move(object));
+  if (request_line && !breakpoint.line)
+    breakpoint.line = *request_line;
+  if (request_column && !breakpoint.column)
+    breakpoint.column = *request_column;
+  return breakpoint;
 }
 
 static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
@@ -567,96 +520,30 @@ CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
   return filter;
 }
 
-// "Source": {
-//   "type": "object",
-//   "description": "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.",
-//   "properties": {
-//     "name": {
-//       "type": "string",
-//       "description": "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."
-//     },
-//     "path": {
-//       "type": "string",
-//       "description": "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)."
-//     },
-//     "sourceReference": {
-//       "type": "number",
-//       "description": "If sourceReference > 0 the contents of the source must
-//                       be retrieved through the SourceRequest (even if a path
-//                       is specified). A sourceReference is only valid for a
-//                       session, so it must not be used to persist a source."
-//     },
-//     "presentationHint": {
-//       "type": "string",
-//       "description": "An optional 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.",
-//       "enum": [ "normal", "emphasize", "deemphasize" ]
-//     },
-//     "origin": {
-//       "type": "string",
-//       "description": "The (optional) origin of this source: possible values
-//                       'internal module', 'inlined content from source map',
-//                       etc."
-//     },
-//     "sources": {
-//       "type": "array",
-//       "items": {
-//         "$ref": "#/definitions/Source"
-//       },
-//       "description": "An optional list of sources that are related to this
-//                       source. These may be the source that generated this
-//                       source."
-//     },
-//     "adapterData": {
-//       "type":["array","boolean","integer","null","number","object","string"],
-//       "description": "Optional data that a debug adapter might want to loop
-//                       through the client. The client should leave the data
-//                       intact and persist it across sessions. The client
-//                       should not interpret the data."
-//     },
-//     "checksums": {
-//       "type": "array",
-//       "items": {
-//         "$ref": "#/definitions/Checksum"
-//       },
-//       "description": "The checksums associated with this file."
-//     }
-//   }
-// }
-llvm::json::Value CreateSource(const lldb::SBFileSpec &file) {
-  llvm::json::Object object;
+protocol::Source CreateSource(const lldb::SBFileSpec &file) {
+  protocol::Source source;
   if (file.IsValid()) {
     const char *name = file.GetFilename();
     if (name)
-      EmplaceSafeString(object, "name", name);
+      source.name = name;
     char path[PATH_MAX] = "";
     if (file.GetPath(path, sizeof(path)) &&
-        lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) {
-      EmplaceSafeString(object, "path", std::string(path));
-    }
+        lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX))
+      source.path = path;
   }
-  return llvm::json::Value(std::move(object));
+  return source;
 }
 
-llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry) {
+protocol::Source CreateSource(const lldb::SBLineEntry &line_entry) {
   return CreateSource(line_entry.GetFileSpec());
 }
 
-llvm::json::Value CreateSource(llvm::StringRef source_path) {
-  llvm::json::Object source;
+protocol::Source CreateSource(llvm::StringRef source_path) {
+  protocol::Source source;
   llvm::StringRef name = llvm::sys::path::filename(source_path);
-  EmplaceSafeString(source, "name", name);
-  EmplaceSafeString(source, "path", source_path);
-  return llvm::json::Value(std::move(source));
+  source.name = name;
+  source.path = source_path;
+  return source;
 }
 
 bool ShouldDisplayAssemblySource(
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index d0e20729f4ed9..d16d41b3bc368 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -198,34 +198,6 @@ GetStringMap(const llvm::json::Object &obj, llvm::StringRef key);
 void FillResponse(const llvm::json::Object &request,
                   llvm::json::Object &response);
 
-/// Converts \a bp to a JSON value and appends the first valid location to the
-/// \a breakpoints array.
-///
-/// \param[in] bp
-///     A LLDB breakpoint object which will get the first valid location
-///     extracted and converted into a JSON object in the \a breakpoints array
-///
-/// \param[in] breakpoints
-///     A JSON array that will get a llvm::json::Value for \a bp
-///     appended to it.
-///
-/// \param[in] request_path
-///     An optional source path to use when creating the "Source" object of this
-///     breakpoint. If not specified, the "Source" object is created from the
-///     breakpoint's address' LineEntry. It is useful to ensure the same source
-///     paths provided by the setBreakpoints request are returned to the IDE.
-///
-/// \param[in] request_line
-///     An optional line to use when creating the "Breakpoint" object to append.
-///     It is used if the breakpoint has no valid locations.
-///     It is useful to ensure the same line
-///     provided by the setBreakpoints request are returned to the IDE as a
-///     fallback.
-void AppendBreakpoint(
-    BreakpointBase *bp, llvm::json::Array &breakpoints,
-    std::optional<llvm::StringRef> request_path = std::nullopt,
-    std::optional<uint32_t> request_line = std::nullopt);
-
 /// Converts breakpoint location to a debug adapter protocol "Breakpoint".
 ///
 /// \param[in] bp
@@ -253,7 +225,7 @@ void AppendBreakpoint(
 /// \return
 ///     A "Breakpoint" JSON object with that follows the formal JSON
 ///     definition outlined by Microsoft.
-llvm::json::Value
+protocol::Breakpoint
 CreateBreakpoint(BreakpointBase *bp,
                  std::optional<llvm::StringRef> request_path = std::nullopt,
                  std::optional<uint32_t> request_line = std::nullopt,
@@ -323,7 +295,7 @@ llvm::json::Value CreateScope(const llvm::StringRef name,
 /// \return
 ///     A "Source" JSON object that follows the formal JSON
 ///     definition outlined by Microsoft.
-llvm::json::Value CreateSource(const lldb::SBFileSpec &file);
+protocol::Source CreateSource(const lldb::SBFileSpec &file);
 
 /// Create a "Source" JSON object as described in the debug adapter definition.
 ///
@@ -334,7 +306,7 @@ llvm::json::Value CreateSource(const lldb::SBFileSpec &file);
 /// \return
 ///     A "Source" JSON object that follows the formal JSON
 ///     definition outlined by Microsoft.
-llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry);
+protocol::Source CreateSource(const lldb::SBLineEntry &line_entry);
 
 /// Create a "Source" object for a given source path.
 ///
@@ -344,7 +316,7 @@ llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry);
 /// \return
 ///     A "Source" JSON object that follows the formal JSON
 ///     definition outlined by Microsoft.
-llvm::json::Value CreateSource(llvm::StringRef source_path);
+protocol::Source CreateSource(llvm::StringRef source_path);
 
 /// Return true if the given line entry should be displayed as assembly.
 ///
diff --git a/lldb/tools/lldb-dap/Watchpoint.cpp b/lldb/tools/lldb-dap/Watchpoint.cpp
index a94cbcdbc4122..72f913d465408 100644
--- a/lldb/tools/lldb-dap/Watchpoint.cpp
+++ b/lldb/tools/lldb-dap/Watchpoint.cpp
@@ -8,25 +8,24 @@
 
 #include "Watchpoint.h"
 #include "DAP.h"
-#include "JSONUtils.h"
+#include "Protocol/ProtocolTypes.h"
 #include "lldb/API/SBTarget.h"
 #include "lldb/lldb-enumerations.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/Support/JSON.h"
 #include <cstdint>
 #include <string>
 
 namespace lldb_dap {
-Watchpoint::Watchpoint(DAP &d, const llvm::json::Object &obj)
-    : BreakpointBase(d, obj) {
-  llvm::StringRef dataId = GetString(obj, "dataId").value_or("");
-  std::string accessType = GetString(obj, "accessType").value_or("").str();
+Watchpoint::Watchpoint(DAP &d, const protocol::DataBreakpointInfo &breakpoint)
+    : BreakpointBase(d, breakpoint.condition, breakpoint.hitCondition) {
+  llvm::StringRef dataId = breakpoint.dataId;
   auto [addr_str, size_str] = dataId.split('/');
   llvm::to_integer(addr_str, m_addr, 16);
   llvm::to_integer(size_str, m_size);
-  m_options.SetWatchpointTypeRead(accessType != "write");
-  if (accessType != "read")
+  m_options.SetWatchpointTypeRead(breakpoint.accessType !=
+                                  protocol::eDataBreakpointAccessTypeWrite);
+  if (breakpoint.accessType != protocol::eDataBreakpointAccessTypeRead)
     m_options.SetWatchpointTypeWrite(lldb::eWatchpointWriteTypeOnModify);
 }
 
@@ -38,13 +37,14 @@ void Watchpoint::SetHitCondition() {
     m_wp.SetIgnoreCount(hitCount - 1);
 }
 
-void Watchpoint::CreateJsonObject(llvm::json::Object &object) {
+protocol::Breakpoint Watchpoint::ToProtocolBreakpoint() {
+  protocol::Breakpoint breakpoint;
   if (!m_error.IsValid() || m_error.Fail()) {
-    object.try_emplace("verified", false);
+    breakpoint.verified = false;
     if (m_error.Fail())
-      EmplaceSafeString(object, "message", m_error.GetCString());
+      breakpoint.message = m_error.GetCString();
   } else {
-    object.try_emplace("verified", true);
+    breakpoint.verified = true;
   }
 }
 
diff --git a/lldb/tools/lldb-dap/Watchpoint.h b/lldb/tools/lldb-dap/Watchpoint.h
index bf52b41281f29..9e0902d6db5bf 100644
--- a/lldb/tools/lldb-dap/Watchpoint.h
+++ b/lldb/tools/lldb-dap/Watchpoint.h
@@ -11,6 +11,7 @@
 
 #include "BreakpointBase.h"
 #include "DAPForward.h"
+#include "Protocol/ProtocolTypes.h"
 #include "lldb/API/SBError.h"
 #include "lldb/API/SBWatchpoint.h"
 #include "lldb/API/SBWatchpointOptions.h"
@@ -21,12 +22,13 @@ namespace lldb_dap {
 
 class Watchpoint : public BreakpointBase {
 public:
-  Watchpoint(DAP &d, const llvm::json::Object &obj);
+  Watchpoint(DAP &d, const protocol::DataBreakpointInfo &breakpoint);
   Watchpoint(DAP &d, lldb::SBWatchpoint wp) : BreakpointBase(d), m_wp(wp) {}
 
   void SetCondition() override;
   void SetHitCondition() override;
-  void CreateJsonObject(llvm::json::Object &object) override;
+
+  protocol::Breakpoint ToProtocolBreakpoint();
 
   void SetWatchpoint();
 

>From 5e8830c64a2e97a1a8d4d448dae6173139e37bf1 Mon Sep 17 00:00:00 2001
From: Ely Ronnen <elyronnen at gmail.com>
Date: Sat, 26 Apr 2025 13:42:21 +0200
Subject: [PATCH 07/10] remove CreateBreakpoint

---
 lldb/tools/lldb-dap/JSONUtils.cpp | 23 ---------------------
 lldb/tools/lldb-dap/JSONUtils.h   | 33 -------------------------------
 2 files changed, 56 deletions(-)

diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index ffa3ef74866e9..9be7bf74c476b 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -354,23 +354,6 @@ llvm::json::Value CreateScope(const llvm::StringRef name,
   return llvm::json::Value(std::move(object));
 }
 
-protocol::Breakpoint
-CreateBreakpoint(BreakpointBase *bp,
-                 std::optional<llvm::StringRef> request_path,
-                 std::optional<uint32_t> request_line,
-                 std::optional<uint32_t> request_column) {
-  protocol::Breakpoint breakpoint = bp->ToProtocolBreakpoint();
-  if (request_path && !breakpoint.source)
-    breakpoint.source = CreateSource(*request_path);
-
-  // We try to add request_line as a fallback
-  if (request_line && !breakpoint.line)
-    breakpoint.line = *request_line;
-  if (request_column && !breakpoint.column)
-    breakpoint.column = *request_column;
-  return breakpoint;
-}
-
 static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
   uint64_t debug_info_size = 0;
   llvm::StringRef section_name(section.GetName());
@@ -459,12 +442,6 @@ llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module) {
   return llvm::json::Value(std::move(object));
 }
 
-void AppendBreakpoint(BreakpointBase *bp, llvm::json::Array &breakpoints,
-                      std::optional<llvm::StringRef> request_path,
-                      std::optional<uint32_t> request_line) {
-  breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line));
-}
-
 // "Event": {
 //   "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
 //     "type": "object",
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index d16d41b3bc368..7c7cf620f0c06 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -198,39 +198,6 @@ GetStringMap(const llvm::json::Object &obj, llvm::StringRef key);
 void FillResponse(const llvm::json::Object &request,
                   llvm::json::Object &response);
 
-/// Converts breakpoint location to a debug adapter protocol "Breakpoint".
-///
-/// \param[in] bp
-///     A LLDB breakpoint object to convert into a JSON value
-///
-/// \param[in] request_path
-///     An optional source path to use when creating the "Source" object of this
-///     breakpoint. If not specified, the "Source" object is created from the
-///     breakpoint's address' LineEntry. It is useful to ensure the same source
-///     paths provided by the setBreakpoints request are returned to the IDE.
-///
-/// \param[in] request_line
-///     An optional line to use when creating the resulting "Breakpoint" object.
-///     It is used if the breakpoint has no valid locations.
-///     It is useful to ensure the same line
-///     provided by the setBreakpoints request are returned to the IDE as a
-///     fallback.
-///
-/// \param[in] request_column
-///     An optional column to use when creating the resulting "Breakpoint"
-///     object. It is used if the breakpoint has no valid locations. It is
-///     useful to ensure the same column provided by the setBreakpoints request
-///     are returned to the IDE as a fallback.
-///
-/// \return
-///     A "Breakpoint" JSON object with that follows the formal JSON
-///     definition outlined by Microsoft.
-protocol::Breakpoint
-CreateBreakpoint(BreakpointBase *bp,
-                 std::optional<llvm::StringRef> request_path = std::nullopt,
-                 std::optional<uint32_t> request_line = std::nullopt,
-                 std::optional<uint32_t> request_column = std::nullopt);
-
 /// Converts a LLDB module to a VS Code DAP module for use in "modules" events.
 ///
 /// \param[in] target

>From 54782ac3dc0deab46bac0de21d47f8f4a9b2edd4 Mon Sep 17 00:00:00 2001
From: Ely Ronnen <elyronnen at gmail.com>
Date: Sat, 26 Apr 2025 18:45:16 +0200
Subject: [PATCH 08/10] data breakpoint

---
 lldb/tools/lldb-dap/DAP.cpp                   |  16 +-
 lldb/tools/lldb-dap/DAP.h                     |   1 +
 .../DataBreakpointInfoRequestHandler.cpp      | 174 ++++--------------
 .../Handler/InitializeRequestHandler.cpp      |   2 +-
 ...TestGetTargetBreakpointsRequestHandler.cpp |   2 +-
 .../lldb-dap/Protocol/ProtocolRequests.h      |   4 +-
 .../tools/lldb-dap/Protocol/ProtocolTypes.cpp |  27 ++-
 lldb/tools/lldb-dap/Protocol/ProtocolTypes.h  |   1 +
 lldb/tools/lldb-dap/Watchpoint.h              |   2 +-
 9 files changed, 77 insertions(+), 152 deletions(-)

diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 55d49667b6398..e7145f56ed903 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -45,6 +45,7 @@
 #include <chrono>
 #include <condition_variable>
 #include <cstdarg>
+#include <cstdint>
 #include <cstdio>
 #include <fstream>
 #include <future>
@@ -514,15 +515,18 @@ lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) {
   return target.GetProcess().GetThreadByID(tid);
 }
 
-lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
-  const uint64_t frame_id =
-      GetInteger<uint64_t>(arguments, "frameId").value_or(UINT64_MAX);
+lldb::SBFrame DAP::GetLLDBFrame(std::optional<uint64_t> frame_id) {
+  uint64_t id = frame_id.value_or(0);
   lldb::SBProcess process = target.GetProcess();
   // Upper 32 bits is the thread index ID
-  lldb::SBThread thread =
-      process.GetThreadByIndexID(GetLLDBThreadIndexID(frame_id));
+  lldb::SBThread thread = process.GetThreadByIndexID(GetLLDBThreadIndexID(id));
   // Lower 32 bits is the frame index
-  return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id));
+  return thread.GetFrameAtIndex(GetLLDBFrameID(id));
+}
+
+lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
+  const auto frame_id = GetInteger<uint64_t>(arguments, "frameId");
+  return GetLLDBFrame(frame_id);
 }
 
 llvm::json::Value DAP::CreateTopLevelScopes() {
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 88eedb0860cf1..ccdb9213a9356 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -275,6 +275,7 @@ struct DAP {
   lldb::SBThread GetLLDBThread(lldb::tid_t id);
   lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments);
 
+  lldb::SBFrame GetLLDBFrame(std::optional<uint64_t> frame_id);
   lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments);
 
   llvm::json::Value CreateTopLevelScopes();
diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
index 4d920f8556254..4f37765e9f453 100644
--- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
@@ -8,144 +8,49 @@
 
 #include "DAP.h"
 #include "EventHelper.h"
-#include "JSONUtils.h"
+#include "Protocol/ProtocolTypes.h"
 #include "RequestHandler.h"
 #include "lldb/API/SBMemoryRegionInfo.h"
 #include "llvm/ADT/StringExtras.h"
+#include <optional>
 
 namespace lldb_dap {
 
-// "DataBreakpointInfoRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "Obtains information on a possible data breakpoint that
-//     could be set on an expression or variable.\nClients should only call this
-//     request if the corresponding capability `supportsDataBreakpoints` is
-//     true.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "dataBreakpointInfo" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/DataBreakpointInfoArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "DataBreakpointInfoArguments": {
-//   "type": "object",
-//   "description": "Arguments for `dataBreakpointInfo` request.",
-//   "properties": {
-//     "variablesReference": {
-//       "type": "integer",
-//       "description": "Reference to the variable container if the data
-//       breakpoint is requested for a child of the container. The
-//       `variablesReference` must have been obtained in the current suspended
-//       state. See 'Lifetime of Object References' in the Overview section for
-//       details."
-//     },
-//     "name": {
-//       "type": "string",
-//       "description": "The name of the variable's child to obtain data
-//       breakpoint information for.\nIf `variablesReference` isn't specified,
-//       this can be an expression."
-//     },
-//     "frameId": {
-//       "type": "integer",
-//       "description": "When `name` is an expression, evaluate it in the scope
-//       of this stack frame. If not specified, the expression is evaluated in
-//       the global scope. When `variablesReference` is specified, this property
-//       has no effect."
-//     }
-//   },
-//   "required": [ "name" ]
-// },
-// "DataBreakpointInfoResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to `dataBreakpointInfo` request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "dataId": {
-//             "type": [ "string", "null" ],
-//             "description": "An identifier for the data on which a data
-//             breakpoint can be registered with the `setDataBreakpoints`
-//             request or null if no data breakpoint is available. If a
-//             `variablesReference` or `frameId` is passed, the `dataId` is
-//             valid in the current suspended state, otherwise it's valid
-//             indefinitely. See 'Lifetime of Object References' in the Overview
-//             section for details. Breakpoints set using the `dataId` in the
-//             `setDataBreakpoints` request may outlive the lifetime of the
-//             associated `dataId`."
-//           },
-//           "description": {
-//             "type": "string",
-//             "description": "UI string that describes on what data the
-//             breakpoint is set on or why a data breakpoint is not available."
-//           },
-//           "accessTypes": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/DataBreakpointAccessType"
-//             },
-//             "description": "Attribute lists the available access types for a
-//             potential data breakpoint. A UI client could surface this
-//             information."
-//           },
-//           "canPersist": {
-//             "type": "boolean",
-//             "description": "Attribute indicates that a potential data
-//             breakpoint could be persisted across sessions."
-//           }
-//         },
-//         "required": [ "dataId", "description" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void DataBreakpointInfoRequestHandler::operator()(
-    const llvm::json::Object &request) const {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  llvm::json::Object body;
-  lldb::SBError error;
-  llvm::json::Array accessTypes{"read", "write", "readWrite"};
-  const auto *arguments = request.getObject("arguments");
-  const auto variablesReference =
-      GetInteger<uint64_t>(arguments, "variablesReference").value_or(0);
-  llvm::StringRef name = GetString(arguments, "name").value_or("");
-  lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
-  lldb::SBValue variable = dap.variables.FindVariable(variablesReference, name);
+/// Obtains information on a possible data breakpoint that could be set on an
+/// expression or variable. Clients should only call this request if the
+/// corresponding capability supportsDataBreakpoints is true.
+llvm::Expected<protocol::DataBreakpointInfoResponseBody>
+DataBreakpointInfoRequestHandler::Run(
+    const protocol::DataBreakpointInfoArguments &args) const {
+  protocol::DataBreakpointInfoResponseBody response;
+  lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
+  lldb::SBValue variable = dap.variables.FindVariable(
+      args.variablesReference.value_or(0), args.name);
   std::string addr, size;
 
   if (variable.IsValid()) {
     lldb::addr_t load_addr = variable.GetLoadAddress();
     size_t byte_size = variable.GetByteSize();
     if (load_addr == LLDB_INVALID_ADDRESS) {
-      body.try_emplace("dataId", nullptr);
-      body.try_emplace("description",
-                       "does not exist in memory, its location is " +
-                           std::string(variable.GetLocation()));
+      response.dataId = std::nullopt;
+      response.description = "does not exist in memory, its location is " +
+                             std::string(variable.GetLocation());
     } else if (byte_size == 0) {
-      body.try_emplace("dataId", nullptr);
-      body.try_emplace("description", "variable size is 0");
+      response.dataId = std::nullopt;
+      response.description = "variable size is 0";
     } else {
       addr = llvm::utohexstr(load_addr);
       size = llvm::utostr(byte_size);
     }
-  } else if (variablesReference == 0 && frame.IsValid()) {
-    lldb::SBValue value = frame.EvaluateExpression(name.data());
+  } else if (args.variablesReference.value_or(0) == 0 && frame.IsValid()) {
+    lldb::SBValue value = frame.EvaluateExpression(args.name.c_str());
     if (value.GetError().Fail()) {
       lldb::SBError error = value.GetError();
       const char *error_cstr = error.GetCString();
-      body.try_emplace("dataId", nullptr);
-      body.try_emplace("description", error_cstr && error_cstr[0]
-                                          ? std::string(error_cstr)
-                                          : "evaluation failed");
+      response.dataId = std::nullopt;
+      response.description = error_cstr && error_cstr[0]
+                                 ? std::string(error_cstr)
+                                 : "evaluation failed";
     } else {
       uint64_t load_addr = value.GetValueAsUnsigned();
       lldb::SBData data = value.GetPointeeData();
@@ -159,32 +64,31 @@ void DataBreakpointInfoRequestHandler::operator()(
         // request if SBProcess::GetMemoryRegionInfo returns error.
         if (err.Success()) {
           if (!(region.IsReadable() || region.IsWritable())) {
-            body.try_emplace("dataId", nullptr);
-            body.try_emplace("description",
-                             "memory region for address " + addr +
-                                 " has no read or write permissions");
+            response.dataId = std::nullopt;
+            response.description = "memory region for address " + addr +
+                                   " has no read or write permissions";
           }
         }
       } else {
-        body.try_emplace("dataId", nullptr);
-        body.try_emplace("description",
-                         "unable to get byte size for expression: " +
-                             name.str());
+        response.dataId = std::nullopt;
+        response.description =
+            "unable to get byte size for expression: " + args.name;
       }
     }
   } else {
-    body.try_emplace("dataId", nullptr);
-    body.try_emplace("description", "variable not found: " + name.str());
+    response.dataId = std::nullopt;
+    response.description = "variable not found: " + args.name;
   }
 
-  if (!body.getObject("dataId")) {
-    body.try_emplace("dataId", addr + "/" + size);
-    body.try_emplace("accessTypes", std::move(accessTypes));
-    body.try_emplace("description",
-                     size + " bytes at " + addr + " " + name.str());
+  if (!response.dataId.has_value()) {
+    response.dataId = addr + "/" + size;
+    response.accessTypes = {protocol::eDataBreakpointAccessTypeRead,
+                            protocol::eDataBreakpointAccessTypeWrite,
+                            protocol::eDataBreakpointAccessTypeReadWrite};
+    response.description = size + " bytes at " + addr + " " + args.name;
   }
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
+
+  return response;
 }
 
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
index 7d8ac676ba935..9614d2876b2d4 100644
--- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
@@ -212,7 +212,7 @@ static void EventThreadFunction(DAP &dap) {
             // avoids sending paths that should be source mapped. Note that
             // CreateBreakpoint doesn't apply source mapping and certain
             // implementation ignore the source part of this event anyway.
-            llvm::json::Value source_bp = CreateBreakpoint(&bp);
+            llvm::json::Value source_bp = bp.ToProtocolBreakpoint();
             source_bp.getAsObject()->erase("source");
 
             llvm::json::Object body;
diff --git a/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp
index 54c11cfa0b791..5f4f016f6a1ef 100644
--- a/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp
@@ -20,7 +20,7 @@ void TestGetTargetBreakpointsRequestHandler::operator()(
   llvm::json::Array response_breakpoints;
   for (uint32_t i = 0; dap.target.GetBreakpointAtIndex(i).IsValid(); ++i) {
     auto bp = Breakpoint(dap, dap.target.GetBreakpointAtIndex(i));
-    AppendBreakpoint(&bp, response_breakpoints);
+    response_breakpoints.push_back(bp.ToProtocolBreakpoint());
   }
   llvm::json::Object body;
   body.try_emplace("breakpoints", std::move(response_breakpoints));
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index 7bcb8212575f4..a3ea017f5551a 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -506,7 +506,7 @@ struct DataBreakpointInfoArguments {
   /// When `name` is an expression, evaluate it in the scope of this stack
   /// frame. If not specified, the expression is evaluated in the global scope.
   /// When `asAddress` is true, the `frameId` is ignored.
-  std::optional<int64_t> frameId;
+  std::optional<uint64_t> frameId;
 
   /// If specified, a debug adapter should return information for the range of
   /// memory extending `bytes` number of bytes from the address or variable
@@ -539,7 +539,7 @@ struct DataBreakpointInfoResponseBody {
   /// for details. Breakpoints set using the `dataId` in the
   /// `setDataBreakpoints` request may outlive the lifetime of the associated
   /// `dataId`.
-  std::optional<std::string> dataId;
+  std::optional<std::optional<std::string>> dataId;
 
   /// UI string that describes on what data the breakpoint is set on or why a
   /// data breakpoint is not available.
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index eaad009150497..93d9931575dfe 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -43,13 +43,28 @@ bool fromJSON(const json::Value &Params, Source &S, json::Path P) {
          O.map("sourceReference", S.sourceReference);
 }
 
+static llvm::json::Value ToString(PresentationHint hint) {
+  switch (hint) {
+  case ePresentationHintNormal:
+    return "normal";
+  case ePresentationHintEmphasize:
+    return "emphasize";
+  case ePresentationHintDeemphasize:
+    return "deemphasize";
+  }
+  llvm_unreachable("unhandled presentation hint.");
+}
+
 llvm::json::Value toJSON(const Source &S) {
-  json::Object result{
-      {"name", S.name},
-      {"path", S.path},
-      {"sourceReference", S.sourceReference},
-      {"presentationHint", S.presentationHint},
-  };
+  json::Object result;
+  if (S.name)
+    result.insert({"name", *S.name});
+  if (S.path)
+    result.insert({"path", *S.path});
+  if (S.name)
+    result.insert({"sourceReference", *S.sourceReference});
+  if (S.presentationHint)
+    result.insert({"sourceReference", ToString(*S.presentationHint)});
 
   return result;
 }
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index ff288a1be776b..3ed28342bd25e 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -273,6 +273,7 @@ enum PresentationHint : unsigned {
   ePresentationHintEmphasize,
   ePresentationHintDeemphasize,
 };
+llvm::json::Value toJSON(const PresentationHint &);
 
 /// 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
diff --git a/lldb/tools/lldb-dap/Watchpoint.h b/lldb/tools/lldb-dap/Watchpoint.h
index 9e0902d6db5bf..b7fe58fe73501 100644
--- a/lldb/tools/lldb-dap/Watchpoint.h
+++ b/lldb/tools/lldb-dap/Watchpoint.h
@@ -28,7 +28,7 @@ class Watchpoint : public BreakpointBase {
   void SetCondition() override;
   void SetHitCondition() override;
 
-  protocol::Breakpoint ToProtocolBreakpoint();
+  protocol::Breakpoint ToProtocolBreakpoint() override;
 
   void SetWatchpoint();
 

>From e84355997e6a28e66dad81277f4ac1960225220e Mon Sep 17 00:00:00 2001
From: Ely Ronnen <elyronnen at gmail.com>
Date: Sat, 26 Apr 2025 19:40:56 +0200
Subject: [PATCH 09/10] requests serialization

---
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |  1 -
 .../SetFunctionBreakpointsRequestHandler.cpp  |  1 -
 ...etInstructionBreakpointsRequestHandler.cpp |  1 -
 lldb/tools/lldb-dap/JSONUtils.cpp             |  1 -
 .../lldb-dap/Protocol/ProtocolRequests.cpp    | 84 +++++++++++++++++++
 .../tools/lldb-dap/Protocol/ProtocolTypes.cpp | 12 +++
 lldb/tools/lldb-dap/Protocol/ProtocolTypes.h  |  2 +-
 lldb/tools/lldb-dap/Watchpoint.cpp            |  2 +
 8 files changed, 99 insertions(+), 5 deletions(-)

diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 8bfe42d9d3fde..64af79dee6ef1 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -15,7 +15,6 @@
 #include "Protocol/ProtocolBase.h"
 #include "Protocol/ProtocolRequests.h"
 #include "Protocol/ProtocolTypes.h"
-#include "lldb/API/SBError.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
diff --git a/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp
index 2d95136f9a018..1367aa3e864d9 100644
--- a/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp
@@ -8,7 +8,6 @@
 
 #include "DAP.h"
 #include "EventHelper.h"
-#include "JSONUtils.h"
 #include "RequestHandler.h"
 
 namespace lldb_dap {
diff --git a/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp
index 0e9553e9d029a..48b2ba5a25594 100644
--- a/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp
@@ -8,7 +8,6 @@
 
 #include "DAP.h"
 #include "EventHelper.h"
-#include "JSONUtils.h"
 #include "RequestHandler.h"
 
 namespace lldb_dap {
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 9be7bf74c476b..e184e7602ef14 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "JSONUtils.h"
-#include "BreakpointBase.h"
 #include "DAP.h"
 #include "ExceptionBreakpoint.h"
 #include "LLDBUtils.h"
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index 61fea66490c30..cfa43b29f8f35 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -301,4 +301,88 @@ bool fromJSON(const llvm::json::Value &Params, StepOutArguments &SOA,
          OM.mapOptional("granularity", SOA.granularity);
 }
 
+bool fromJSON(const llvm::json::Value &Params, SetBreakpointsArguments &SBA,
+              llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("source", SBA.source) &&
+         O.map("breakpoints", SBA.breakpoints) && O.map("lines", SBA.lines) &&
+         O.map("sourceModified", SBA.sourceModified);
+}
+
+llvm::json::Value toJSON(const SetBreakpointsResponseBody &SBR) {
+  json::Object result;
+  result["breakpoints"] = SBR.breakpoints;
+  return result;
+}
+
+bool fromJSON(const llvm::json::Value &Params,
+              SetFunctionBreakpointsArguments &SFBA, llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("breakpoints", SFBA.breakpoints);
+}
+
+llvm::json::Value toJSON(const SetFunctionBreakpointsResponseBody &SFBR) {
+  json::Object result;
+  result["breakpoints"] = SFBR.breakpoints;
+  return result;
+}
+
+bool fromJSON(const llvm::json::Value &Params,
+              SetExceptionBreakpointsArguments &SEBA, llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("filters", SEBA.filters) &&
+         O.map("filterOptions", SEBA.filterOptions) &&
+         O.map("exceptionOptions", SEBA.exceptionOptions);
+}
+
+llvm::json::Value toJSON(const SetExceptionBreakpointsResponseBody &SEBR) {
+  json::Object result;
+  result["breakpoints"] = SEBR.breakpoints;
+  return result;
+}
+
+bool fromJSON(const llvm::json::Value &Params,
+              SetInstructionBreakpointsArguments &SIBA, llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("breakpoints", SIBA.breakpoints);
+}
+
+llvm::json::Value toJSON(const SetInstructionBreakpointsResponseBody &SIBR) {
+  json::Object result;
+  result["breakpoints"] = SIBR.breakpoints;
+  return result;
+}
+
+bool fromJSON(const llvm::json::Value &Params,
+              DataBreakpointInfoArguments &DBIA, llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("variablesReference", DBIA.variablesReference) &&
+         O.map("name", DBIA.name) && O.map("frameId", DBIA.frameId) &&
+         O.map("bytes", DBIA.bytes) && O.map("asAddress", DBIA.asAddress) &&
+         O.map("mode", DBIA.mode);
+}
+
+llvm::json::Value toJSON(const DataBreakpointInfoResponseBody &DBIRB) {
+  json::Object result;
+  result["dataId"] = DBIRB.dataId ? *DBIRB.dataId : llvm::json::Value(nullptr);
+  result["description"] = DBIRB.description;
+  if (DBIRB.accessTypes)
+    result["accessTypes"] = *DBIRB.accessTypes;
+  if (DBIRB.canPersist)
+    result["canPersist"] = *DBIRB.canPersist;
+  return result;
+}
+
+bool fromJSON(const llvm::json::Value &Params,
+              SetDataBreakpointsArguments &SDBA, llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("breakpoints", SDBA.breakpoints);
+}
+
+llvm::json::Value toJSON(const SetDataBreakpointsResponseBody &SDBR) {
+  json::Object result;
+  result["breakpoints"] = SDBR.breakpoints;
+  return result;
+}
+
 } // namespace lldb_dap::protocol
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index 93d9931575dfe..31a4522513722 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -393,6 +393,18 @@ bool fromJSON(const llvm::json::Value &Params, DataBreakpointAccessType &DBAT,
   return true;
 }
 
+llvm::json::Value toJSON(const DataBreakpointAccessType &DBAT) {
+  switch (DBAT) {
+  case eDataBreakpointAccessTypeRead:
+    return "read";
+  case eDataBreakpointAccessTypeWrite:
+    return "write";
+  case eDataBreakpointAccessTypeReadWrite:
+    return "readWrite";
+  }
+  llvm_unreachable("unhandled data breakpoint access type.");
+}
+
 bool fromJSON(const llvm::json::Value &Params, DataBreakpointInfo &DBI,
               llvm::json::Path P) {
   json::ObjectMapper O(Params, P);
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index 3ed28342bd25e..8bbb64ab08748 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -273,7 +273,6 @@ enum PresentationHint : unsigned {
   ePresentationHintEmphasize,
   ePresentationHintDeemphasize,
 };
-llvm::json::Value toJSON(const PresentationHint &);
 
 /// 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
@@ -453,6 +452,7 @@ enum DataBreakpointAccessType : unsigned {
 };
 bool fromJSON(const llvm::json::Value &, DataBreakpointAccessType &,
               llvm::json::Path);
+llvm::json::Value toJSON(const DataBreakpointAccessType &);
 
 /// Properties of a data breakpoint passed to the `setDataBreakpoints` request.
 struct DataBreakpointInfo {
diff --git a/lldb/tools/lldb-dap/Watchpoint.cpp b/lldb/tools/lldb-dap/Watchpoint.cpp
index 72f913d465408..73ed4fdbae1b8 100644
--- a/lldb/tools/lldb-dap/Watchpoint.cpp
+++ b/lldb/tools/lldb-dap/Watchpoint.cpp
@@ -46,6 +46,8 @@ protocol::Breakpoint Watchpoint::ToProtocolBreakpoint() {
   } else {
     breakpoint.verified = true;
   }
+
+  return breakpoint;
 }
 
 void Watchpoint::SetWatchpoint() {

>From c7d1bbe1835d59ede2d493c9f890fda48b3e119d Mon Sep 17 00:00:00 2001
From: Ely Ronnen <elyronnen at gmail.com>
Date: Sat, 26 Apr 2025 20:10:08 +0200
Subject: [PATCH 10/10] fix

---
 lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index 31a4522513722..e93ea05f8cb9a 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -61,10 +61,10 @@ llvm::json::Value toJSON(const Source &S) {
     result.insert({"name", *S.name});
   if (S.path)
     result.insert({"path", *S.path});
-  if (S.name)
+  if (S.sourceReference)
     result.insert({"sourceReference", *S.sourceReference});
   if (S.presentationHint)
-    result.insert({"sourceReference", ToString(*S.presentationHint)});
+    result.insert({"presentationHint", ToString(*S.presentationHint)});
 
   return result;
 }



More information about the lldb-commits mailing list