[Lldb-commits] [lldb] 6526cda - [lldb-dap] Migrating DAP 'initialize' to new typed RequestHandler. (#133007)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Mar 28 09:13:15 PDT 2025
Author: John Harrison
Date: 2025-03-28T09:13:10-07:00
New Revision: 6526cda5d865d55b1db5aa0faffb29448e5c6a23
URL: https://github.com/llvm/llvm-project/commit/6526cda5d865d55b1db5aa0faffb29448e5c6a23
DIFF: https://github.com/llvm/llvm-project/commit/6526cda5d865d55b1db5aa0faffb29448e5c6a23.diff
LOG: [lldb-dap] Migrating DAP 'initialize' to new typed RequestHandler. (#133007)
This adds new types and helpers to support the 'initialize' request with
the new typed RequestHandler. While working on this I found there were a
few cases where we incorrectly treated initialize arguments as
capabilities. The new `lldb_dap::protocol::InitializeRequestArguments`
and `lldb_dap::protocol::Capabilities` uncovered the inconsistencies.
---------
Co-authored-by: Jonas Devlieghere <jonas at devlieghere.com>
Added:
Modified:
lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
lldb/tools/lldb-dap/DAP.cpp
lldb/tools/lldb-dap/DAP.h
lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
lldb/tools/lldb-dap/Handler/RequestHandler.cpp
lldb/tools/lldb-dap/Handler/RequestHandler.h
lldb/tools/lldb-dap/JSONUtils.cpp
lldb/tools/lldb-dap/JSONUtils.h
lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
Removed:
################################################################################
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index 359ac718138b2..01ef4b68f2653 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -776,7 +776,8 @@ def request_initialize(self, sourceInitFile):
"supportsVariablePaging": True,
"supportsVariableType": True,
"supportsStartDebuggingRequest": True,
- "sourceInitFile": sourceInitFile,
+ "supportsProgressReporting": True,
+ "$__lldb_sourceInitFile": sourceInitFile,
},
}
response = self.send_recv(command_dict)
@@ -1261,7 +1262,7 @@ def launch(cls, /, executable, env=None, log_file=None, connection=None):
expected_prefix = "Listening for: "
out = process.stdout.readline().decode()
if not out.startswith(expected_prefix):
- self.process.kill()
+ process.kill()
raise ValueError(
"lldb-dap failed to print listening address, expected '{}', got '{}'".format(
expected_prefix, out
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
index 0c92e5bff07c6..64c99019a1c9b 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
@@ -524,8 +524,7 @@ def test_version(self):
# The first line is the prompt line like "(lldb) version", so we skip it.
version_eval_output_without_prompt_line = version_eval_output.splitlines()[1:]
- lldb_json = self.dap_server.get_initialize_value("__lldb")
- version_string = lldb_json["version"]
+ version_string = self.dap_server.get_initialize_value("$__lldb_version")
self.assertEqual(
version_eval_output_without_prompt_line,
version_string.splitlines(),
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 65de0488729e5..23f0400c8bd4d 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -14,6 +14,7 @@
#include "LLDBUtils.h"
#include "OutputRedirector.h"
#include "Protocol/ProtocolBase.h"
+#include "Protocol/ProtocolTypes.h"
#include "Transport.h"
#include "lldb/API/SBBreakpoint.h"
#include "lldb/API/SBCommandInterpreter.h"
@@ -1144,32 +1145,41 @@ lldb::SBValue Variables::FindVariable(uint64_t variablesReference,
return variable;
}
-llvm::StringMap<bool> DAP::GetCapabilities() {
- llvm::StringMap<bool> capabilities;
-
- // Supported capabilities.
- capabilities["supportTerminateDebuggee"] = true;
- capabilities["supportsDataBreakpoints"] = true;
- capabilities["supportsDelayedStackTraceLoading"] = true;
- capabilities["supportsEvaluateForHovers"] = true;
- capabilities["supportsExceptionOptions"] = true;
- capabilities["supportsLogPoints"] = true;
- capabilities["supportsProgressReporting"] = true;
- capabilities["supportsSteppingGranularity"] = true;
- capabilities["supportsValueFormattingOptions"] = true;
-
- // Unsupported capabilities.
- capabilities["supportsGotoTargetsRequest"] = false;
- capabilities["supportsLoadedSourcesRequest"] = false;
- capabilities["supportsRestartFrame"] = false;
- capabilities["supportsStepBack"] = false;
+protocol::Capabilities DAP::GetCapabilities() {
+ protocol::Capabilities capabilities;
+
+ // Supported capabilities that are not specific to a single request.
+ capabilities.supportedFeatures = {
+ protocol::eAdapterFeatureLogPoints,
+ protocol::eAdapterFeatureSteppingGranularity,
+ protocol::eAdapterFeatureValueFormattingOptions,
+ };
// Capabilities associated with specific requests.
for (auto &kv : request_handlers) {
- for (auto &request_kv : kv.second->GetCapabilities())
- capabilities[request_kv.getKey()] = request_kv.getValue();
+ llvm::SmallDenseSet<AdapterFeature, 1> features =
+ kv.second->GetSupportedFeatures();
+ capabilities.supportedFeatures.insert(features.begin(), features.end());
}
+ // Available filters or options for the setExceptionBreakpoints request.
+ std::vector<protocol::ExceptionBreakpointsFilter> filters;
+ for (const auto &exc_bp : *exception_breakpoints)
+ filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp));
+ capabilities.exceptionBreakpointFilters = std::move(filters);
+
+ // FIXME: This should be registered based on the supported languages?
+ std::vector<std::string> completion_characters;
+ completion_characters.emplace_back(".");
+ // FIXME: I wonder if we should remove this key... its very aggressive
+ // triggering and accepting completions.
+ completion_characters.emplace_back(" ");
+ completion_characters.emplace_back("\t");
+ capabilities.completionTriggerCharacters = std::move(completion_characters);
+
+ // Put in non-DAP specification lldb specific information.
+ capabilities.lldbExtVersion = debugger.GetVersionString();
+
return capabilities;
}
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 4b4d471161137..6689980806047 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -16,6 +16,8 @@
#include "OutputRedirector.h"
#include "ProgressEvent.h"
#include "Protocol/ProtocolBase.h"
+#include "Protocol/ProtocolRequests.h"
+#include "Protocol/ProtocolTypes.h"
#include "SourceBreakpoint.h"
#include "Transport.h"
#include "lldb/API/SBBroadcaster.h"
@@ -58,6 +60,9 @@ typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap;
typedef llvm::DenseMap<lldb::addr_t, InstructionBreakpoint>
InstructionBreakpointMap;
+using AdapterFeature = protocol::AdapterFeature;
+using ClientFeature = protocol::ClientFeature;
+
enum class OutputType { Console, Stdout, Stderr, Telemetry };
/// Buffer size for handling output events.
@@ -205,6 +210,8 @@ struct DAP {
// empty; if the previous expression was a variable expression, this string
// will contain that expression.
std::string last_nonempty_var_expression;
+ /// The set of features supported by the connected client.
+ llvm::DenseSet<ClientFeature> clientFeatures;
/// Creates a new DAP sessions.
///
@@ -363,8 +370,8 @@ struct DAP {
request_handlers[Handler::GetCommand()] = std::make_unique<Handler>(*this);
}
- /// Return a key-value list of capabilities.
- llvm::StringMap<bool> GetCapabilities();
+ /// The set of capablities supported by this adapter.
+ protocol::Capabilities GetCapabilities();
/// Debuggee will continue from stopped state.
void WillContinue() { variables.Clear(); }
diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
index a8fe0d6ffce8b..e7c565aad13a3 100644
--- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
@@ -9,12 +9,14 @@
#include "DAP.h"
#include "EventHelper.h"
#include "JSONUtils.h"
+#include "Protocol/ProtocolRequests.h"
#include "RequestHandler.h"
#include "lldb/API/SBEvent.h"
#include "lldb/API/SBListener.h"
#include "lldb/API/SBStream.h"
using namespace lldb;
+using namespace lldb_dap::protocol;
namespace lldb_dap {
@@ -229,118 +231,31 @@ static void EventThreadFunction(DAP &dap) {
}
}
-// "InitializeRequest": {
-// "allOf": [ { "$ref": "#/definitions/Request" }, {
-// "type": "object",
-// "description": "Initialize request; value of command field is
-// 'initialize'.",
-// "properties": {
-// "command": {
-// "type": "string",
-// "enum": [ "initialize" ]
-// },
-// "arguments": {
-// "$ref": "#/definitions/InitializeRequestArguments"
-// }
-// },
-// "required": [ "command", "arguments" ]
-// }]
-// },
-// "InitializeRequestArguments": {
-// "type": "object",
-// "description": "Arguments for 'initialize' request.",
-// "properties": {
-// "clientID": {
-// "type": "string",
-// "description": "The ID of the (frontend) client using this adapter."
-// },
-// "adapterID": {
-// "type": "string",
-// "description": "The ID of the debug adapter."
-// },
-// "locale": {
-// "type": "string",
-// "description": "The ISO-639 locale of the (frontend) client using
-// this adapter, e.g. en-US or de-CH."
-// },
-// "linesStartAt1": {
-// "type": "boolean",
-// "description": "If true all line numbers are 1-based (default)."
-// },
-// "columnsStartAt1": {
-// "type": "boolean",
-// "description": "If true all column numbers are 1-based (default)."
-// },
-// "pathFormat": {
-// "type": "string",
-// "_enum": [ "path", "uri" ],
-// "description": "Determines in what format paths are specified. The
-// default is 'path', which is the native format."
-// },
-// "supportsVariableType": {
-// "type": "boolean",
-// "description": "Client supports the optional type attribute for
-// variables."
-// },
-// "supportsVariablePaging": {
-// "type": "boolean",
-// "description": "Client supports the paging of variables."
-// },
-// "supportsRunInTerminalRequest": {
-// "type": "boolean",
-// "description": "Client supports the runInTerminal request."
-// }
-// },
-// "required": [ "adapterID" ]
-// },
-// "InitializeResponse": {
-// "allOf": [ { "$ref": "#/definitions/Response" }, {
-// "type": "object",
-// "description": "Response to 'initialize' request.",
-// "properties": {
-// "body": {
-// "$ref": "#/definitions/Capabilities",
-// "description": "The capabilities of this debug adapter."
-// }
-// }
-// }]
-// }
-void InitializeRequestHandler::operator()(
- const llvm::json::Object &request) const {
- llvm::json::Object response;
- FillResponse(request, response);
- llvm::json::Object body;
-
- const auto *arguments = request.getObject("arguments");
- // sourceInitFile option is not from formal DAP specification. It is only
- // used by unit tests to prevent sourcing .lldbinit files from environment
- // which may affect the outcome of tests.
- bool source_init_file =
- GetBoolean(arguments, "sourceInitFile").value_or(true);
+/// Initialize request; value of command field is 'initialize'.
+llvm::Expected<InitializeResponseBody> InitializeRequestHandler::Run(
+ const InitializeRequestArguments &arguments) const {
+ dap.clientFeatures = arguments.supportedFeatures;
// Do not source init files until in/out/err are configured.
dap.debugger = lldb::SBDebugger::Create(false);
dap.debugger.SetInputFile(dap.in);
- auto out_fd = dap.out.GetWriteFileDescriptor();
- if (llvm::Error err = out_fd.takeError()) {
- response["success"] = false;
- EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
- dap.SendJSON(llvm::json::Value(std::move(response)));
- return;
- }
+
+ llvm::Expected<int> out_fd = dap.out.GetWriteFileDescriptor();
+ if (!out_fd)
+ return out_fd.takeError();
dap.debugger.SetOutputFile(lldb::SBFile(*out_fd, "w", false));
- auto err_fd = dap.err.GetWriteFileDescriptor();
- if (llvm::Error err = err_fd.takeError()) {
- response["success"] = false;
- EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
- dap.SendJSON(llvm::json::Value(std::move(response)));
- return;
- }
+
+ llvm::Expected<int> err_fd = dap.err.GetWriteFileDescriptor();
+ if (!err_fd)
+ return err_fd.takeError();
dap.debugger.SetErrorFile(lldb::SBFile(*err_fd, "w", false));
auto interp = dap.debugger.GetCommandInterpreter();
- if (source_init_file) {
+ // The sourceInitFile option is not part of the DAP specification. It is an
+ // extension used by the test suite to prevent sourcing `.lldbinit` and
+ // changing its behavior.
+ if (arguments.lldbExtSourceInitFile.value_or(true)) {
dap.debugger.SkipLLDBInitFiles(false);
dap.debugger.SkipAppInitFiles(false);
lldb::SBCommandReturnObject init;
@@ -348,17 +263,14 @@ void InitializeRequestHandler::operator()(
interp.SourceInitFileInHomeDirectory(init);
}
- if (llvm::Error err = dap.RunPreInitCommands()) {
- response["success"] = false;
- EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
- dap.SendJSON(llvm::json::Value(std::move(response)));
- return;
- }
+ if (llvm::Error err = dap.RunPreInitCommands())
+ return err;
dap.PopulateExceptionBreakpoints();
auto cmd = dap.debugger.GetCommandInterpreter().AddMultiwordCommand(
"lldb-dap", "Commands for managing lldb-dap.");
- if (GetBoolean(arguments, "supportsStartDebuggingRequest").value_or(false)) {
+ if (arguments.supportedFeatures.contains(
+ eClientFeatureStartDebuggingRequest)) {
cmd.AddCommand(
"start-debugging", new StartDebuggingRequestHandler(dap),
"Sends a startDebugging request from the debug adapter to the client "
@@ -370,37 +282,15 @@ void InitializeRequestHandler::operator()(
cmd.AddCommand("send-event", new SendEventRequestHandler(dap),
"Sends an DAP event to the client.");
- dap.progress_event_thread =
- std::thread(ProgressEventThreadFunction, std::ref(dap));
+ if (arguments.supportedFeatures.contains(eClientFeatureProgressReporting))
+ dap.progress_event_thread =
+ std::thread(ProgressEventThreadFunction, std::ref(dap));
// Start our event thread so we can receive events from the debugger, target,
// process and more.
dap.event_thread = std::thread(EventThreadFunction, std::ref(dap));
- llvm::StringMap<bool> capabilities = dap.GetCapabilities();
- for (auto &kv : capabilities)
- body.try_emplace(kv.getKey(), kv.getValue());
-
- // Available filters or options for the setExceptionBreakpoints request.
- llvm::json::Array filters;
- for (const auto &exc_bp : *dap.exception_breakpoints)
- filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp));
- body.try_emplace("exceptionBreakpointFilters", std::move(filters));
-
- llvm::json::Array completion_characters;
- completion_characters.emplace_back(".");
- completion_characters.emplace_back(" ");
- completion_characters.emplace_back("\t");
- body.try_emplace("completionTriggerCharacters",
- std::move(completion_characters));
-
- // Put in non-DAP specification lldb specific information.
- llvm::json::Object lldb_json;
- lldb_json.try_emplace("version", dap.debugger.GetVersionString());
- body.try_emplace("__lldb", std::move(lldb_json));
-
- response.try_emplace("body", std::move(body));
- dap.SendJSON(llvm::json::Value(std::move(response)));
+ return dap.GetCapabilities();
}
} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index 60c82649938d6..f067dfc5544fe 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -12,6 +12,7 @@
#include "JSONUtils.h"
#include "LLDBUtils.h"
#include "RunInTerminal.h"
+#include "llvm/Support/Error.h"
#if !defined(_WIN32)
#include <unistd.h>
@@ -97,6 +98,11 @@ void BaseRequestHandler::SetSourceMapFromArguments(
static llvm::Error RunInTerminal(DAP &dap,
const llvm::json::Object &launch_request,
const uint64_t timeout_seconds) {
+ if (!dap.clientFeatures.contains(
+ protocol::eClientFeatureRunInTerminalRequest))
+ return llvm::make_error<DAPError>("Cannot use runInTerminal, feature is "
+ "not supported by the connected client");
+
dap.is_attach = true;
lldb::SBAttachInfo attach_info;
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 8971b02fcb92e..396815b04c84a 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -14,7 +14,9 @@
#include "DAPLog.h"
#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"
#include "llvm/Support/JSON.h"
@@ -47,7 +49,8 @@ class BaseRequestHandler {
virtual void operator()(const protocol::Request &request) const = 0;
- virtual llvm::StringMap<bool> GetCapabilities() const { return {}; }
+ using FeatureSet = llvm::SmallDenseSet<AdapterFeature, 1>;
+ virtual FeatureSet GetSupportedFeatures() const { return {}; }
protected:
/// Helpers used by multiple request handlers.
@@ -175,8 +178,8 @@ class BreakpointLocationsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "breakpointLocations"; }
- llvm::StringMap<bool> GetCapabilities() const override {
- return {{"supportsBreakpointLocationsRequest", true}};
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureBreakpointLocationsRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
@@ -185,8 +188,8 @@ class CompletionsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "completions"; }
- llvm::StringMap<bool> GetCapabilities() const override {
- return {{"supportsCompletionsRequest", true}};
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureCompletionsRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
@@ -202,8 +205,8 @@ class ConfigurationDoneRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "configurationDone"; }
- llvm::StringMap<bool> GetCapabilities() const override {
- return {{"supportsConfigurationDoneRequest", true}};
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureConfigurationDoneRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
@@ -214,6 +217,9 @@ class DisconnectRequestHandler
public:
using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "disconnect"; }
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureTerminateDebuggee};
+ }
llvm::Expected<protocol::DisconnectResponse>
Run(const std::optional<protocol::DisconnectArguments> &args) const override;
};
@@ -223,26 +229,29 @@ class EvaluateRequestHandler : public LegacyRequestHandler {
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "evaluate"; }
void operator()(const llvm::json::Object &request) const override;
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureEvaluateForHovers};
+ }
};
class ExceptionInfoRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "exceptionInfo"; }
- llvm::StringMap<bool> GetCapabilities() const override {
- return {{"supportsExceptionInfoRequest", true}};
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureExceptionInfoRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
-class InitializeRequestHandler : public LegacyRequestHandler {
+class InitializeRequestHandler
+ : public RequestHandler<protocol::InitializeRequestArguments,
+ protocol::InitializeResponseBody> {
public:
- using LegacyRequestHandler::LegacyRequestHandler;
+ using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "initialize"; }
- llvm::StringMap<bool> GetCapabilities() const override {
- return {{"supportsRunInTerminalRequest", true}};
- }
- void operator()(const llvm::json::Object &request) const override;
+ llvm::Expected<protocol::InitializeResponseBody>
+ Run(const protocol::InitializeRequestArguments &args) const override;
};
class LaunchRequestHandler : public LegacyRequestHandler {
@@ -256,8 +265,8 @@ class RestartRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "restart"; }
- llvm::StringMap<bool> GetCapabilities() const override {
- return {{"supportsRestartRequest", true}};
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureRestartRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
@@ -280,8 +289,8 @@ class StepInTargetsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "stepInTargets"; }
- llvm::StringMap<bool> GetCapabilities() const override {
- return {{"supportsStepInTargetsRequest", true}};
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureStepInTargetsRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
@@ -297,9 +306,9 @@ class SetBreakpointsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "setBreakpoints"; }
- llvm::StringMap<bool> GetCapabilities() const override {
- return {{"supportsConditionalBreakpoints", true},
- {"supportsHitConditionalBreakpoints", true}};
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureConditionalBreakpoints,
+ protocol::eAdapterFeatureHitConditionalBreakpoints};
}
void operator()(const llvm::json::Object &request) const override;
};
@@ -308,6 +317,9 @@ class SetExceptionBreakpointsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "setExceptionBreakpoints"; }
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureExceptionOptions};
+ }
void operator()(const llvm::json::Object &request) const override;
};
@@ -315,8 +327,8 @@ class SetFunctionBreakpointsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "setFunctionBreakpoints"; }
- llvm::StringMap<bool> GetCapabilities() const override {
- return {{"supportsFunctionBreakpoints", true}};
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureFunctionBreakpoints};
}
void operator()(const llvm::json::Object &request) const override;
};
@@ -333,6 +345,9 @@ class SetDataBreakpointsRequestHandler : public LegacyRequestHandler {
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "setDataBreakpoints"; }
void operator()(const llvm::json::Object &request) const override;
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureDataBreakpoints};
+ }
};
class SetInstructionBreakpointsRequestHandler : public LegacyRequestHandler {
@@ -342,6 +357,9 @@ class SetInstructionBreakpointsRequestHandler : public LegacyRequestHandler {
return "setInstructionBreakpoints";
}
void operator()(const llvm::json::Object &request) const override;
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureInstructionBreakpoints};
+ }
};
class CompileUnitsRequestHandler : public LegacyRequestHandler {
@@ -355,8 +373,8 @@ class ModulesRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "modules"; }
- llvm::StringMap<bool> GetCapabilities() const override {
- return {{"supportsModulesRequest", true}};
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureModulesRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
@@ -379,8 +397,8 @@ class SetVariableRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "setVariable"; }
- llvm::StringMap<bool> GetCapabilities() const override {
- return {{"supportsSetVariable", true}};
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureSetVariable};
}
void operator()(const llvm::json::Object &request) const override;
};
@@ -400,6 +418,9 @@ class StackTraceRequestHandler : public LegacyRequestHandler {
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "stackTrace"; }
void operator()(const llvm::json::Object &request) const override;
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureDelayedStackTraceLoading};
+ }
};
class ThreadsRequestHandler : public LegacyRequestHandler {
@@ -427,8 +448,8 @@ class DisassembleRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "disassemble"; }
- llvm::StringMap<bool> GetCapabilities() const override {
- return {{"supportsDisassembleRequest", true}};
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureDisassembleRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
@@ -437,8 +458,8 @@ class ReadMemoryRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "readMemory"; }
- llvm::StringMap<bool> GetCapabilities() const override {
- return {{"supportsReadMemoryRequest", true}};
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureReadMemoryRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index ea942c5d65934..9773b91a35a45 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -557,35 +557,13 @@ llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
return event;
}
-// "ExceptionBreakpointsFilter": {
-// "type": "object",
-// "description": "An ExceptionBreakpointsFilter is shown in the UI as an
-// option for configuring how exceptions are dealt with.",
-// "properties": {
-// "filter": {
-// "type": "string",
-// "description": "The internal ID of the filter. This value is passed
-// to the setExceptionBreakpoints request."
-// },
-// "label": {
-// "type": "string",
-// "description": "The name of the filter. This will be shown in the UI."
-// },
-// "default": {
-// "type": "boolean",
-// "description": "Initial value of the filter. If not specified a value
-// 'false' is assumed."
-// }
-// },
-// "required": [ "filter", "label" ]
-// }
-llvm::json::Value
+protocol::ExceptionBreakpointsFilter
CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
- llvm::json::Object object;
- EmplaceSafeString(object, "filter", bp.filter);
- EmplaceSafeString(object, "label", bp.label);
- object.try_emplace("default", bp.default_value);
- return llvm::json::Value(std::move(object));
+ protocol::ExceptionBreakpointsFilter filter;
+ filter.filter = bp.filter;
+ filter.label = bp.label;
+ filter.defaultState = bp.default_value;
+ return filter;
}
// "Source": {
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index a8858020f7d85..5d403d39a76d4 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -10,6 +10,7 @@
#define LLDB_TOOLS_LLDB_DAP_JSONUTILS_H
#include "DAPForward.h"
+#include "Protocol/ProtocolTypes.h"
#include "lldb/API/SBCompileUnit.h"
#include "lldb/API/SBFileSpec.h"
#include "lldb/API/SBFormat.h"
@@ -289,7 +290,7 @@ llvm::json::Object CreateEventObject(const llvm::StringRef event_name);
/// \return
/// A "ExceptionBreakpointsFilter" JSON object with that follows
/// the formal JSON definition outlined by Microsoft.
-llvm::json::Value
+protocol::ExceptionBreakpointsFilter
CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp);
/// Create a "Scope" JSON object as described in the debug adapter definition.
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
index 0ef90eb7d76bd..86e26f4deb111 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
@@ -31,7 +31,11 @@ static bool mapRaw(const json::Value &Params, StringLiteral Prop,
namespace lldb_dap::protocol {
-enum class MessageType { request, response, event };
+enum MessageType {
+ eMessageTypeRequest,
+ eMessageTypeResponse,
+ eMessageTypeEvent
+};
bool fromJSON(const json::Value &Params, MessageType &M, json::Path P) {
auto rawType = Params.getAsString();
@@ -41,9 +45,9 @@ bool fromJSON(const json::Value &Params, MessageType &M, json::Path P) {
}
std::optional<MessageType> type =
StringSwitch<std::optional<MessageType>>(*rawType)
- .Case("request", MessageType::request)
- .Case("response", MessageType::response)
- .Case("event", MessageType::event)
+ .Case("request", eMessageTypeRequest)
+ .Case("response", eMessageTypeResponse)
+ .Case("event", eMessageTypeEvent)
.Default(std::nullopt);
if (!type) {
P.report("unexpected value, expected 'request', 'response' or 'event'");
@@ -76,7 +80,7 @@ bool fromJSON(json::Value const &Params, Request &R, json::Path P) {
!O.map("seq", R.seq))
return false;
- if (type != MessageType::request) {
+ if (type != eMessageTypeRequest) {
P.field("type").report("expected to be 'request'");
return false;
}
@@ -154,7 +158,7 @@ bool fromJSON(json::Value const &Params, Response &R, json::Path P) {
!O.map("command", R.command) || !O.map("request_seq", R.request_seq))
return false;
- if (type != MessageType::response) {
+ if (type != eMessageTypeResponse) {
P.field("type").report("expected to be 'response'");
return false;
}
@@ -231,7 +235,7 @@ bool fromJSON(json::Value const &Params, Event &E, json::Path P) {
if (!O.map("type", type) || !O.map("seq", seq) || !O.map("event", E.event))
return false;
- if (type != MessageType::event) {
+ if (type != eMessageTypeEvent) {
P.field("type").report("expected to be 'event'");
return false;
}
@@ -259,21 +263,21 @@ bool fromJSON(const json::Value &Params, Message &PM, json::Path P) {
return false;
switch (type) {
- case MessageType::request: {
+ case eMessageTypeRequest: {
Request req;
if (!fromJSON(Params, req, P))
return false;
PM = std::move(req);
return true;
}
- case MessageType::response: {
+ case eMessageTypeResponse: {
Response resp;
if (!fromJSON(Params, resp, P))
return false;
PM = std::move(resp);
return true;
}
- case MessageType::event:
+ case eMessageTypeEvent:
Event evt;
if (!fromJSON(Params, evt, P))
return false;
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index 5cc5429227439..9a613128739c2 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -7,6 +7,8 @@
//===----------------------------------------------------------------------===//
#include "Protocol/ProtocolRequests.h"
+#include "DAP.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/JSON.h"
#include <utility>
@@ -23,6 +25,63 @@ bool fromJSON(const json::Value &Params, DisconnectArguments &DA,
O.mapOptional("suspendDebuggee", DA.suspendDebuggee);
}
+bool fromJSON(const llvm::json::Value &Params, PathFormat &PF,
+ llvm::json::Path P) {
+ auto rawPathFormat = Params.getAsString();
+ if (!rawPathFormat) {
+ P.report("expected a string");
+ return false;
+ }
+
+ std::optional<PathFormat> pathFormat =
+ StringSwitch<std::optional<PathFormat>>(*rawPathFormat)
+ .Case("path", ePatFormatPath)
+ .Case("uri", ePathFormatURI)
+ .Default(std::nullopt);
+ if (!pathFormat) {
+ P.report("unexpected value, expected 'path' or 'uri'");
+ return false;
+ }
+
+ PF = *pathFormat;
+ return true;
+}
+
+static const llvm::StringMap<ClientFeature> ClientFeatureByKey{
+ {"supportsVariableType", eClientFeatureVariableType},
+ {"supportsVariablePaging", eClientFeatureVariablePaging},
+ {"supportsRunInTerminalRequest", eClientFeatureRunInTerminalRequest},
+ {"supportsMemoryReferences", eClientFeatureMemoryReferences},
+ {"supportsProgressReporting", eClientFeatureProgressReporting},
+ {"supportsInvalidatedEvent", eClientFeatureInvalidatedEvent},
+ {"supportsMemoryEvent", eClientFeatureMemoryEvent},
+ {"supportsArgsCanBeInterpretedByShell",
+ eClientFeatureArgsCanBeInterpretedByShell},
+ {"supportsStartDebuggingRequest", eClientFeatureStartDebuggingRequest},
+ {"supportsANSIStyling", eClientFeatureANSIStyling}};
+
+bool fromJSON(const llvm::json::Value &Params, InitializeRequestArguments &IRA,
+ llvm::json::Path P) {
+ json::ObjectMapper OM(Params, P);
+ if (!OM)
+ return false;
+
+ const json::Object *O = Params.getAsObject();
+
+ for (auto &kv : ClientFeatureByKey)
+ if (std::optional<bool> v = O->getBoolean(kv.first()); v && *v)
+ IRA.supportedFeatures.insert(kv.second);
+
+ return OM.mapOptional("adatperID", IRA.adatperID) &&
+ OM.mapOptional("clientID", IRA.clientID) &&
+ OM.mapOptional("clientName", IRA.clientName) &&
+ OM.mapOptional("locale", IRA.locale) &&
+ OM.mapOptional("linesStartAt1", IRA.linesStartAt1) &&
+ OM.mapOptional("columnsStartAt1", IRA.columnsStartAt1) &&
+ OM.mapOptional("pathFormat", IRA.pathFormat) &&
+ OM.mapOptional("$__lldb_sourceInitFile", IRA.lldbExtSourceInitFile);
+}
+
bool fromJSON(const json::Value &Params, SourceArguments &SA, json::Path P) {
json::ObjectMapper O(Params, P);
return O && O.mapOptional("source", SA.source) &&
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index 5dc4a589178d2..c49a13711f8c7 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -54,6 +54,69 @@ bool fromJSON(const llvm::json::Value &, DisconnectArguments &,
/// body field is required.
using DisconnectResponse = VoidResponse;
+/// Features supported by DAP clients.
+enum ClientFeature {
+ eClientFeatureVariableType,
+ eClientFeatureVariablePaging,
+ eClientFeatureRunInTerminalRequest,
+ eClientFeatureMemoryReferences,
+ eClientFeatureProgressReporting,
+ eClientFeatureInvalidatedEvent,
+ eClientFeatureMemoryEvent,
+ /// Client supports the `argsCanBeInterpretedByShell` attribute on the
+ /// `runInTerminal` request.
+ eClientFeatureArgsCanBeInterpretedByShell,
+ eClientFeatureStartDebuggingRequest,
+ /// The client will interpret ANSI escape sequences in the display of
+ /// `OutputEvent.output` and `Variable.value` fields when
+ /// `Capabilities.supportsANSIStyling` is also enabled.
+ eClientFeatureANSIStyling,
+};
+
+/// Format of paths reported by the debug adapter.
+enum PathFormat { ePatFormatPath, ePathFormatURI };
+
+/// Arguments for `initialize` request.
+struct InitializeRequestArguments {
+ /// The ID of the debug adapter.
+ std::string adatperID;
+
+ /// The ID of the client using this adapter.
+ std::optional<std::string> clientID;
+
+ /// The human-readable name of the client using this adapter.
+ std::optional<std::string> clientName;
+
+ /// The ISO-639 locale of the client using this adapter, e.g. en-US or de-CH.
+ std::optional<std::string> locale;
+
+ /// Determines in what format paths are specified. The default is `path`,
+ /// which is the native format.
+ std::optional<PathFormat> pathFormat = ePatFormatPath;
+
+ /// If true all line numbers are 1-based (default).
+ std::optional<bool> linesStartAt1;
+
+ /// If true all column numbers are 1-based (default).
+ std::optional<bool> columnsStartAt1;
+
+ /// The set of supported features reported by the client.
+ llvm::DenseSet<ClientFeature> supportedFeatures;
+
+ /// lldb-dap Extensions
+ /// @{
+
+ /// Source init files when initializing lldb::SBDebugger.
+ std::optional<bool> lldbExtSourceInitFile;
+
+ /// @}
+};
+bool fromJSON(const llvm::json::Value &, InitializeRequestArguments &,
+ llvm::json::Path);
+
+/// Response to `initialize` request. The capabilities of this debug adapter.
+using InitializeResponseBody = std::optional<Capabilities>;
+
/// Arguments for `source` request.
struct SourceArguments {
/// Specifies the source content to load. Either `source.path` or
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index efb5c3abe32bf..d1dd9ad9c5fee 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -16,18 +16,17 @@ using namespace llvm;
namespace lldb_dap::protocol {
-bool fromJSON(const json::Value &Params, Source::PresentationHint &PH,
- json::Path P) {
+bool fromJSON(const json::Value &Params, PresentationHint &PH, json::Path P) {
auto rawHint = Params.getAsString();
if (!rawHint) {
P.report("expected a string");
return false;
}
- std::optional<Source::PresentationHint> hint =
- StringSwitch<std::optional<Source::PresentationHint>>(*rawHint)
- .Case("normal", Source::PresentationHint::normal)
- .Case("emphasize", Source::PresentationHint::emphasize)
- .Case("deemphasize", Source::PresentationHint::deemphasize)
+ std::optional<PresentationHint> hint =
+ StringSwitch<std::optional<PresentationHint>>(*rawHint)
+ .Case("normal", ePresentationHintNormal)
+ .Case("emphasize", ePresentationHintEmphasize)
+ .Case("deemphasize", ePresentationHintDeemphasize)
.Default(std::nullopt);
if (!hint) {
P.report("unexpected value");
@@ -44,4 +43,190 @@ bool fromJSON(const json::Value &Params, Source &S, json::Path P) {
O.mapOptional("sourceReference", S.sourceReference);
}
+json::Value toJSON(const ExceptionBreakpointsFilter &EBF) {
+ json::Object result{{"filter", EBF.filter}, {"label", EBF.label}};
+
+ if (EBF.description)
+ result.insert({"description", *EBF.description});
+ if (EBF.defaultState)
+ result.insert({"default", *EBF.defaultState});
+ if (EBF.supportsCondition)
+ result.insert({"supportsCondition", *EBF.supportsCondition});
+ if (EBF.conditionDescription)
+ result.insert({"conditionDescription", *EBF.conditionDescription});
+
+ return result;
+}
+
+json::Value toJSON(const ColumnType &T) {
+ switch (T) {
+ case eColumnTypeString:
+ return "string";
+ case eColumnTypeNumber:
+ return "number";
+ case eColumnTypeBoolean:
+ return "boolean";
+ case eColumnTypeTimestamp:
+ return "unixTimestampUTC";
+ }
+}
+
+json::Value toJSON(const ColumnDescriptor &CD) {
+ json::Object result{{"attributeName", CD.attributeName}, {"label", CD.label}};
+
+ if (CD.format)
+ result.insert({"format", *CD.format});
+ if (CD.type)
+ result.insert({"type", *CD.type});
+ if (CD.width)
+ result.insert({"width", *CD.width});
+
+ return result;
+}
+
+json::Value toJSON(const ChecksumAlgorithm &CA) {
+ switch (CA) {
+ case eChecksumAlgorithmMD5:
+ return "MD5";
+ case eChecksumAlgorithmSHA1:
+ return "SHA1";
+ case eChecksumAlgorithmSHA256:
+ return "SHA256";
+ case eChecksumAlgorithmTimestamp:
+ return "timestamp";
+ }
+}
+
+json::Value toJSON(const BreakpointModeApplicability &BMA) {
+ switch (BMA) {
+ case eBreakpointModeApplicabilitySource:
+ return "source";
+ case eBreakpointModeApplicabilityException:
+ return "exception";
+ case eBreakpointModeApplicabilityData:
+ return "data";
+ case eBreakpointModeApplicabilityInstruction:
+ return "instruction";
+ }
+}
+
+json::Value toJSON(const BreakpointMode &BM) {
+ json::Object result{
+ {"mode", BM.mode},
+ {"label", BM.label},
+ {"appliesTo", BM.appliesTo},
+ };
+
+ if (BM.description)
+ result.insert({"description", *BM.description});
+
+ return result;
+}
+
+static llvm::StringLiteral ToString(AdapterFeature feature) {
+ switch (feature) {
+ case eAdapterFeatureANSIStyling:
+ return "supportsANSIStyling";
+ case eAdapterFeatureBreakpointLocationsRequest:
+ return "supportsBreakpointLocationsRequest";
+ case eAdapterFeatureCancelRequest:
+ return "supportsCancelRequest";
+ case eAdapterFeatureClipboardContext:
+ return "supportsClipboardContext";
+ case eAdapterFeatureCompletionsRequest:
+ return "supportsCompletionsRequest";
+ case eAdapterFeatureConditionalBreakpoints:
+ return "supportsConditionalBreakpoints";
+ case eAdapterFeatureConfigurationDoneRequest:
+ return "supportsConfigurationDoneRequest";
+ case eAdapterFeatureDataBreakpointBytes:
+ return "supportsDataBreakpointBytes";
+ case eAdapterFeatureDataBreakpoints:
+ return "supportsDataBreakpoints";
+ case eAdapterFeatureDelayedStackTraceLoading:
+ return "supportsDelayedStackTraceLoading";
+ case eAdapterFeatureDisassembleRequest:
+ return "supportsDisassembleRequest";
+ case eAdapterFeatureEvaluateForHovers:
+ return "supportsEvaluateForHovers";
+ case eAdapterFeatureExceptionFilterOptions:
+ return "supportsExceptionFilterOptions";
+ case eAdapterFeatureExceptionInfoRequest:
+ return "supportsExceptionInfoRequest";
+ case eAdapterFeatureExceptionOptions:
+ return "supportsExceptionOptions";
+ case eAdapterFeatureFunctionBreakpoints:
+ return "supportsFunctionBreakpoints";
+ case eAdapterFeatureGotoTargetsRequest:
+ return "supportsGotoTargetsRequest";
+ case eAdapterFeatureHitConditionalBreakpoints:
+ return "supportsHitConditionalBreakpoints";
+ case eAdapterFeatureInstructionBreakpoints:
+ return "supportsInstructionBreakpoints";
+ case eAdapterFeatureLoadedSourcesRequest:
+ return "supportsLoadedSourcesRequest";
+ case eAdapterFeatureLogPoints:
+ return "supportsLogPoints";
+ case eAdapterFeatureModulesRequest:
+ return "supportsModulesRequest";
+ case eAdapterFeatureReadMemoryRequest:
+ return "supportsReadMemoryRequest";
+ case eAdapterFeatureRestartFrame:
+ return "supportsRestartFrame";
+ case eAdapterFeatureRestartRequest:
+ return "supportsRestartRequest";
+ case eAdapterFeatureSetExpression:
+ return "supportsSetExpression";
+ case eAdapterFeatureSetVariable:
+ return "supportsSetVariable";
+ case eAdapterFeatureSingleThreadExecutionRequests:
+ return "supportsSingleThreadExecutionRequests";
+ case eAdapterFeatureStepBack:
+ return "supportsStepBack";
+ case eAdapterFeatureStepInTargetsRequest:
+ return "supportsStepInTargetsRequest";
+ case eAdapterFeatureSteppingGranularity:
+ return "supportsSteppingGranularity";
+ case eAdapterFeatureTerminateRequest:
+ return "supportsTerminateRequest";
+ case eAdapterFeatureTerminateThreadsRequest:
+ return "supportsTerminateThreadsRequest";
+ case eAdapterFeatureSuspendDebuggee:
+ return "supportSuspendDebuggee";
+ case eAdapterFeatureValueFormattingOptions:
+ return "supportsValueFormattingOptions";
+ case eAdapterFeatureWriteMemoryRequest:
+ return "supportsWriteMemoryRequest";
+ case eAdapterFeatureTerminateDebuggee:
+ return "supportTerminateDebuggee";
+ }
+}
+
+json::Value toJSON(const Capabilities &C) {
+ json::Object result;
+
+ for (const auto &feature : C.supportedFeatures)
+ result.insert({ToString(feature), true});
+
+ if (C.exceptionBreakpointFilters && !C.exceptionBreakpointFilters->empty())
+ result.insert(
+ {"exceptionBreakpointFilters", *C.exceptionBreakpointFilters});
+ if (C.completionTriggerCharacters && !C.completionTriggerCharacters->empty())
+ result.insert(
+ {"completionTriggerCharacters", *C.completionTriggerCharacters});
+ if (C.additionalModuleColumns && !C.additionalModuleColumns->empty())
+ result.insert({"additionalModuleColumns", *C.additionalModuleColumns});
+ if (C.supportedChecksumAlgorithms && !C.supportedChecksumAlgorithms->empty())
+ result.insert(
+ {"supportedChecksumAlgorithms", *C.supportedChecksumAlgorithms});
+ if (C.breakpointModes && !C.breakpointModes->empty())
+ result.insert({"breakpointModes", *C.breakpointModes});
+
+ // lldb-dap extensions
+ if (C.lldbExtVersion && !C.lldbExtVersion->empty())
+ result.insert({"$__lldb_version", *C.lldbExtVersion});
+
+ return result;
+}
+
} // namespace lldb_dap::protocol
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index b54d76cb29a77..934368aa2435f 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -20,6 +20,7 @@
#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H
#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H
+#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/JSON.h"
#include <cstdint>
#include <optional>
@@ -27,12 +28,256 @@
namespace lldb_dap::protocol {
+/// An `ExceptionBreakpointsFilter` is shown in the UI as an filter option for
+/// configuring how exceptions are dealt with.
+struct ExceptionBreakpointsFilter {
+ /// The internal ID of the filter option. This value is passed to the
+ /// `setExceptionBreakpoints` request.
+ std::string filter;
+
+ /// The name of the filter option. This is shown in the UI.
+ std::string label;
+
+ /// A help text providing additional information about the exception filter.
+ /// This string is typically shown as a hover and can be translated.
+ std::optional<std::string> description;
+
+ /// Initial value of the filter option. If not specified a value false is
+ /// assumed.
+ std::optional<bool> defaultState;
+
+ /// Controls whether a condition can be specified for this filter option. If
+ /// false or missing, a condition can not be set.
+ std::optional<bool> supportsCondition;
+
+ /// A help text providing information about the condition. This string is
+ /// shown as the placeholder text for a text box and can be translated.
+ std::optional<std::string> conditionDescription;
+};
+llvm::json::Value toJSON(const ExceptionBreakpointsFilter &);
+
+enum ColumnType {
+ eColumnTypeString,
+ eColumnTypeNumber,
+ eColumnTypeBoolean,
+ eColumnTypeTimestamp
+};
+
+/// A ColumnDescriptor specifies what module attribute to show in a column of
+/// the modules view, how to format it, and what the column’s label should be.
+///
+/// It is only used if the underlying UI actually supports this level of
+/// customization.
+struct ColumnDescriptor {
+ /// Name of the attribute rendered in this column.
+ std::string attributeName;
+
+ /// Header UI label of column.
+ std::string label;
+
+ /// Format to use for the rendered values in this column. TBD how the format
+ /// strings looks like.
+ std::optional<std::string> format;
+
+ /// Datatype of values in this column. Defaults to `string` if not specified.
+ /// Values: 'string', 'number', 'boolean', 'unixTimestampUTC'.
+ std::optional<ColumnType> type;
+
+ /// Width of this column in characters (hint only).
+ std::optional<int> width;
+};
+llvm::json::Value toJSON(const ColumnDescriptor &);
+
+/// Names of checksum algorithms that may be supported by a debug adapter.
+/// Values: ‘MD5’, ‘SHA1’, ‘SHA256’, ‘timestamp’.
+enum ChecksumAlgorithm {
+ eChecksumAlgorithmMD5,
+ eChecksumAlgorithmSHA1,
+ eChecksumAlgorithmSHA256,
+ eChecksumAlgorithmTimestamp
+};
+llvm::json::Value toJSON(const ChecksumAlgorithm &);
+
+/// Describes one or more type of breakpoint a BreakpointMode applies to. This
+/// is a non-exhaustive enumeration and may expand as future breakpoint types
+/// are added.
+enum BreakpointModeApplicability {
+ /// In `SourceBreakpoint`'s.
+ eBreakpointModeApplicabilitySource,
+ /// In exception breakpoints applied in the `ExceptionFilterOptions`.
+ eBreakpointModeApplicabilityException,
+ /// In data breakpoints requested in the `DataBreakpointInfo` request.
+ eBreakpointModeApplicabilityData,
+ /// In `InstructionBreakpoint`'s.
+ eBreakpointModeApplicabilityInstruction
+};
+llvm::json::Value toJSON(const BreakpointModeApplicability &);
+
+/// A `BreakpointMode` is provided as a option when setting breakpoints on
+/// sources or instructions.
+struct BreakpointMode {
+ /// The internal ID of the mode. This value is passed to the `setBreakpoints`
+ /// request.
+ std::string mode;
+
+ /// The name of the breakpoint mode. This is shown in the UI.
+ std::string label;
+
+ /// A help text providing additional information about the breakpoint mode.
+ /// This string is typically shown as a hover and can be translated.
+ std::optional<std::string> description;
+
+ /// Describes one or more type of breakpoint this mode applies to.
+ std::vector<BreakpointModeApplicability> appliesTo;
+};
+llvm::json::Value toJSON(const BreakpointMode &);
+
+/// Debug Adapter Features flags supported by lldb-dap.
+enum AdapterFeature {
+ /// The debug adapter supports ANSI escape sequences in styling of
+ /// `OutputEvent.output` and `Variable.value` fields.
+ eAdapterFeatureANSIStyling,
+ /// The debug adapter supports the `breakpointLocations` request.
+ eAdapterFeatureBreakpointLocationsRequest,
+ /// The debug adapter supports the `cancel` request.
+ eAdapterFeatureCancelRequest,
+ /// The debug adapter supports the `clipboard` context value in the
+ /// `evaluate` request.
+ eAdapterFeatureClipboardContext,
+ /// The debug adapter supports the `completions` request.
+ eAdapterFeatureCompletionsRequest,
+ /// The debug adapter supports conditional breakpoints.
+ eAdapterFeatureConditionalBreakpoints,
+ /// The debug adapter supports the `configurationDone` request.
+ eAdapterFeatureConfigurationDoneRequest,
+ /// The debug adapter supports the `asAddress` and `bytes` fields in the
+ /// `dataBreakpointInfo` request.
+ eAdapterFeatureDataBreakpointBytes,
+ /// The debug adapter supports data breakpoints.
+ eAdapterFeatureDataBreakpoints,
+ /// The debug adapter supports the delayed loading of parts of the stack,
+ /// which requires that both the `startFrame` and `levels` arguments and the
+ /// `totalFrames` result of the `stackTrace` request are supported.
+ eAdapterFeatureDelayedStackTraceLoading,
+ /// The debug adapter supports the `disassemble` request.
+ eAdapterFeatureDisassembleRequest,
+ /// The debug adapter supports a (side effect free) `evaluate` request for
+ /// data hovers.
+ eAdapterFeatureEvaluateForHovers,
+ /// The debug adapter supports `filterOptions` as an argument on the
+ /// `setExceptionBreakpoints` request.
+ eAdapterFeatureExceptionFilterOptions,
+ /// The debug adapter supports the `exceptionInfo` request.
+ eAdapterFeatureExceptionInfoRequest,
+ /// The debug adapter supports `exceptionOptions` on the
+ /// `setExceptionBreakpoints` request.
+ eAdapterFeatureExceptionOptions,
+ /// The debug adapter supports function breakpoints.
+ eAdapterFeatureFunctionBreakpoints,
+ /// The debug adapter supports the `gotoTargets` request.
+ eAdapterFeatureGotoTargetsRequest,
+ /// The debug adapter supports breakpoints that break execution after a
+ /// specified number of hits.
+ eAdapterFeatureHitConditionalBreakpoints,
+ /// The debug adapter supports adding breakpoints based on instruction
+ /// references.
+ eAdapterFeatureInstructionBreakpoints,
+ /// The debug adapter supports the `loadedSources` request.
+ eAdapterFeatureLoadedSourcesRequest,
+ /// The debug adapter supports log points by interpreting the `logMessage`
+ /// attribute of the `SourceBreakpoint`.
+ eAdapterFeatureLogPoints,
+ /// The debug adapter supports the `modules` request.
+ eAdapterFeatureModulesRequest,
+ /// The debug adapter supports the `readMemory` request.
+ eAdapterFeatureReadMemoryRequest,
+ /// The debug adapter supports restarting a frame.
+ eAdapterFeatureRestartFrame,
+ /// The debug adapter supports the `restart` request. In this case a client
+ /// should not implement `restart` by terminating and relaunching the
+ /// adapter but by calling the `restart` request.
+ eAdapterFeatureRestartRequest,
+ /// The debug adapter supports the `setExpression` request.
+ eAdapterFeatureSetExpression,
+ /// The debug adapter supports setting a variable to a value.
+ eAdapterFeatureSetVariable,
+ /// The debug adapter supports the `singleThread` property on the execution
+ /// requests (`continue`, `next`, `stepIn`, `stepOut`, `reverseContinue`,
+ /// `stepBack`).
+ eAdapterFeatureSingleThreadExecutionRequests,
+ /// The debug adapter supports stepping back via the `stepBack` and
+ /// `reverseContinue` requests.
+ eAdapterFeatureStepBack,
+ /// The debug adapter supports the `stepInTargets` request.
+ eAdapterFeatureStepInTargetsRequest,
+ /// The debug adapter supports stepping granularities (argument
+ /// `granularity`) for the stepping requests.
+ eAdapterFeatureSteppingGranularity,
+ /// The debug adapter supports the `terminate` request.
+ eAdapterFeatureTerminateRequest,
+ /// The debug adapter supports the `terminateThreads` request.
+ eAdapterFeatureTerminateThreadsRequest,
+ /// The debug adapter supports the `suspendDebuggee` attribute on the
+ /// `disconnect` request.
+ eAdapterFeatureSuspendDebuggee,
+ /// The debug adapter supports a `format` attribute on the `stackTrace`,
+ /// `variables`, and `evaluate` requests.
+ eAdapterFeatureValueFormattingOptions,
+ /// The debug adapter supports the `writeMemory` request.
+ eAdapterFeatureWriteMemoryRequest,
+ /// The debug adapter supports the `terminateDebuggee` attribute on the
+ /// `disconnect` request.
+ eAdapterFeatureTerminateDebuggee,
+};
+
+/// Information about the capabilities of a debug adapter.
+struct Capabilities {
+ /// The supported features for this adapter.
+ llvm::DenseSet<AdapterFeature> supportedFeatures;
+
+ /// Available exception filter options for the `setExceptionBreakpoints`
+ /// request.
+ std::optional<std::vector<ExceptionBreakpointsFilter>>
+ exceptionBreakpointFilters;
+
+ /// The set of characters that should trigger completion in a REPL. If not
+ /// specified, the UI should assume the `.` character.
+ std::optional<std::vector<std::string>> completionTriggerCharacters;
+
+ /// The set of additional module information exposed by the debug adapter.
+ std::optional<std::vector<ColumnDescriptor>> additionalModuleColumns;
+
+ /// Checksum algorithms supported by the debug adapter.
+ std::optional<std::vector<ChecksumAlgorithm>> supportedChecksumAlgorithms;
+
+ /// Modes of breakpoints supported by the debug adapter, such as 'hardware' or
+ /// 'software'. If present, the client may allow the user to select a mode and
+ /// include it in its `setBreakpoints` request.
+ ///
+ /// Clients may present the first applicable mode in this array as the
+ /// 'default' mode in gestures that set breakpoints.
+ std::optional<std::vector<BreakpointMode>> breakpointModes;
+
+ /// lldb-dap Extensions
+ /// @{
+
+ /// The version of the adapter.
+ std::optional<std::string> lldbExtVersion;
+
+ /// @}
+};
+llvm::json::Value toJSON(const Capabilities &);
+
+enum PresentationHint {
+ ePresentationHintNormal,
+ ePresentationHintEmphasize,
+ ePresentationHintDeemphasize,
+};
+
/// A `Source` is a descriptor for source code. It is returned from the debug
/// adapter as part of a `StackFrame` and it is used by clients when specifying
/// breakpoints.
struct Source {
- enum class PresentationHint { normal, emphasize, deemphasize };
-
/// The short name of the source. Every source returned from the debug adapter
/// has a name. When sending a source to the debug adapter this name is
/// optional.
More information about the lldb-commits
mailing list