https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/133007
>From b6850bfee90229c8da5ffa5359023c682b574954 Mon Sep 17 00:00:00 2001 From: John Harrison <harj...@google.com> Date: Tue, 25 Mar 2025 14:58:03 -0700 Subject: [PATCH] [lldb-dap] Migrating DAP 'initialize' to new typed RequestHandler. 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. --- .../test/tools/lldb-dap/dap_server.py | 5 +- .../tools/lldb-dap/launch/TestDAP_launch.py | 3 +- lldb/tools/lldb-dap/DAP.cpp | 145 ++++++++-- lldb/tools/lldb-dap/DAP.h | 6 +- .../Handler/InitializeRequestHandler.cpp | 162 ++--------- .../tools/lldb-dap/Handler/RequestHandler.cpp | 1 + lldb/tools/lldb-dap/Handler/RequestHandler.h | 89 +++--- lldb/tools/lldb-dap/JSONUtils.cpp | 34 +-- lldb/tools/lldb-dap/JSONUtils.h | 3 +- .../lldb-dap/Protocol/ProtocolRequests.cpp | 52 ++++ .../lldb-dap/Protocol/ProtocolRequests.h | 69 +++++ .../tools/lldb-dap/Protocol/ProtocolTypes.cpp | 199 ++++++++++++++ lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 255 ++++++++++++++++++ 13 files changed, 800 insertions(+), 223 deletions(-) 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..0da8ce43f73c4 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,31 +1145,137 @@ lldb::SBValue Variables::FindVariable(uint64_t variablesReference, return variable; } -llvm::StringMap<bool> DAP::GetCapabilities() { - llvm::StringMap<bool> capabilities; +static void mergeCapabilities(protocol::Capabilities &into, + const protocol::Capabilities &from) { + if (from.supportsConfigurationDoneRequest) + into.supportsConfigurationDoneRequest = + *from.supportsConfigurationDoneRequest; + if (from.supportsFunctionBreakpoints) + into.supportsFunctionBreakpoints = *from.supportsFunctionBreakpoints; + if (from.supportsConditionalBreakpoints) + into.supportsConditionalBreakpoints = *from.supportsConditionalBreakpoints; + if (from.supportsHitConditionalBreakpoints) + into.supportsHitConditionalBreakpoints = + *from.supportsHitConditionalBreakpoints; + if (from.supportsEvaluateForHovers) + into.supportsEvaluateForHovers = *from.supportsEvaluateForHovers; + if (from.exceptionBreakpointFilters) + into.exceptionBreakpointFilters = *from.exceptionBreakpointFilters; + if (from.supportsStepBack) + into.supportsStepBack = *from.supportsStepBack; + if (from.supportsSetVariable) + into.supportsSetVariable = *from.supportsSetVariable; + if (from.supportsRestartFrame) + into.supportsRestartFrame = *from.supportsRestartFrame; + if (from.supportsGotoTargetsRequest) + into.supportsGotoTargetsRequest = *from.supportsGotoTargetsRequest; + if (from.supportsStepInTargetsRequest) + into.supportsStepInTargetsRequest = *from.supportsStepInTargetsRequest; + if (from.supportsCompletionsRequest) + into.supportsCompletionsRequest = *from.supportsCompletionsRequest; + if (from.completionTriggerCharacters) + into.completionTriggerCharacters = *from.completionTriggerCharacters; + if (from.supportsModulesRequest) + into.supportsModulesRequest = *from.supportsModulesRequest; + if (from.additionalModuleColumns) + into.additionalModuleColumns = *from.additionalModuleColumns; + if (from.supportedChecksumAlgorithms) + into.supportedChecksumAlgorithms = *from.supportedChecksumAlgorithms; + if (from.supportsRestartRequest) + into.supportsRestartRequest = *from.supportsRestartRequest; + if (from.supportsExceptionOptions) + into.supportsExceptionOptions = *from.supportsExceptionOptions; + if (from.supportsValueFormattingOptions) + into.supportsValueFormattingOptions = *from.supportsValueFormattingOptions; + if (from.supportsExceptionInfoRequest) + into.supportsExceptionInfoRequest = *from.supportsExceptionInfoRequest; + if (from.supportTerminateDebuggee) + into.supportTerminateDebuggee = *from.supportTerminateDebuggee; + if (from.supportSuspendDebuggee) + into.supportSuspendDebuggee = *from.supportSuspendDebuggee; + if (from.supportsDelayedStackTraceLoading) + into.supportsDelayedStackTraceLoading = + *from.supportsDelayedStackTraceLoading; + if (from.supportsLoadedSourcesRequest) + into.supportsLoadedSourcesRequest = *from.supportsLoadedSourcesRequest; + if (from.supportsLogPoints) + into.supportsLogPoints = *from.supportsLogPoints; + if (from.supportsTerminateThreadsRequest) + into.supportsTerminateThreadsRequest = + *from.supportsTerminateThreadsRequest; + if (from.supportsSetExpression) + into.supportsSetExpression = *from.supportsSetExpression; + if (from.supportsTerminateRequest) + into.supportsTerminateRequest = *from.supportsTerminateRequest; + if (from.supportsDataBreakpoints) + into.supportsDataBreakpoints = *from.supportsDataBreakpoints; + if (from.supportsReadMemoryRequest) + into.supportsReadMemoryRequest = *from.supportsReadMemoryRequest; + if (from.supportsWriteMemoryRequest) + into.supportsWriteMemoryRequest = *from.supportsWriteMemoryRequest; + if (from.supportsDisassembleRequest) + into.supportsDisassembleRequest = *from.supportsDisassembleRequest; + if (from.supportsCancelRequest) + into.supportsCancelRequest = *from.supportsCancelRequest; + if (from.supportsBreakpointLocationsRequest) + into.supportsBreakpointLocationsRequest = + *from.supportsBreakpointLocationsRequest; + if (from.supportsClipboardContext) + into.supportsClipboardContext = *from.supportsClipboardContext; + if (from.supportsSteppingGranularity) + into.supportsSteppingGranularity = *from.supportsSteppingGranularity; + if (from.supportsInstructionBreakpoints) + into.supportsInstructionBreakpoints = *from.supportsInstructionBreakpoints; + if (from.supportsExceptionFilterOptions) + into.supportsExceptionFilterOptions = *from.supportsExceptionFilterOptions; + if (from.supportsSingleThreadExecutionRequests) + into.supportsSingleThreadExecutionRequests = + *from.supportsSingleThreadExecutionRequests; + if (from.supportsDataBreakpointBytes) + into.supportsDataBreakpointBytes = *from.supportsDataBreakpointBytes; + if (from.breakpointModes) + into.breakpointModes = *from.breakpointModes; + if (from.supportsANSIStyling) + into.supportsANSIStyling = *from.supportsANSIStyling; +} + +protocol::Capabilities DAP::GetCapabilities() { + protocol::Capabilities 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; + capabilities.supportTerminateDebuggee = true; + capabilities.supportsDataBreakpoints = true; + capabilities.supportsDelayedStackTraceLoading = true; + capabilities.supportsEvaluateForHovers = true; + capabilities.supportsExceptionOptions = true; + capabilities.supportsLogPoints = true; + capabilities.supportsSteppingGranularity = true; + capabilities.supportsValueFormattingOptions = true; // Unsupported capabilities. - capabilities["supportsGotoTargetsRequest"] = false; - capabilities["supportsLoadedSourcesRequest"] = false; - capabilities["supportsRestartFrame"] = false; - capabilities["supportsStepBack"] = false; + capabilities.supportsGotoTargetsRequest = false; + capabilities.supportsLoadedSourcesRequest = false; + capabilities.supportsRestartFrame = false; + capabilities.supportsStepBack = false; // Capabilities associated with specific requests. - for (auto &kv : request_handlers) { - for (auto &request_kv : kv.second->GetCapabilities()) - capabilities[request_kv.getKey()] = request_kv.getValue(); - } + for (auto &kv : request_handlers) + mergeCapabilities(capabilities, kv.second->GetCapabilities()); + + // 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); + + std::vector<std::string> completion_characters; + completion_characters.emplace_back("."); + completion_characters.emplace_back(" "); + completion_characters.emplace_back("\t"); + capabilities.completionTriggerCharacters = std::move(completion_characters); + + // Put in non-DAP specification lldb specific information. + capabilities.lldbVersion = debugger.GetVersionString(); return capabilities; } diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 4b4d471161137..2f23e5a116bc3 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -16,6 +16,7 @@ #include "OutputRedirector.h" #include "ProgressEvent.h" #include "Protocol/ProtocolBase.h" +#include "Protocol/ProtocolTypes.h" #include "SourceBreakpoint.h" #include "Transport.h" #include "lldb/API/SBBroadcaster.h" @@ -180,6 +181,7 @@ struct DAP { bool enable_auto_variable_summaries; bool enable_synthetic_child_debugging; bool display_extended_backtrace; + bool supports_run_in_terminal_request; // The process event thread normally responds to process exited events by // shutting down the entire adapter. When we're restarting, we keep the id of // the old process here so we can detect this case and keep running. @@ -363,8 +365,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..b85424975a0fc 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -9,6 +9,7 @@ #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" @@ -229,118 +230,29 @@ 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<protocol::InitializeResponseBody> InitializeRequestHandler::Run( + const protocol::InitializeRequestArguments &arguments) const { // 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) { + // 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. + if (arguments.sourceInitFile.value_or(true)) { dap.debugger.SkipLLDBInitFiles(false); dap.debugger.SkipAppInitFiles(false); lldb::SBCommandReturnObject init; @@ -348,59 +260,35 @@ 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.supportsStartDebuggingRequest.value_or(false)) { cmd.AddCommand( "start-debugging", new StartDebuggingRequestHandler(dap), "Sends a startDebugging request from the debug adapter to the client " "to start a child debug session of the same type as the caller."); } + dap.supports_run_in_terminal_request = + arguments.supportsRunInTerminalRequest.value_or(false); cmd.AddCommand( "repl-mode", new ReplModeRequestHandler(dap), "Get or set the repl behavior of lldb-dap evaluation requests."); 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.supportsProgressReporting) + 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..d2a0da420739e 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -201,6 +201,7 @@ BaseRequestHandler::LaunchProcess(const llvm::json::Object &request) const { const auto timeout_seconds = GetInteger<uint64_t>(arguments, "timeout").value_or(30); + // FIXME: Check dap.supports_run_in_terminal_request. if (GetBoolean(arguments, "runInTerminal").value_or(false)) { if (llvm::Error err = RunInTerminal(dap, request, timeout_seconds)) error.SetErrorString(llvm::toString(std::move(err)).c_str()); diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 8971b02fcb92e..ba854c9884cde 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -14,6 +14,7 @@ #include "DAPLog.h" #include "Protocol/ProtocolBase.h" #include "Protocol/ProtocolRequests.h" +#include "Protocol/ProtocolTypes.h" #include "lldb/API/SBError.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" @@ -47,7 +48,7 @@ class BaseRequestHandler { virtual void operator()(const protocol::Request &request) const = 0; - virtual llvm::StringMap<bool> GetCapabilities() const { return {}; } + virtual protocol::Capabilities GetCapabilities() const { return {}; } protected: /// Helpers used by multiple request handlers. @@ -175,8 +176,10 @@ class BreakpointLocationsRequestHandler : public LegacyRequestHandler { public: using LegacyRequestHandler::LegacyRequestHandler; static llvm::StringLiteral GetCommand() { return "breakpointLocations"; } - llvm::StringMap<bool> GetCapabilities() const override { - return {{"supportsBreakpointLocationsRequest", true}}; + protocol::Capabilities GetCapabilities() const override { + protocol::Capabilities capabilities; + capabilities.supportsBreakpointLocationsRequest = true; + return capabilities; } void operator()(const llvm::json::Object &request) const override; }; @@ -185,8 +188,10 @@ class CompletionsRequestHandler : public LegacyRequestHandler { public: using LegacyRequestHandler::LegacyRequestHandler; static llvm::StringLiteral GetCommand() { return "completions"; } - llvm::StringMap<bool> GetCapabilities() const override { - return {{"supportsCompletionsRequest", true}}; + protocol::Capabilities GetCapabilities() const override { + protocol::Capabilities capabilities; + capabilities.supportsCompletionsRequest = true; + return capabilities; } void operator()(const llvm::json::Object &request) const override; }; @@ -202,8 +207,10 @@ class ConfigurationDoneRequestHandler : public LegacyRequestHandler { public: using LegacyRequestHandler::LegacyRequestHandler; static llvm::StringLiteral GetCommand() { return "configurationDone"; } - llvm::StringMap<bool> GetCapabilities() const override { - return {{"supportsConfigurationDoneRequest", true}}; + protocol::Capabilities GetCapabilities() const override { + protocol::Capabilities capabilities; + capabilities.supportsConfigurationDoneRequest = true; + return capabilities; } void operator()(const llvm::json::Object &request) const override; }; @@ -229,20 +236,22 @@ class ExceptionInfoRequestHandler : public LegacyRequestHandler { public: using LegacyRequestHandler::LegacyRequestHandler; static llvm::StringLiteral GetCommand() { return "exceptionInfo"; } - llvm::StringMap<bool> GetCapabilities() const override { - return {{"supportsExceptionInfoRequest", true}}; + protocol::Capabilities GetCapabilities() const override { + protocol::Capabilities capabilities; + capabilities.supportsExceptionInfoRequest = true; + return capabilities; } 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,10 @@ class RestartRequestHandler : public LegacyRequestHandler { public: using LegacyRequestHandler::LegacyRequestHandler; static llvm::StringLiteral GetCommand() { return "restart"; } - llvm::StringMap<bool> GetCapabilities() const override { - return {{"supportsRestartRequest", true}}; + protocol::Capabilities GetCapabilities() const override { + protocol::Capabilities capabilities; + capabilities.supportsRestartRequest = true; + return capabilities; } void operator()(const llvm::json::Object &request) const override; }; @@ -280,8 +291,10 @@ class StepInTargetsRequestHandler : public LegacyRequestHandler { public: using LegacyRequestHandler::LegacyRequestHandler; static llvm::StringLiteral GetCommand() { return "stepInTargets"; } - llvm::StringMap<bool> GetCapabilities() const override { - return {{"supportsStepInTargetsRequest", true}}; + protocol::Capabilities GetCapabilities() const override { + protocol::Capabilities capabilities; + capabilities.supportsStepInTargetsRequest = true; + return capabilities; } void operator()(const llvm::json::Object &request) const override; }; @@ -297,9 +310,11 @@ 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}}; + protocol::Capabilities GetCapabilities() const override { + protocol::Capabilities capabilities; + capabilities.supportsConditionalBreakpoints = true; + capabilities.supportsHitConditionalBreakpoints = true; + return capabilities; } void operator()(const llvm::json::Object &request) const override; }; @@ -315,8 +330,10 @@ class SetFunctionBreakpointsRequestHandler : public LegacyRequestHandler { public: using LegacyRequestHandler::LegacyRequestHandler; static llvm::StringLiteral GetCommand() { return "setFunctionBreakpoints"; } - llvm::StringMap<bool> GetCapabilities() const override { - return {{"supportsFunctionBreakpoints", true}}; + protocol::Capabilities GetCapabilities() const override { + protocol::Capabilities capabilities; + capabilities.supportsFunctionBreakpoints = true; + return capabilities; } void operator()(const llvm::json::Object &request) const override; }; @@ -355,8 +372,10 @@ class ModulesRequestHandler : public LegacyRequestHandler { public: using LegacyRequestHandler::LegacyRequestHandler; static llvm::StringLiteral GetCommand() { return "modules"; } - llvm::StringMap<bool> GetCapabilities() const override { - return {{"supportsModulesRequest", true}}; + protocol::Capabilities GetCapabilities() const override { + protocol::Capabilities capabilities; + capabilities.supportsModulesRequest = true; + return capabilities; } void operator()(const llvm::json::Object &request) const override; }; @@ -379,8 +398,10 @@ class SetVariableRequestHandler : public LegacyRequestHandler { public: using LegacyRequestHandler::LegacyRequestHandler; static llvm::StringLiteral GetCommand() { return "setVariable"; } - llvm::StringMap<bool> GetCapabilities() const override { - return {{"supportsSetVariable", true}}; + protocol::Capabilities GetCapabilities() const override { + protocol::Capabilities capabilities; + capabilities.supportsSetVariable = true; + return capabilities; } void operator()(const llvm::json::Object &request) const override; }; @@ -427,8 +448,10 @@ class DisassembleRequestHandler : public LegacyRequestHandler { public: using LegacyRequestHandler::LegacyRequestHandler; static llvm::StringLiteral GetCommand() { return "disassemble"; } - llvm::StringMap<bool> GetCapabilities() const override { - return {{"supportsDisassembleRequest", true}}; + protocol::Capabilities GetCapabilities() const override { + protocol::Capabilities capabilities; + capabilities.supportsDisassembleRequest = true; + return capabilities; } void operator()(const llvm::json::Object &request) const override; }; @@ -437,8 +460,10 @@ class ReadMemoryRequestHandler : public LegacyRequestHandler { public: using LegacyRequestHandler::LegacyRequestHandler; static llvm::StringLiteral GetCommand() { return "readMemory"; } - llvm::StringMap<bool> GetCapabilities() const override { - return {{"supportsReadMemoryRequest", true}}; + protocol::Capabilities GetCapabilities() const override { + protocol::Capabilities capabilities; + capabilities.supportsReadMemoryRequest = true; + return capabilities; } 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/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index 5cc5429227439..16d54b1184f1d 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -23,6 +23,58 @@ bool fromJSON(const json::Value &Params, DisconnectArguments &DA, O.mapOptional("suspendDebuggee", DA.suspendDebuggee); } +bool fromJSON(const llvm::json::Value &Params, + InitializeRequestArguments::PathFormat &PF, llvm::json::Path P) { + auto rawPathFormat = Params.getAsString(); + if (!rawPathFormat) { + P.report("expected a string"); + return false; + } + + std::optional<InitializeRequestArguments::PathFormat> pathFormat = + StringSwitch<std::optional<InitializeRequestArguments::PathFormat>>( + *rawPathFormat) + .Case("path", InitializeRequestArguments::PathFormat::path) + .Case("uri", InitializeRequestArguments::PathFormat::uri) + .Default(std::nullopt); + if (!pathFormat) { + P.report("unexpected value, expected 'path' or 'uri'"); + return false; + } + + PF = *pathFormat; + return true; +} + +bool fromJSON(const llvm::json::Value &Params, InitializeRequestArguments &IRA, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.mapOptional("adatperID", IRA.adatperID) && + O.mapOptional("clientID", IRA.clientID) && + O.mapOptional("clientName", IRA.clientName) && + O.mapOptional("locale", IRA.locale) && + O.mapOptional("linesStartAt1", IRA.linesStartAt1) && + O.mapOptional("columnsStartAt1", IRA.columnsStartAt1) && + O.mapOptional("pathFormat", IRA.pathFormat) && + O.mapOptional("supportsVariableType", IRA.supportsVariableType) && + O.mapOptional("supportsVariablePaging", IRA.supportsVariablePaging) && + O.mapOptional("supportsRunInTerminalRequest", + IRA.supportsRunInTerminalRequest) && + O.mapOptional("supportsMemoryReferences", + IRA.supportsMemoryReferences) && + O.mapOptional("supportsProgressReporting", + IRA.supportsProgressReporting) && + O.mapOptional("supportsInvalidatedEvent", + IRA.supportsInvalidatedEvent) && + O.mapOptional("supportsMemoryEvent", IRA.supportsMemoryEvent) && + O.mapOptional("supportsArgsCanBeInterpretedByShell", + IRA.supportsArgsCanBeInterpretedByShell) && + O.mapOptional("supportsStartDebuggingRequest", + IRA.supportsStartDebuggingRequest) && + O.mapOptional("supportsANSIStyling", IRA.supportsANSIStyling) && + O.mapOptional("$__lldb_sourceInitFile", IRA.sourceInitFile); +} + 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..beb573515df62 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -54,6 +54,75 @@ bool fromJSON(const llvm::json::Value &, DisconnectArguments &, /// body field is required. using DisconnectResponse = VoidResponse; +/// 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; + + /// 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; + + enum class PathFormat { path, uri }; + + /// Determines in what format paths are specified. The default is `path`, + /// which is the native format. + std::optional<PathFormat> pathFormat = PathFormat::path; + + /// Client supports the `type` attribute for variables. + std::optional<bool> supportsVariableType; + + /// Client supports the paging of variables. + std::optional<bool> supportsVariablePaging; + + /// Client supports the `runInTerminal` request. + std::optional<bool> supportsRunInTerminalRequest; + + /// Client supports memory references. + std::optional<bool> supportsMemoryReferences; + + /// Client supports progress reporting. + std::optional<bool> supportsProgressReporting; + + /// Client supports the `invalidated` event. + std::optional<bool> supportsInvalidatedEvent; + + /// Client supports the `memory` event. + std::optional<bool> supportsMemoryEvent; + + /// Client supports the `argsCanBeInterpretedByShell` attribute on the + /// `runInTerminal` request. + std::optional<bool> supportsArgsCanBeInterpretedByShell; + + /// Client supports the `startDebugging` request. + std::optional<bool> supportsStartDebuggingRequest; + + /// The client will interpret ANSI escape sequences in the display of + /// `OutputEvent.output` and `Variable.value` fields when + /// `Capabilities.supportsANSIStyling` is also enabled. + std::optional<bool> supportsANSIStyling; + + /// lldb-dap Extensions + /// Source init files when initializing lldb::SBDebugger. + std::optional<bool> sourceInitFile; +}; +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..d81d4bc801538 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp @@ -44,4 +44,203 @@ 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({"defaultState", *EBF.defaultState}); + if (EBF.supportsCondition) + result.insert({"supportsCondition", *EBF.supportsCondition}); + if (EBF.conditionDescription) + result.insert({"conditionDescription", *EBF.conditionDescription}); + + return result; +} + +json::Value toJSON(const ColumnDescriptor::Type &T) { + switch (T) { + case ColumnDescriptor::Type::String: + return "string"; + case ColumnDescriptor::Type::Number: + return "number"; + case ColumnDescriptor::Type::Boolean: + return "boolean"; + case ColumnDescriptor::Type::Timestamp: + 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 ChecksumAlgorithm::md5: + return "MD5"; + case ChecksumAlgorithm::sha1: + return "SHA1"; + case ChecksumAlgorithm::sha256: + return "SHA256"; + case ChecksumAlgorithm::timestamp: + return "timestamp"; + } +} + +json::Value toJSON(const BreakpointModeApplicability &BMA) { + switch (BMA) { + case BreakpointModeApplicability::source: + return "source"; + case BreakpointModeApplicability::exception: + return "exception"; + case BreakpointModeApplicability::data: + return "data"; + case BreakpointModeApplicability::instruction: + 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; +} + +json::Value toJSON(const Capabilities &C) { + json::Object result; + + if (C.supportsConfigurationDoneRequest && *C.supportsConfigurationDoneRequest) + result.insert({"supportsConfigurationDoneRequest", + *C.supportsConfigurationDoneRequest}); + if (C.supportsFunctionBreakpoints && *C.supportsFunctionBreakpoints) + result.insert( + {"supportsFunctionBreakpoints", *C.supportsFunctionBreakpoints}); + if (C.supportsConditionalBreakpoints && *C.supportsConditionalBreakpoints) + result.insert( + {"supportsConditionalBreakpoints", *C.supportsConditionalBreakpoints}); + if (C.supportsHitConditionalBreakpoints && + *C.supportsHitConditionalBreakpoints) + result.insert({"supportsHitConditionalBreakpoints", + *C.supportsHitConditionalBreakpoints}); + if (C.supportsEvaluateForHovers && *C.supportsEvaluateForHovers) + result.insert({"supportsEvaluateForHovers", *C.supportsEvaluateForHovers}); + if (C.exceptionBreakpointFilters && !C.exceptionBreakpointFilters->empty()) + result.insert( + {"exceptionBreakpointFilters", *C.exceptionBreakpointFilters}); + if (C.supportsStepBack && *C.supportsStepBack) + result.insert({"supportsStepBack", *C.supportsStepBack}); + if (C.supportsSetVariable && *C.supportsSetVariable) + result.insert({"supportsSetVariable", *C.supportsSetVariable}); + if (C.supportsRestartFrame && *C.supportsRestartFrame) + result.insert({"supportsRestartFrame", *C.supportsRestartFrame}); + if (C.supportsGotoTargetsRequest && *C.supportsGotoTargetsRequest) + result.insert( + {"supportsGotoTargetsRequest", *C.supportsGotoTargetsRequest}); + if (C.supportsStepInTargetsRequest && *C.supportsStepInTargetsRequest) + result.insert( + {"supportsStepInTargetsRequest", *C.supportsStepInTargetsRequest}); + if (C.supportsCompletionsRequest && *C.supportsCompletionsRequest) + result.insert( + {"supportsCompletionsRequest", *C.supportsCompletionsRequest}); + if (C.completionTriggerCharacters && !C.completionTriggerCharacters->empty()) + result.insert( + {"completionTriggerCharacters", *C.completionTriggerCharacters}); + if (C.supportsModulesRequest && *C.supportsModulesRequest) + result.insert({"supportsModulesRequest", *C.supportsModulesRequest}); + if (C.additionalModuleColumns && !C.additionalModuleColumns->empty()) + result.insert({"additionalModuleColumns", *C.additionalModuleColumns}); + if (C.supportedChecksumAlgorithms && !C.supportedChecksumAlgorithms->empty()) + result.insert( + {"supportedChecksumAlgorithms", *C.supportedChecksumAlgorithms}); + if (C.supportsRestartRequest && *C.supportsRestartRequest) + result.insert({"supportsRestartRequest", *C.supportsRestartRequest}); + if (C.supportsExceptionOptions && *C.supportsExceptionOptions) + result.insert({"supportsExceptionOptions", *C.supportsExceptionOptions}); + if (C.supportsValueFormattingOptions && *C.supportsValueFormattingOptions) + result.insert( + {"supportsValueFormattingOptions", *C.supportsValueFormattingOptions}); + if (C.supportsExceptionInfoRequest && *C.supportsExceptionInfoRequest) + result.insert( + {"supportsExceptionInfoRequest", *C.supportsExceptionInfoRequest}); + if (C.supportTerminateDebuggee && *C.supportTerminateDebuggee) + result.insert({"supportTerminateDebuggee", *C.supportTerminateDebuggee}); + if (C.supportSuspendDebuggee && *C.supportSuspendDebuggee) + result.insert({"supportSuspendDebuggee", *C.supportSuspendDebuggee}); + if (C.supportsDelayedStackTraceLoading && *C.supportsDelayedStackTraceLoading) + result.insert({"supportsDelayedStackTraceLoading", + *C.supportsDelayedStackTraceLoading}); + if (C.supportsLoadedSourcesRequest && *C.supportsLoadedSourcesRequest) + result.insert( + {"supportsLoadedSourcesRequest", *C.supportsLoadedSourcesRequest}); + if (C.supportsLogPoints && *C.supportsLogPoints) + result.insert({"supportsLogPoints", *C.supportsLogPoints}); + if (C.supportsTerminateThreadsRequest && *C.supportsTerminateThreadsRequest) + result.insert({"supportsTerminateThreadsRequest", + *C.supportsTerminateThreadsRequest}); + if (C.supportsSetExpression && *C.supportsSetExpression) + result.insert({"supportsSetExpression", *C.supportsSetExpression}); + if (C.supportsTerminateRequest && *C.supportsTerminateRequest) + result.insert({"supportsTerminateRequest", *C.supportsTerminateRequest}); + if (C.supportsDataBreakpoints && *C.supportsDataBreakpoints) + result.insert({"supportsDataBreakpoints", *C.supportsDataBreakpoints}); + if (C.supportsReadMemoryRequest && *C.supportsReadMemoryRequest) + result.insert({"supportsReadMemoryRequest", *C.supportsReadMemoryRequest}); + if (C.supportsWriteMemoryRequest && *C.supportsWriteMemoryRequest) + result.insert( + {"supportsWriteMemoryRequest", *C.supportsWriteMemoryRequest}); + if (C.supportsDisassembleRequest && *C.supportsDisassembleRequest) + result.insert( + {"supportsDisassembleRequest", *C.supportsDisassembleRequest}); + if (C.supportsCancelRequest && *C.supportsCancelRequest) + result.insert({"supportsCancelRequest", *C.supportsCancelRequest}); + if (C.supportsBreakpointLocationsRequest && + *C.supportsBreakpointLocationsRequest) + result.insert({"supportsBreakpointLocationsRequest", + *C.supportsBreakpointLocationsRequest}); + if (C.supportsClipboardContext && *C.supportsClipboardContext) + result.insert({"supportsClipboardContext", *C.supportsClipboardContext}); + if (C.supportsSteppingGranularity && *C.supportsSteppingGranularity) + result.insert( + {"supportsSteppingGranularity", *C.supportsSteppingGranularity}); + if (C.supportsInstructionBreakpoints && *C.supportsInstructionBreakpoints) + result.insert( + {"supportsInstructionBreakpoints", *C.supportsInstructionBreakpoints}); + if (C.supportsExceptionFilterOptions && *C.supportsExceptionFilterOptions) + result.insert( + {"supportsExceptionFilterOptions", *C.supportsExceptionFilterOptions}); + if (C.supportsSingleThreadExecutionRequests && + *C.supportsSingleThreadExecutionRequests) + result.insert({"supportsSingleThreadExecutionRequests", + *C.supportsSingleThreadExecutionRequests}); + if (C.supportsDataBreakpointBytes && *C.supportsDataBreakpointBytes) + result.insert( + {"supportsDataBreakpointBytes", *C.supportsDataBreakpointBytes}); + if (C.breakpointModes && !C.breakpointModes->empty()) + result.insert({"breakpointModes", *C.breakpointModes}); + if (C.supportsANSIStyling && *C.supportsANSIStyling) + result.insert({"supportsANSIStyling", *C.supportsANSIStyling}); + if (C.lldbVersion && !C.lldbVersion->empty()) + result.insert({"$__lldb_version", *C.lldbVersion}); + + 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..9a58e83b39ad1 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -27,6 +27,261 @@ 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 &); + +/// 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; + + enum class Type { String, Number, Boolean, Timestamp }; + + /// Datatype of values in this column. Defaults to `string` if not specified. + /// Values: 'string', 'number', 'boolean', 'unixTimestampUTC'. + std::optional<Type> 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 class ChecksumAlgorithm { md5, sha1, sha256, timestamp }; +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 class BreakpointModeApplicability { + /// In `SourceBreakpoint`'s. + source, + /// In exception breakpoints applied in the `ExceptionFilterOptions`. + exception, + /// In data breakpoints requested in the `DataBreakpointInfo` request. + data, + /// In `InstructionBreakpoint`'s. + instruction +}; +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 &); + +/// Information about the capabilities of a debug adapter. +struct Capabilities { + /// The debug adapter supports the `configurationDone` request. + std::optional<bool> supportsConfigurationDoneRequest; + + /// The debug adapter supports function breakpoints. + std::optional<bool> supportsFunctionBreakpoints; + + /// The debug adapter supports conditional breakpoints. + std::optional<bool> supportsConditionalBreakpoints; + + /// The debug adapter supports breakpoints that break execution after a + /// specified number of hits. + std::optional<bool> supportsHitConditionalBreakpoints; + + /// The debug adapter supports a (side effect free) `evaluate` request for + /// data hovers. + std::optional<bool> supportsEvaluateForHovers; + + /// Available exception filter options for the `setExceptionBreakpoints` + /// request. + std::optional<std::vector<ExceptionBreakpointsFilter>> + exceptionBreakpointFilters; + + /// The debug adapter supports stepping back via the `stepBack` and + /// `reverseContinue` requests. + std::optional<bool> supportsStepBack; + + /// The debug adapter supports setting a variable to a value. + std::optional<bool> supportsSetVariable; + + /// The debug adapter supports restarting a frame. + std::optional<bool> supportsRestartFrame; + + /// The debug adapter supports the `gotoTargets` request. + std::optional<bool> supportsGotoTargetsRequest; + + /// The debug adapter supports the `stepInTargets` request. + std::optional<bool> supportsStepInTargetsRequest; + + /// The debug adapter supports the `completions` request. + std::optional<bool> supportsCompletionsRequest; + + /// 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 debug adapter supports the `modules` request. + std::optional<bool> supportsModulesRequest; + + /// 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; + + /// 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. + std::optional<bool> supportsRestartRequest; + + /// The debug adapter supports `exceptionOptions` on the + /// `setExceptionBreakpoints` request. + std::optional<bool> supportsExceptionOptions; + + /// The debug adapter supports a `format` attribute on the `stackTrace`, + /// `variables`, and `evaluate` requests. + std::optional<bool> supportsValueFormattingOptions; + + /// The debug adapter supports the `exceptionInfo` request. + std::optional<bool> supportsExceptionInfoRequest; + + /// The debug adapter supports the `terminateDebuggee` attribute on the + /// `disconnect` request. + std::optional<bool> supportTerminateDebuggee; + + /// The debug adapter supports the `suspendDebuggee` attribute on the + /// `disconnect` request. + std::optional<bool> supportSuspendDebuggee; + + /// 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. + std::optional<bool> supportsDelayedStackTraceLoading; + + /// The debug adapter supports the `loadedSources` request. + std::optional<bool> supportsLoadedSourcesRequest; + + /// The debug adapter supports log points by interpreting the `logMessage` + /// attribute of the `SourceBreakpoint`. + std::optional<bool> supportsLogPoints; + + /// The debug adapter supports the `terminateThreads` request. + std::optional<bool> supportsTerminateThreadsRequest; + + /// The debug adapter supports the `setExpression` request. + std::optional<bool> supportsSetExpression; + + /// The debug adapter supports the `terminate` request. + std::optional<bool> supportsTerminateRequest; + + /// The debug adapter supports data breakpoints. + std::optional<bool> supportsDataBreakpoints; + + /// The debug adapter supports the `readMemory` request. + std::optional<bool> supportsReadMemoryRequest; + + /// The debug adapter supports the `writeMemory` request. + std::optional<bool> supportsWriteMemoryRequest; + + /// The debug adapter supports the `disassemble` request. + std::optional<bool> supportsDisassembleRequest; + + /// The debug adapter supports the `cancel` request. + std::optional<bool> supportsCancelRequest; + + /// The debug adapter supports the `breakpointLocations` request. + std::optional<bool> supportsBreakpointLocationsRequest; + + /// The debug adapter supports the `clipboard` context value in the `evaluate` + /// request. + std::optional<bool> supportsClipboardContext; + + /// The debug adapter supports stepping granularities (argument `granularity`) + /// for the stepping requests. + std::optional<bool> supportsSteppingGranularity; + + /// The debug adapter supports adding breakpoints based on instruction + /// references. + std::optional<bool> supportsInstructionBreakpoints; + + /// The debug adapter supports `filterOptions` as an argument on the + /// `setExceptionBreakpoints` request. + std::optional<bool> supportsExceptionFilterOptions; + + /// The debug adapter supports the `singleThread` property on the execution + /// requests (`continue`, `next`, `stepIn`, `stepOut`, `reverseContinue`, + /// `stepBack`). + std::optional<bool> supportsSingleThreadExecutionRequests; + + /// The debug adapter supports the `asAddress` and `bytes` fields in the + /// `dataBreakpointInfo` request. + std::optional<bool> supportsDataBreakpointBytes; + + /// 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; + + /// The debug adapter supports ANSI escape sequences in styling of + /// `OutputEvent.output` and `Variable.value` fields. + std::optional<bool> supportsANSIStyling; + + /// lldb-dap Extensions + std::optional<std::string> lldbVersion; +}; +llvm::json::Value toJSON(const Capabilities &); + /// 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. _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits