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 <jo...@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. _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits