diff options
Diffstat (limited to 'lldb/tools/lldb-dap/Handler')
6 files changed, 145 insertions, 150 deletions
diff --git a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp index c72fc56..de9a15d 100644 --- a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp @@ -8,156 +8,46 @@ #include "DAP.h" #include "JSONUtils.h" +#include "Protocol/ProtocolRequests.h" +#include "Protocol/ProtocolTypes.h" #include "RequestHandler.h" #include "lldb/API/SBStringList.h" -namespace lldb_dap { +using namespace llvm; +using namespace lldb_dap; +using namespace lldb_dap::protocol; -// "CompletionsRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Returns a list of possible completions for a given caret -// position and text.\nThe CompletionsRequest may only be called if the -// 'supportsCompletionsRequest' capability exists and is true.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "completions" ] -// }, -// "arguments": { -// "$ref": "#/definitions/CompletionsArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "CompletionsArguments": { -// "type": "object", -// "description": "Arguments for 'completions' request.", -// "properties": { -// "frameId": { -// "type": "integer", -// "description": "Returns completions in the scope of this stack frame. -// If not specified, the completions are returned for the global scope." -// }, -// "text": { -// "type": "string", -// "description": "One or more source lines. Typically this is the text a -// user has typed into the debug console before he asked for completion." -// }, -// "column": { -// "type": "integer", -// "description": "The character position for which to determine the -// completion proposals." -// }, -// "line": { -// "type": "integer", -// "description": "An optional line for which to determine the completion -// proposals. If missing the first line of the text is assumed." -// } -// }, -// "required": [ "text", "column" ] -// }, -// "CompletionsResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'completions' request.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "targets": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/CompletionItem" -// }, -// "description": "The possible completions for ." -// } -// }, -// "required": [ "targets" ] -// } -// }, -// "required": [ "body" ] -// }] -// }, -// "CompletionItem": { -// "type": "object", -// "description": "CompletionItems are the suggestions returned from the -// CompletionsRequest.", "properties": { -// "label": { -// "type": "string", -// "description": "The label of this completion item. By default this is -// also the text that is inserted when selecting this completion." -// }, -// "text": { -// "type": "string", -// "description": "If text is not falsy then it is inserted instead of the -// label." -// }, -// "sortText": { -// "type": "string", -// "description": "A string that should be used when comparing this item -// with other items. When `falsy` the label is used." -// }, -// "type": { -// "$ref": "#/definitions/CompletionItemType", -// "description": "The item's type. Typically the client uses this -// information to render the item in the UI with an icon." -// }, -// "start": { -// "type": "integer", -// "description": "This value determines the location (in the -// CompletionsRequest's 'text' attribute) where the completion text is -// added.\nIf missing the text is added at the location specified by the -// CompletionsRequest's 'column' attribute." -// }, -// "length": { -// "type": "integer", -// "description": "This value determines how many characters are -// overwritten by the completion text.\nIf missing the value 0 is assumed -// which results in the completion text being inserted." -// } -// }, -// "required": [ "label" ] -// }, -// "CompletionItemType": { -// "type": "string", -// "description": "Some predefined types for the CompletionItem. Please note -// that not all clients have specific icons for all of them.", "enum": [ -// "method", "function", "constructor", "field", "variable", "class", -// "interface", "module", "property", "unit", "value", "enum", "keyword", -// "snippet", "text", "color", "file", "reference", "customcolor" ] -// } -void CompletionsRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - FillResponse(request, response); - llvm::json::Object body; - const auto *arguments = request.getObject("arguments"); +namespace lldb_dap { +/// Returns a list of possible completions for a given caret position and text. +/// +/// Clients should only call this request if the corresponding capability +/// `supportsCompletionsRequest` is true. +Expected<CompletionsResponseBody> +CompletionsRequestHandler::Run(const CompletionsArguments &args) const { // If we have a frame, try to set the context for variable completions. - lldb::SBFrame frame = dap.GetLLDBFrame(*arguments); + lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId); if (frame.IsValid()) { frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread()); frame.GetThread().SetSelectedFrame(frame.GetFrameID()); } - std::string text = GetString(arguments, "text").value_or("").str(); - auto original_column = - GetInteger<int64_t>(arguments, "column").value_or(text.size()); - auto original_line = GetInteger<int64_t>(arguments, "line").value_or(1); + std::string text = args.text; + auto original_column = args.column; + auto original_line = args.line; auto offset = original_column - 1; if (original_line > 1) { - llvm::SmallVector<::llvm::StringRef, 2> lines; - llvm::StringRef(text).split(lines, '\n'); + SmallVector<StringRef, 2> lines; + StringRef(text).split(lines, '\n'); for (int i = 0; i < original_line - 1; i++) { offset += lines[i].size(); } } - llvm::json::Array targets; + + std::vector<CompletionItem> targets; bool had_escape_prefix = - llvm::StringRef(text).starts_with(dap.configuration.commandEscapePrefix); + StringRef(text).starts_with(dap.configuration.commandEscapePrefix); ReplMode completion_mode = dap.DetectReplMode(frame, text, true); // Handle the offset change introduced by stripping out the @@ -165,10 +55,7 @@ void CompletionsRequestHandler::operator()( if (had_escape_prefix) { if (offset < static_cast<int64_t>(dap.configuration.commandEscapePrefix.size())) { - body.try_emplace("targets", std::move(targets)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; + return CompletionsResponseBody{std::move(targets)}; } offset -= dap.configuration.commandEscapePrefix.size(); } @@ -198,27 +85,25 @@ void CompletionsRequestHandler::operator()( std::string match = matches.GetStringAtIndex(i); std::string description = descriptions.GetStringAtIndex(i); - llvm::json::Object item; - llvm::StringRef match_ref = match; - for (llvm::StringRef commit_point : {".", "->"}) { + CompletionItem item; + StringRef match_ref = match; + for (StringRef commit_point : {".", "->"}) { if (match_ref.contains(commit_point)) { match_ref = match_ref.rsplit(commit_point).second; } } - EmplaceSafeString(item, "text", match_ref); + item.text = match_ref; if (description.empty()) - EmplaceSafeString(item, "label", match); + item.label = match; else - EmplaceSafeString(item, "label", match + " -- " + description); + item.label = match + " -- " + description; targets.emplace_back(std::move(item)); } } - body.try_emplace("targets", std::move(targets)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + return CompletionsResponseBody{std::move(targets)}; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp index e7735a7..1bfe7b7 100644 --- a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp @@ -9,6 +9,7 @@ #include "DAP.h" #include "EventHelper.h" #include "LLDBUtils.h" +#include "Protocol/ProtocolEvents.h" #include "Protocol/ProtocolRequests.h" #include "ProtocolUtils.h" #include "RequestHandler.h" @@ -44,7 +45,10 @@ ConfigurationDoneRequestHandler::Run(const ConfigurationDoneArguments &) const { // Waiting until 'configurationDone' to send target based capabilities in case // the launch or attach scripts adjust the target. The initial dummy target // may have different capabilities than the final target. - SendTargetBasedCapabilities(dap); + + /// Also send here custom capabilities to the client, which is consumed by the + /// lldb-dap specific editor extension. + SendExtraCapabilities(dap); // Clients can request a baseline of currently existing threads after // we acknowledge the configurationDone request. diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp index 8cb25d0..87b93fc 100644 --- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp @@ -23,7 +23,7 @@ llvm::Expected<protocol::DataBreakpointInfoResponseBody> DataBreakpointInfoRequestHandler::Run( const protocol::DataBreakpointInfoArguments &args) const { protocol::DataBreakpointInfoResponseBody response; - lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId.value_or(UINT64_MAX)); + lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId); lldb::SBValue variable = dap.variables.FindVariable( args.variablesReference.value_or(0), args.name); std::string addr, size; diff --git a/lldb/tools/lldb-dap/Handler/ModuleSymbolsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ModuleSymbolsRequestHandler.cpp new file mode 100644 index 0000000..4a9d256 --- /dev/null +++ b/lldb/tools/lldb-dap/Handler/ModuleSymbolsRequestHandler.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DAP.h" +#include "DAPError.h" +#include "Protocol/DAPTypes.h" +#include "RequestHandler.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBModule.h" +#include "lldb/API/SBModuleSpec.h" +#include "lldb/Utility/UUID.h" +#include "llvm/Support/Error.h" +#include <cstddef> + +using namespace lldb_dap::protocol; +namespace lldb_dap { + +llvm::Expected<ModuleSymbolsResponseBody> +ModuleSymbolsRequestHandler::Run(const ModuleSymbolsArguments &args) const { + ModuleSymbolsResponseBody response; + + lldb::SBModuleSpec module_spec; + if (!args.moduleId.empty()) { + llvm::SmallVector<uint8_t, 20> uuid_bytes; + if (!lldb_private::UUID::DecodeUUIDBytesFromString(args.moduleId, + uuid_bytes) + .empty()) + return llvm::make_error<DAPError>("invalid module ID"); + + module_spec.SetUUIDBytes(uuid_bytes.data(), uuid_bytes.size()); + } + + if (!args.moduleName.empty()) { + lldb::SBFileSpec file_spec; + file_spec.SetFilename(args.moduleName.c_str()); + module_spec.SetFileSpec(file_spec); + } + + // Empty request, return empty response. + if (!module_spec.IsValid()) + return response; + + std::vector<Symbol> &symbols = response.symbols; + lldb::SBModule module = dap.target.FindModule(module_spec); + if (!module.IsValid()) + return llvm::make_error<DAPError>("module not found"); + + const size_t num_symbols = module.GetNumSymbols(); + const size_t start_index = args.startIndex.value_or(0); + const size_t end_index = + std::min(start_index + args.count.value_or(num_symbols), num_symbols); + for (size_t i = start_index; i < end_index; ++i) { + lldb::SBSymbol symbol = module.GetSymbolAtIndex(i); + if (!symbol.IsValid()) + continue; + + Symbol dap_symbol; + dap_symbol.id = symbol.GetID(); + dap_symbol.type = symbol.GetType(); + dap_symbol.isDebug = symbol.IsDebug(); + dap_symbol.isSynthetic = symbol.IsSynthetic(); + dap_symbol.isExternal = symbol.IsExternal(); + + lldb::SBAddress start_address = symbol.GetStartAddress(); + if (start_address.IsValid()) { + lldb::addr_t file_address = start_address.GetFileAddress(); + if (file_address != LLDB_INVALID_ADDRESS) + dap_symbol.fileAddress = file_address; + + lldb::addr_t load_address = start_address.GetLoadAddress(dap.target); + if (load_address != LLDB_INVALID_ADDRESS) + dap_symbol.loadAddress = load_address; + } + + dap_symbol.size = symbol.GetSize(); + if (const char *symbol_name = symbol.GetName()) + dap_symbol.name = symbol_name; + symbols.push_back(std::move(dap_symbol)); + } + + return response; +} + +} // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 16f8062..977a247 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -243,14 +243,17 @@ public: uint32_t end_line) const; }; -class CompletionsRequestHandler : public LegacyRequestHandler { +class CompletionsRequestHandler + : public RequestHandler<protocol::CompletionsArguments, + llvm::Expected<protocol::CompletionsResponseBody>> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "completions"; } FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureCompletionsRequest}; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected<protocol::CompletionsResponseBody> + Run(const protocol::CompletionsArguments &args) const override; }; class ContinueRequestHandler @@ -594,6 +597,20 @@ public: llvm::Error Run(const protocol::CancelArguments &args) const override; }; +class ModuleSymbolsRequestHandler + : public RequestHandler< + protocol::ModuleSymbolsArguments, + llvm::Expected<protocol::ModuleSymbolsResponseBody>> { +public: + using RequestHandler::RequestHandler; + static llvm::StringLiteral GetCommand() { return "__lldb_moduleSymbols"; } + FeatureSet GetSupportedFeatures() const override { + return {protocol::eAdapterFeatureSupportsModuleSymbolsRequest}; + } + llvm::Expected<protocol::ModuleSymbolsResponseBody> + Run(const protocol::ModuleSymbolsArguments &args) const override; +}; + /// A request used in testing to get the details on all breakpoints that are /// currently set in the target. This helps us to test "setBreakpoints" and /// "setFunctionBreakpoints" requests to verify we have the correct set of diff --git a/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp index 5d336af..142351f 100644 --- a/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp @@ -9,7 +9,6 @@ #include "DAP.h" #include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" -#include <vector> namespace lldb_dap { |