Author: John Harrison Date: 2026-02-19T08:50:19-08:00 New Revision: 070db69bebad7c5d35d7b8327fa9dce597f60991
URL: https://github.com/llvm/llvm-project/commit/070db69bebad7c5d35d7b8327fa9dce597f60991 DIFF: https://github.com/llvm/llvm-project/commit/070db69bebad7c5d35d7b8327fa9dce597f60991.diff LOG: Reapply "[lldb-dap] Validate utf8 protocol messages." (#181930) (#182056) This reverts commit 977d910d005c47f884ecf838e504da301b1124b9. Addressing build issues with gcc. Added: lldb/unittests/DAP/ProtocolBaseTest.cpp Modified: lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py lldb/test/API/tools/lldb-dap/variables/main.cpp lldb/tools/lldb-dap/DAP.cpp lldb/tools/lldb-dap/DAP.h lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp lldb/tools/lldb-dap/Handler/RequestHandler.cpp lldb/tools/lldb-dap/JSONUtils.cpp lldb/tools/lldb-dap/JSONUtils.h lldb/tools/lldb-dap/LLDBUtils.cpp lldb/tools/lldb-dap/LLDBUtils.h lldb/tools/lldb-dap/Protocol/DAPTypes.h lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp lldb/tools/lldb-dap/Protocol/ProtocolBase.h lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp lldb/tools/lldb-dap/Protocol/ProtocolRequests.h lldb/tools/lldb-dap/Protocol/ProtocolTypes.h lldb/tools/lldb-dap/ProtocolUtils.cpp lldb/tools/lldb-dap/tool/lldb-dap.cpp lldb/unittests/DAP/CMakeLists.txt lldb/unittests/DAP/ProtocolTypesTest.cpp lldb/unittests/DAP/TestBase.cpp lldb/unittests/DAP/VariablesTest.cpp Removed: ################################################################################ diff --git a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py index a70fefd358b4b..7abcec56a7880 100644 --- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py +++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py @@ -195,6 +195,8 @@ def do_test_scopes_variables_setVariable_evaluate( }, "readOnly": True, }, + "valid_str": {}, + "malformed_str": {}, "x": {"equals": {"type": "int"}}, } @@ -357,9 +359,9 @@ def do_test_scopes_variables_setVariable_evaluate( verify_locals["argc"]["equals"]["value"] = "123" verify_locals["pt"]["children"]["x"]["equals"]["value"] = "111" - verify_locals["x @ main.cpp:19"] = {"equals": {"type": "int", "value": "89"}} - verify_locals["x @ main.cpp:21"] = {"equals": {"type": "int", "value": "42"}} - verify_locals["x @ main.cpp:23"] = {"equals": {"type": "int", "value": "72"}} + verify_locals["x @ main.cpp:23"] = {"equals": {"type": "int", "value": "89"}} + verify_locals["x @ main.cpp:25"] = {"equals": {"type": "int", "value": "42"}} + verify_locals["x @ main.cpp:27"] = {"equals": {"type": "int", "value": "72"}} self.verify_variables(verify_locals, self.dap_server.get_local_variables()) @@ -367,22 +369,22 @@ def do_test_scopes_variables_setVariable_evaluate( self.assertFalse(self.set_local("x2", 9)["success"]) self.assertFalse(self.set_local("x @ main.cpp:0", 9)["success"]) - self.assertTrue(self.set_local("x @ main.cpp:19", 19)["success"]) - self.assertTrue(self.set_local("x @ main.cpp:21", 21)["success"]) - self.assertTrue(self.set_local("x @ main.cpp:23", 23)["success"]) + self.assertTrue(self.set_local("x @ main.cpp:23", 19)["success"]) + self.assertTrue(self.set_local("x @ main.cpp:25", 21)["success"]) + self.assertTrue(self.set_local("x @ main.cpp:27", 23)["success"]) # The following should have no effect - self.assertFalse(self.set_local("x @ main.cpp:23", "invalid")["success"]) + self.assertFalse(self.set_local("x @ main.cpp:27", "invalid")["success"]) - verify_locals["x @ main.cpp:19"]["equals"]["value"] = "19" - verify_locals["x @ main.cpp:21"]["equals"]["value"] = "21" - verify_locals["x @ main.cpp:23"]["equals"]["value"] = "23" + verify_locals["x @ main.cpp:23"]["equals"]["value"] = "19" + verify_locals["x @ main.cpp:25"]["equals"]["value"] = "21" + verify_locals["x @ main.cpp:27"]["equals"]["value"] = "23" self.verify_variables(verify_locals, self.dap_server.get_local_variables()) # The plain x variable shold refer to the innermost x self.assertTrue(self.set_local("x", 22)["success"]) - verify_locals["x @ main.cpp:23"]["equals"]["value"] = "22" + verify_locals["x @ main.cpp:27"]["equals"]["value"] = "22" self.verify_variables(verify_locals, self.dap_server.get_local_variables()) @@ -399,9 +401,9 @@ def do_test_scopes_variables_setVariable_evaluate( names = [var["name"] for var in locals] # The first shadowed x shouldn't have a suffix anymore verify_locals["x"] = {"equals": {"type": "int", "value": "19"}} - self.assertNotIn("x @ main.cpp:19", names) - self.assertNotIn("x @ main.cpp:21", names) self.assertNotIn("x @ main.cpp:23", names) + self.assertNotIn("x @ main.cpp:25", names) + self.assertNotIn("x @ main.cpp:27", names) self.verify_variables(verify_locals, locals) @@ -472,6 +474,22 @@ def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: boo }, "readOnly": True, }, + "valid_str": { + "equals": { + "type": "const char *", + }, + "matches": { + "value": r'0x\w+ "πΆπ°LπΎπ CππΌπ΄π"', + }, + }, + "malformed_str": { + "equals": { + "type": "const char *", + }, + "matches": { + "value": r'0x\w+ "lone trailing \\x81\\x82 bytes"', + }, + }, "x": { "equals": {"type": "int"}, "missing": ["indexedVariables"], @@ -712,6 +730,8 @@ def test_return_variables(self): "argc": {}, "argv": {}, "pt": {"readOnly": True}, + "valid_str": {}, + "malformed_str": {}, "x": {}, "return_result": {"equals": {"type": "int"}}, } diff --git a/lldb/test/API/tools/lldb-dap/variables/main.cpp b/lldb/test/API/tools/lldb-dap/variables/main.cpp index 0e363001f2f42..04fc62f02c22f 100644 --- a/lldb/test/API/tools/lldb-dap/variables/main.cpp +++ b/lldb/test/API/tools/lldb-dap/variables/main.cpp @@ -5,6 +5,7 @@ struct PointType { int y; int buffer[BUFFER_SIZE]; }; +#include <cstdio> #include <vector> int g_global = 123; static int s_global = 234; @@ -16,6 +17,9 @@ int main(int argc, char const *argv[]) { PointType pt = {11, 22, {0}}; for (int i = 0; i < BUFFER_SIZE; ++i) pt.buffer[i] = i; + const char *valid_str = "πΆπ°LπΎπ CππΌπ΄π"; + const char *malformed_str = "lone trailing \x81\x82 bytes"; + printf("print malformed utf8 %s %s\n", valid_str, malformed_str); int x = s_global - g_global - pt.y; // breakpoint 1 { int x = 42; diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 4d7fee5b1e848..8986d740d31aa 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -124,7 +124,7 @@ static std::string capitalize(llvm::StringRef str) { llvm::StringRef DAP::debug_adapter_path = ""; DAP::DAP(Log &log, const ReplMode default_repl_mode, - std::vector<std::string> pre_init_commands, bool no_lldbinit, + const std::vector<String> &pre_init_commands, bool no_lldbinit, llvm::StringRef client_name, DAPTransport &transport, MainLoop &loop) : log(log), transport(transport), reference_storage(log), broadcaster("lldb-dap"), @@ -132,7 +132,7 @@ DAP::DAP(Log &log, const ReplMode default_repl_mode, [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), repl_mode(default_repl_mode), no_lldbinit(no_lldbinit), m_client_name(client_name), m_loop(loop) { - configuration.preInitCommands = std::move(pre_init_commands); + configuration.preInitCommands = pre_init_commands; RegisterRequests(); } @@ -413,9 +413,10 @@ void DAP::SendOutput(OutputType o, const llvm::StringRef output) { if (end == llvm::StringRef::npos) end = output.size() - 1; llvm::json::Object event(CreateEventObject("output")); - llvm::json::Object body; - body.try_emplace("category", category); - EmplaceSafeString(body, "output", output.slice(idx, end + 1).str()); + llvm::json::Object body{ + {"category", category}, + {"output", protocol::String(output.slice(idx, end + 1))}, + }; event.try_emplace("body", std::move(body)); SendJSON(llvm::json::Value(std::move(event))); idx = end + 1; @@ -700,7 +701,7 @@ DAP::ResolveAssemblySource(lldb::SBAddress address) { } bool DAP::RunLLDBCommands(llvm::StringRef prefix, - llvm::ArrayRef<std::string> commands) { + llvm::ArrayRef<String> commands) { bool required_command_failed = false; std::string output = ::RunLLDBCommands( debugger, prefix, commands, required_command_failed, @@ -719,15 +720,13 @@ static llvm::Error createRunLLDBCommandsErrorMessage(llvm::StringRef category) { .c_str()); } -llvm::Error -DAP::RunAttachCommands(llvm::ArrayRef<std::string> attach_commands) { +llvm::Error DAP::RunAttachCommands(llvm::ArrayRef<String> attach_commands) { if (!RunLLDBCommands("Running attachCommands:", attach_commands)) return createRunLLDBCommandsErrorMessage("attach"); return llvm::Error::success(); } -llvm::Error -DAP::RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands) { +llvm::Error DAP::RunLaunchCommands(llvm::ArrayRef<String> launch_commands) { if (!RunLLDBCommands("Running launchCommands:", launch_commands)) return createRunLLDBCommandsErrorMessage("launch"); return llvm::Error::success(); @@ -779,9 +778,9 @@ lldb::SBTarget DAP::CreateTarget(lldb::SBError &error) { // omitted at all), so it is good to leave the user an opportunity to specify // those. Any of those three can be left empty. auto target = this->debugger.CreateTarget( - /*filename=*/configuration.program.data(), - /*target_triple=*/configuration.targetTriple.data(), - /*platform_name=*/configuration.platformName.data(), + /*filename=*/configuration.program.c_str(), + /*target_triple=*/configuration.targetTriple.c_str(), + /*platform_name=*/configuration.platformName.c_str(), /*add_dependent_modules=*/true, // Add dependent modules. error); @@ -826,8 +825,7 @@ bool DAP::HandleObject(const Message &M) { }); auto handler_pos = request_handlers.find(req->command); - dispatcher.Set("client_data", - llvm::Twine("request_command:", req->command).str()); + dispatcher.Set("client_data", "request_command:" + req->command); if (handler_pos != request_handlers.end()) { handler_pos->second->Run(*req); } else { @@ -855,14 +853,13 @@ bool DAP::HandleObject(const Message &M) { // Result should be given, use null if not. if (resp->success) { (*response_handler)(resp->body); - dispatcher.Set("client_data", - llvm::Twine("response_command:", resp->command).str()); + dispatcher.Set("client_data", "response_command:" + resp->command); } else { llvm::StringRef message = "Unknown error, response failed"; if (resp->message) { message = std::visit(llvm::makeVisitor( - [](const std::string &message) -> llvm::StringRef { + [](const String &message) -> llvm::StringRef { return message; }, [](const protocol::ResponseMessage &message) @@ -946,7 +943,7 @@ void DAP::ClearCancelRequest(const CancelArguments &args) { template <typename T> static std::optional<T> getArgumentsIfRequest(const Request &req, - llvm::StringLiteral command) { + const protocol::String &command) { if (req.command != command) return std::nullopt; @@ -1244,7 +1241,7 @@ protocol::Capabilities DAP::GetCapabilities() { capabilities.exceptionBreakpointFilters = std::move(filters); // FIXME: This should be registered based on the supported languages? - std::vector<std::string> completion_characters; + std::vector<String> completion_characters; completion_characters.emplace_back("."); // FIXME: I wonder if we should remove this key... its very aggressive // triggering and accepting completions. diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 3346be75fa001..51f652892f61f 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -192,7 +192,7 @@ struct DAP final : public DAPTransport::MessageHandler { /// \param[in] loop /// Main loop associated with this instance. DAP(Log &log, const ReplMode default_repl_mode, - std::vector<std::string> pre_init_commands, bool no_lldbinit, + const std::vector<protocol::String> &pre_init_commands, bool no_lldbinit, llvm::StringRef client_name, DAPTransport &transport, lldb_private::MainLoop &loop); @@ -305,10 +305,12 @@ struct DAP final : public DAPTransport::MessageHandler { /// \b false if a fatal error was found while executing these commands, /// according to the rules of \a LLDBUtils::RunLLDBCommands. bool RunLLDBCommands(llvm::StringRef prefix, - llvm::ArrayRef<std::string> commands); + llvm::ArrayRef<protocol::String> commands); - llvm::Error RunAttachCommands(llvm::ArrayRef<std::string> attach_commands); - llvm::Error RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands); + llvm::Error + RunAttachCommands(llvm::ArrayRef<protocol::String> attach_commands); + llvm::Error + RunLaunchCommands(llvm::ArrayRef<protocol::String> launch_commands); llvm::Error RunPreInitCommands(); llvm::Error RunInitCommands(); llvm::Error RunPreRunCommands(); diff --git a/lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp index e1a3b15b4697b..d24072c8cc05d 100644 --- a/lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp @@ -30,7 +30,7 @@ llvm::Expected<CompileUnitsResponseBody> CompileUnitsRequestHandler::Run( int num_modules = dap.target.GetNumModules(); for (int i = 0; i < num_modules; i++) { auto curr_module = dap.target.GetModuleAtIndex(i); - if (args->moduleId == llvm::StringRef(curr_module.GetUUIDString())) { + if (args->moduleId == curr_module.GetUUIDString()) { int num_units = curr_module.GetNumCompileUnits(); for (int j = 0; j < num_units; j++) { auto curr_unit = curr_module.GetCompileUnitAtIndex(j); diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp index 0b959a5260d23..76177eb9b57cc 100644 --- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp @@ -94,7 +94,8 @@ DataBreakpointInfoRequestHandler::Run( size = llvm::utostr(args.bytes.value_or(dap.target.GetAddressByteSize())); lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; if (llvm::StringRef(args.name).getAsInteger<lldb::addr_t>(0, load_addr)) - return llvm::make_error<DAPError>(args.name + " is not a valid address", + return llvm::make_error<DAPError>(args.name.str() + + " is not a valid address", llvm::inconvertibleErrorCode(), false); addr = llvm::utohexstr(load_addr); if (!IsRW(dap, load_addr)) diff --git a/lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp index 8387f9ab5c387..9d470b493188f 100644 --- a/lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp @@ -101,7 +101,7 @@ static DisassembledInstruction ConvertSBInstructionToDisassembledInstruction( const char *m = inst.GetMnemonic(target); const char *o = inst.GetOperands(target); - std::string c = inst.GetComment(target); + std::string comment = inst.GetComment(target); auto d = inst.GetData(target); std::string bytes; @@ -119,24 +119,22 @@ static DisassembledInstruction ConvertSBInstructionToDisassembledInstruction( if (!bytes.empty()) // remove last whitespace bytes.pop_back(); disassembled_inst.instructionBytes = std::move(bytes); - - llvm::raw_string_ostream si(disassembled_inst.instruction); - si << llvm::formatv("{0,-7} {1,-25}", m, o); + disassembled_inst.instruction = llvm::formatv("{0,-7} {1,-25}", m, o); // Only add the symbol on the first line of the function. // in the comment section if (lldb::SBSymbol symbol = addr.GetSymbol(); symbol.GetStartAddress() == addr) { const llvm::StringRef sym_display_name = symbol.GetDisplayName(); - c.append(" "); - c.append(sym_display_name); + comment.append(" "); + comment.append(sym_display_name); if (resolve_symbols) disassembled_inst.symbol = sym_display_name; } - if (!c.empty()) { - si << " ; " << c; + if (!comment.empty()) { + disassembled_inst.instruction += " ; " + comment; } std::optional<protocol::Source> source = dap.ResolveSource(addr); diff --git a/lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp index 74ab9a2837034..c92a607d9b7b0 100644 --- a/lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp @@ -268,15 +268,18 @@ static std::optional<ExceptionDetails> FormatException(lldb::SBThread &thread) { return {}; ExceptionDetails details; - raw_string_ostream OS(details.message); if (const char *name = exception.GetName()) details.evaluateName = name; if (const char *typeName = exception.GetDisplayTypeName()) details.typeName = typeName; + std::string message; + raw_string_ostream OS(message); OS << exception; + details.message = std::move(message); + if (lldb::SBThread exception_backtrace = thread.GetCurrentExceptionBacktrace()) details.stackTrace = FormatStackTrace(exception_backtrace); @@ -316,22 +319,19 @@ ExceptionInfoRequestHandler::Run(const ExceptionInfoArguments &args) const { body.breakMode = eExceptionBreakModeAlways; body.exceptionId = FormatExceptionId(dap, thread); body.details = FormatException(thread); - - raw_string_ostream OS(body.description); - OS << FormatStopDescription(thread); + body.description = FormatStopDescription(thread); if (std::string stop_info = FormatExtendedStopInfo(thread); !stop_info.empty()) - OS << "\n\n" << stop_info; + body.description += formatv("\n\n{0}", stop_info); if (std::string crash_report = FormatCrashReport(thread); !crash_report.empty()) - OS << "\n\n" << crash_report; + body.description += formatv("\n\n{0}", crash_report); lldb::SBProcess process = thread.GetProcess(); for (uint32_t idx = 0; idx < lldb::eNumInstrumentationRuntimeTypes; idx++) { - lldb::InstrumentationRuntimeType type = - static_cast<lldb::InstrumentationRuntimeType>(idx); + auto type = static_cast<lldb::InstrumentationRuntimeType>(idx); if (!process.IsInstrumentationRuntimePresent(type)) continue; diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 47ae9a7195a7d..5e8c2163c838f 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -37,8 +37,7 @@ using namespace lldb_dap::protocol; namespace lldb_dap { -static std::vector<const char *> -MakeArgv(const llvm::ArrayRef<std::string> &strs) { +static std::vector<const char *> MakeArgv(const llvm::ArrayRef<String> &strs) { // Create and return an array of "const char *", one for each C string in // "strs" and terminate the list with a NULL. This can be used for argument // vectors (argv) or environment vectors (envp) like those passed to the @@ -60,9 +59,8 @@ static uint32_t SetLaunchFlag(uint32_t flags, bool flag, return flags; } -static void -SetupIORedirection(const std::vector<std::optional<std::string>> &stdio, - lldb::SBLaunchInfo &launch_info) { +static void SetupIORedirection(const std::vector<std::optional<String>> &stdio, + lldb::SBLaunchInfo &launch_info) { for (const auto &[idx, value_opt] : llvm::enumerate(stdio)) { if (!value_opt) continue; @@ -191,7 +189,7 @@ void BaseRequestHandler::Run(const Request &request) { llvm::Error BaseRequestHandler::LaunchProcess( const protocol::LaunchRequestArguments &arguments) const { - const std::vector<std::string> &launchCommands = arguments.launchCommands; + const std::vector<String> &launchCommands = arguments.launchCommands; // Instantiate a launch info instance for the target. auto launch_info = dap.target.GetLaunchInfo(); @@ -340,8 +338,8 @@ void BaseRequestHandler::BuildErrorResponse( error_message.format = err.getMessage(); error_message.showUser = err.getShowUser(); error_message.id = err.convertToErrorCode().value(); - error_message.url = err.getURL(); - error_message.urlLabel = err.getURLLabel(); + error_message.url = err.getURL().value_or(""); + error_message.urlLabel = err.getURLLabel().value_or(""); protocol::ErrorResponseBody body; body.error = error_message; diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index d10212d22ab0d..d75b4e23d0e46 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -359,10 +359,10 @@ std::pair<int64_t, bool> UnpackLocation(int64_t location_id) { /// See /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal llvm::json::Object CreateRunInTerminalReverseRequest( - llvm::StringRef program, const std::vector<std::string> &args, - const llvm::StringMap<std::string> &env, llvm::StringRef cwd, + llvm::StringRef program, const std::vector<protocol::String> &args, + const llvm::StringMap<protocol::String> &env, llvm::StringRef cwd, llvm::StringRef comm_file, lldb::pid_t debugger_pid, - const std::vector<std::optional<std::string>> &stdio, bool external) { + const std::vector<std::optional<protocol::String>> &stdio, bool external) { llvm::json::Object run_in_terminal_args; if (external) { // This indicates the IDE to open an external terminal window. @@ -385,10 +385,10 @@ llvm::json::Object CreateRunInTerminalReverseRequest( std::stringstream ss; std::string_view delimiter; - for (const std::optional<std::string> &file : stdio) { + for (const std::optional<protocol::String> &file : stdio) { ss << std::exchange(delimiter, ":"); if (file) - ss << *file; + ss << file->str(); } req_args.push_back(ss.str()); } diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h index 662f20b3b2372..74a7e4bb5c983 100644 --- a/lldb/tools/lldb-dap/JSONUtils.h +++ b/lldb/tools/lldb-dap/JSONUtils.h @@ -193,10 +193,10 @@ std::pair<int64_t, bool> UnpackLocation(int64_t location_id); /// A "runInTerminal" JSON object that follows the specification outlined by /// Microsoft. llvm::json::Object CreateRunInTerminalReverseRequest( - llvm::StringRef program, const std::vector<std::string> &args, - const llvm::StringMap<std::string> &env, llvm::StringRef cwd, + llvm::StringRef program, const std::vector<protocol::String> &args, + const llvm::StringMap<protocol::String> &env, llvm::StringRef cwd, llvm::StringRef comm_file, lldb::pid_t debugger_pid, - const std::vector<std::optional<std::string>> &stdio, bool external); + const std::vector<std::optional<protocol::String>> &stdio, bool external); /// Create a "Terminated" JSON object that contains statistics /// diff --git a/lldb/tools/lldb-dap/LLDBUtils.cpp b/lldb/tools/lldb-dap/LLDBUtils.cpp index d5e91e7e18170..95eb85b7f2226 100644 --- a/lldb/tools/lldb-dap/LLDBUtils.cpp +++ b/lldb/tools/lldb-dap/LLDBUtils.cpp @@ -32,7 +32,7 @@ namespace lldb_dap { bool RunLLDBCommands(lldb::SBDebugger &debugger, llvm::StringRef prefix, - const llvm::ArrayRef<std::string> &commands, + const llvm::ArrayRef<protocol::String> &commands, llvm::raw_ostream &strm, bool parse_command_directives, bool echo_commands) { if (commands.empty()) @@ -115,7 +115,7 @@ bool RunLLDBCommands(lldb::SBDebugger &debugger, llvm::StringRef prefix, } std::string RunLLDBCommands(lldb::SBDebugger &debugger, llvm::StringRef prefix, - const llvm::ArrayRef<std::string> &commands, + const llvm::ArrayRef<protocol::String> &commands, bool &required_command_failed, bool parse_command_directives, bool echo_commands) { required_command_failed = false; diff --git a/lldb/tools/lldb-dap/LLDBUtils.h b/lldb/tools/lldb-dap/LLDBUtils.h index daec8ca65a760..3565b398160e7 100644 --- a/lldb/tools/lldb-dap/LLDBUtils.h +++ b/lldb/tools/lldb-dap/LLDBUtils.h @@ -10,6 +10,7 @@ #define LLDB_TOOLS_LLDB_DAP_LLDBUTILS_H #include "DAPForward.h" +#include "Protocol/ProtocolBase.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBEnvironment.h" #include "lldb/API/SBError.h" @@ -63,7 +64,7 @@ namespace lldb_dap { /// \b true, unless a command prefixed with \b ! fails and parsing of /// command directives is enabled. bool RunLLDBCommands(lldb::SBDebugger &debugger, llvm::StringRef prefix, - const llvm::ArrayRef<std::string> &commands, + const llvm::ArrayRef<protocol::String> &commands, llvm::raw_ostream &strm, bool parse_command_directives, bool echo_commands); @@ -97,7 +98,7 @@ bool RunLLDBCommands(lldb::SBDebugger &debugger, llvm::StringRef prefix, /// A std::string that contains the prefix and all commands and /// command output. std::string RunLLDBCommands(lldb::SBDebugger &debugger, llvm::StringRef prefix, - const llvm::ArrayRef<std::string> &commands, + const llvm::ArrayRef<protocol::String> &commands, bool &required_command_failed, bool parse_command_directives = true, bool echo_commands = false); diff --git a/lldb/tools/lldb-dap/Protocol/DAPTypes.h b/lldb/tools/lldb-dap/Protocol/DAPTypes.h index 4fc0c8db1acc8..5c5d279656d83 100644 --- a/lldb/tools/lldb-dap/Protocol/DAPTypes.h +++ b/lldb/tools/lldb-dap/Protocol/DAPTypes.h @@ -16,12 +16,12 @@ #ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_DAP_TYPES_H #define LLDB_TOOLS_LLDB_DAP_PROTOCOL_DAP_TYPES_H +#include "Protocol/ProtocolBase.h" #include "lldb/lldb-defines.h" #include "lldb/lldb-types.h" #include "llvm/Support/JSON.h" #include <cstdint> #include <optional> -#include <string> namespace lldb_dap::protocol { @@ -99,10 +99,10 @@ inline llvm::json::Value toJSON(const var_ref_t &var_ref) { /// breakpoints the path and line are the same For each session. struct PersistenceData { /// The source module path. - std::string module_path; + String module_path; /// The symbol name of the Source. - std::string symbol_name; + String symbol_name; }; bool fromJSON(const llvm::json::Value &, PersistenceData &, llvm::json::Path); llvm::json::Value toJSON(const PersistenceData &); @@ -145,7 +145,7 @@ struct Symbol { lldb::addr_t size = 0; /// The symbol name. - std::string name; + String name; }; bool fromJSON(const llvm::json::Value &, Symbol &, llvm::json::Path); llvm::json::Value toJSON(const Symbol &); diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp index 72359214c8537..9008719ba543d 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp @@ -57,6 +57,22 @@ bool fromJSON(const json::Value &Params, MessageType &M, json::Path P) { return true; } +json::Value toJSON(const String &S) { + if (LLVM_LIKELY(llvm::json::isUTF8(std::string(S)))) + return std::string(S); + return llvm::json::fixUTF8(std::string(S)); +} + +bool fromJSON(const llvm::json::Value &Param, String &Str, + llvm::json::Path Path) { + if (auto s = Param.getAsString()) { + Str = *std::move(s); + return true; + } + Path.report("expected string"); + return false; +} + json::Value toJSON(const Request &R) { assert(R.seq != kCalculateSeq && "invalid seq"); @@ -124,8 +140,7 @@ json::Value toJSON(const Response &R) { Result.insert({"message", "notStopped"}); break; } - } else if (const auto *messageString = - std::get_if<std::string>(&*R.message)) { + } else if (const auto *messageString = std::get_if<String>(&*R.message)) { Result.insert({"message", *messageString}); } } @@ -137,8 +152,7 @@ json::Value toJSON(const Response &R) { } static bool fromJSON(json::Value const &Params, - std::variant<ResponseMessage, std::string> &M, - json::Path P) { + std::variant<ResponseMessage, String> &M, json::Path P) { auto rawMessage = Params.getAsString(); if (!rawMessage) { P.report("expected a string"); @@ -188,31 +202,46 @@ bool operator==(const Response &a, const Response &b) { json::Value toJSON(const ErrorMessage &EM) { json::Object Result{{"id", EM.id}, {"format", EM.format}}; - if (EM.variables) { + if (!EM.variables.empty()) { json::Object variables; - for (auto &var : *EM.variables) - variables[var.first] = var.second; + for (auto &var : EM.variables) + variables[var.first.str()] = var.second; Result.insert({"variables", std::move(variables)}); } if (EM.sendTelemetry) Result.insert({"sendTelemetry", EM.sendTelemetry}); if (EM.showUser) Result.insert({"showUser", EM.showUser}); - if (EM.url) + if (!EM.url.empty()) Result.insert({"url", EM.url}); - if (EM.urlLabel) + if (!EM.urlLabel.empty()) Result.insert({"urlLabel", EM.urlLabel}); return std::move(Result); } +bool fromJSON(json::Value const &Params, std::map<String, String> &M, + json::Path P) { + const auto *const O = Params.getAsObject(); + if (!O) { + P.report("expected object"); + return false; + } + for (auto [k, v] : *O) { + auto str = v.getAsString(); + if (str) + M[k.str()] = *std::move(str); + } + return true; +} + bool fromJSON(json::Value const &Params, ErrorMessage &EM, json::Path P) { json::ObjectMapper O(Params, P); return O && O.map("id", EM.id) && O.map("format", EM.format) && - O.map("variables", EM.variables) && - O.map("sendTelemetry", EM.sendTelemetry) && - O.map("showUser", EM.showUser) && O.map("url", EM.url) && - O.map("urlLabel", EM.urlLabel); + O.mapOptional("variables", EM.variables) && + O.mapOptional("sendTelemetry", EM.sendTelemetry) && + O.mapOptional("showUser", EM.showUser) && + O.mapOptional("url", EM.url) && O.mapOptional("urlLabel", EM.urlLabel); } json::Value toJSON(const Event &E) { diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h index 09ce6802b17c0..4f51d7785cf14 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h @@ -20,7 +20,10 @@ #ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_BASE_H #define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_BASE_H +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" #include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" #include <cstdint> #include <optional> #include <string> @@ -37,10 +40,93 @@ using Id = uint64_t; /// the current session. static constexpr Id kCalculateSeq = UINT64_MAX; +/// A wrapper around a 'std::string' to ensure the contents are valid utf8 +/// during serialization. +class String { +public: + String() = default; + String(const std::string &str) : m_str(str) {} + String(llvm::StringRef str) : m_str(str.str()) {} + String(const char *str) : m_str(str) {} + String(const llvm::formatv_object_base &payload) : m_str(payload.str()) {} + String(const String &) = default; + String(String &&str) : m_str(std::move(str.m_str)) {} + String(std::string &&str) : m_str(std::move(str)) {} + + ~String() = default; + + String &operator=(const String &) = default; + String &operator=(String &&Other) { + m_str = std::move(Other.m_str); + return *this; + } + + /// Conversion Operators + /// @{ + operator llvm::Twine() const { return m_str; } + operator std::string() const { return m_str; } + operator llvm::StringRef() const { return {m_str}; } + /// @} + + void clear() { m_str.clear(); } + bool empty() const { return m_str.empty(); } + const char *c_str() const { return m_str.c_str(); } + const char *data() const { return m_str.data(); } + std::string str() const { return m_str; } + + inline String &operator+=(const String &RHS) { + m_str += RHS.m_str; + return *this; + } + + friend String operator+(const String &LHS, const String &RHS) { + return {LHS.m_str + RHS.m_str}; + } + + /// @name String Comparision Operators + /// @{ + + friend bool operator==(const String &LHS, const String &RHS) { + return llvm::StringRef(LHS) == llvm::StringRef(RHS); + } + + friend bool operator!=(const String &LHS, const String &RHS) { + return !(LHS == RHS); + } + + friend bool operator<(const String &LHS, const String &RHS) { + return llvm::StringRef(LHS) < llvm::StringRef(RHS); + } + + friend bool operator<=(const String &LHS, const String &RHS) { + return llvm::StringRef(LHS) <= llvm::StringRef(RHS); + } + + friend bool operator>(const String &LHS, const String &RHS) { + return llvm::StringRef(LHS) > llvm::StringRef(RHS); + } + + friend bool operator>=(const String &LHS, const String &RHS) { + return llvm::StringRef(LHS) >= llvm::StringRef(RHS); + } + + /// @} + +private: + std::string m_str; +}; +llvm::json::Value toJSON(const String &s); +bool fromJSON(const llvm::json::Value &, String &, llvm::json::Path); + +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const String &S) { + OS << S.str(); + return OS; +} + /// A client or debug adapter initiated request. struct Request { /// The command to execute. - std::string command; + String command; /// Object containing arguments for the command. /// @@ -64,7 +150,7 @@ bool operator==(const Request &, const Request &); /// A debug adapter initiated event. struct Event { /// Type of event. - std::string event; + String event; /// Event-specific information. std::optional<llvm::json::Value> body = std::nullopt; @@ -95,7 +181,7 @@ struct Response { Id request_seq = 0; /// The command requested. - std::string command; + String command; /// Outcome of the request. If true, the request was successful and the `body` /// attribute may contain the result of the request. If the value is false, @@ -108,8 +194,7 @@ struct Response { /// Contains the raw error in short form if `success` is false. This raw error /// might be interpreted by the client and is not shown in the UI. Some /// predefined values exist. - std::optional<std::variant<ResponseMessage, std::string>> message = - std::nullopt; + std::optional<std::variant<ResponseMessage, String>> message = std::nullopt; /// Contains request result if success is true and error details if success is /// false. @@ -144,11 +229,11 @@ struct ErrorMessage { /// `{name}`. If variable name starts with an underscore character, the /// variable does not contain user data (PII) and can be safely used for /// telemetry purposes. - std::string format; + String format; /// An object used as a dictionary for looking up the variables in the format /// string. - std::optional<std::map<std::string, std::string>> variables; + std::map<String, String> variables; /// If true send to telemetry. bool sendTelemetry = false; @@ -157,10 +242,10 @@ struct ErrorMessage { bool showUser = false; /// A url where additional information about this message can be found. - std::optional<std::string> url; + String url; /// A label that is presented to the user as the UI for opening the url. - std::optional<std::string> urlLabel; + String urlLabel; }; bool fromJSON(const llvm::json::Value &, ErrorMessage &, llvm::json::Path); llvm::json::Value toJSON(const ErrorMessage &); diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index faed1f8c4d574..f16d2ffcb7998 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -8,6 +8,7 @@ #include "Protocol/ProtocolRequests.h" #include "JSONUtils.h" +#include "Protocol/ProtocolBase.h" #include "Protocol/ProtocolTypes.h" #include "lldb/lldb-defines.h" #include "llvm/ADT/DenseMap.h" @@ -21,8 +22,8 @@ using namespace llvm; // The 'env' field is either an object as a map of strings or as an array of // strings formatted like 'key=value'. -static bool parseEnv(const json::Value &Params, StringMap<std::string> &env, - json::Path P) { +static bool parseEnv(const json::Value &Params, + StringMap<lldb_dap::protocol::String> &env, json::Path P) { const json::Object *O = Params.getAsObject(); if (!O) { P.report("expected object"); @@ -87,7 +88,8 @@ static bool parseTimeout(const json::Value &Params, std::chrono::seconds &S, static bool parseSourceMap(const json::Value &Params, - std::vector<std::pair<std::string, std::string>> &sourceMap, + std::vector<std::pair<lldb_dap::protocol::String, + lldb_dap::protocol::String>> &sourceMap, json::Path P) { const json::Object *O = Params.getAsObject(); if (!O) { @@ -311,7 +313,7 @@ bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA, if (!success) return false; - for (std::optional<std::string> &io_path : LRA.stdio) { + for (std::optional<String> &io_path : LRA.stdio) { // set empty paths to null. if (io_path && llvm::StringRef(*io_path).trim().empty()) io_path.reset(); diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index a8280bcdd9ee6..315f2d7b2f269 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -106,16 +106,16 @@ enum PathFormat : unsigned { ePatFormatPath, ePathFormatURI }; /// Arguments for `initialize` request. struct InitializeRequestArguments { /// The ID of the debug adapter. - std::string adapterID; + String adapterID; /// The ID of the client using this adapter. - std::string clientID; + String clientID; /// The human-readable name of the client using this adapter. - std::string clientName; + String clientName; /// The ISO-639 locale of the client using this adapter, e.g. en-US or de-CH. - std::string locale; + String locale; /// Determines in what format paths are specified. The default is `path`, /// which is the native format. @@ -153,7 +153,7 @@ struct Configuration { /// information in your executable contains relative paths, this option can be /// used so that `lldb-dap` can find source files and object files that have /// relative paths. - std::string debuggerRoot; + String debuggerRoot; /// Enable auto generated summaries for variables when no summaries exist for /// a given type. This feature can cause performance delays in large projects @@ -180,7 +180,7 @@ struct Configuration { /// Console, instead of printing variables. Defaults to a backtick. If it's an /// empty string, then all expression in the Debug Console are treated as /// regular LLDB commands. - std::string commandEscapePrefix = "`"; + String commandEscapePrefix = "`"; /// If non-empty, stack frames will have descriptions generated based on the /// provided format. See https://lldb.llvm.org/use/formatting.html for an @@ -189,58 +189,58 @@ struct Configuration { /// default frame names will be used. This might come with a performance cost /// because debug information might need to be processed to generate the /// description. - std::optional<std::string> customFrameFormat; + std::optional<String> customFrameFormat; /// Same as `customFrameFormat`, but for threads instead of stack frames. - std::optional<std::string> customThreadFormat; + std::optional<String> customThreadFormat; /// Specify a source path to remap "./" to allow full paths to be used when /// setting breakpoints in binaries that have relative source paths. - std::string sourcePath; + String sourcePath; /// Specify an array of path re-mappings. Each element in the array must be a /// two element array containing a source and destination pathname. Overrides /// sourcePath. - std::vector<std::pair<std::string, std::string>> sourceMap; + std::vector<std::pair<String, String>> sourceMap; /// LLDB commands executed upon debugger startup prior to creating the LLDB /// target. - std::vector<std::string> preInitCommands; + std::vector<String> preInitCommands; /// LLDB commands executed upon debugger startup prior to creating the LLDB /// target. - std::vector<std::string> initCommands; + std::vector<String> initCommands; /// LLDB commands executed just before launching/attaching, after the LLDB /// target has been created. - std::vector<std::string> preRunCommands; + std::vector<String> preRunCommands; /// LLDB commands executed just after launching/attaching, after the LLDB /// target has been created. - std::vector<std::string> postRunCommands; + std::vector<String> postRunCommands; /// LLDB commands executed just after each stop. - std::vector<std::string> stopCommands; + std::vector<String> stopCommands; /// LLDB commands executed when the program exits. - std::vector<std::string> exitCommands; + std::vector<String> exitCommands; /// LLDB commands executed when the debugging session ends. - std::vector<std::string> terminateCommands; + std::vector<String> terminateCommands; /// Path to the executable. /// /// *NOTE:* When launching, either `launchCommands` or `program` must be /// configured. If both are configured then `launchCommands` takes priority. - std::string program; + String program; /// Target triple for the program (arch-vendor-os). If not set, inferred from /// the binary. - std::string targetTriple; + String targetTriple; /// Specify name of the platform to use for this target, creating the platform /// if necessary. - std::string platformName; + String platformName; }; enum Console : unsigned { @@ -269,19 +269,19 @@ struct LaunchRequestArguments { /// *NOTE:* Either launchCommands or program must be configured. /// /// If set, takes priority over the 'program' when launching the target. - std::vector<std::string> launchCommands; + std::vector<String> launchCommands; /// The program working directory. - std::string cwd; + String cwd; /// An array of command line argument strings to be passed to the program /// being launched. - std::vector<std::string> args; + std::vector<String> args; /// Environment variables to set when launching the program. The format of /// each environment variable string is "VAR=VALUE" for environment variables /// with values or just "VAR" for environment variables with no values. - llvm::StringMap<std::string> env; + llvm::StringMap<String> env; /// If set, then the client stub should detach rather than killing the /// debuggee if it loses connection with lldb. @@ -302,7 +302,7 @@ struct LaunchRequestArguments { Console console = eConsoleInternal; /// An array of file paths for redirecting the program's standard IO streams. - std::vector<std::optional<std::string>> stdio; + std::vector<std::optional<String>> stdio; /// @} }; @@ -341,7 +341,7 @@ struct AttachRequestArguments { /// to a process by name. These commands may optionally create a new target /// and must perform an attach. A valid process must exist after these /// commands complete or the `"attach"` will fail. - std::vector<std::string> attachCommands; + std::vector<String> attachCommands; /// System process ID to attach to. lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; @@ -355,10 +355,10 @@ struct AttachRequestArguments { /// The hostname to connect to a remote system. The default hostname being /// used `localhost`. - std::string gdbRemoteHostname = "localhost"; + String gdbRemoteHostname = "localhost"; /// Path to the core file to debug. - std::string coreFile; + String coreFile; /// An existing session that consist of a target and debugger. std::optional<DAPSession> session; @@ -402,7 +402,7 @@ struct CompletionsArguments { /// One or more source lines. Typically this is the text users have typed into /// the debug console before they asked for completion. - std::string text; + String text; /// The position within `text` for which to determine the completion /// proposals. It is measured in UTF-16 code units and the client capability @@ -438,10 +438,10 @@ struct SetVariableArguments { var_ref_t variablesReference{var_ref_t::k_invalid_var_ref}; /// The name of the variable in the container. - std::string name; + String name; /// The value of the variable. - std::string value; + String value; /// Specifies details on how to format the response value. std::optional<ValueFormat> format; @@ -452,11 +452,11 @@ bool fromJSON(const llvm::json::Value &, SetVariableArguments &, /// Response to `setVariable` request. struct SetVariableResponseBody { /// The new value of the variable. - std::string value; + String value; /// The type of the new value. Typically shown in the UI when hovering over /// the value. - std::string type; + String type; /// If `variablesReference` is > 0, the new value is structured and its /// children can be retrieved by passing `variablesReference` to the @@ -527,10 +527,10 @@ bool fromJSON(const llvm::json::Value &, SourceArguments &, llvm::json::Path); /// Response to `source` request. struct SourceResponseBody { /// Content of the source reference. - std::string content; + String content; /// Content type (MIME type) of the source. - std::optional<std::string> mimeType; + std::optional<String> mimeType; }; llvm::json::Value toJSON(const SourceResponseBody &); @@ -732,7 +732,7 @@ struct DataBreakpointInfoArguments { /// The name of the variable's child to obtain data breakpoint information /// for. If `variablesReference` isn't specified, this can be an expression, /// or an address if `asAddress` is also true. - std::string name; + String name; /// When `name` is an expression, evaluate it in the scope of this stack /// frame. If not specified, the expression is evaluated in the global scope. @@ -755,7 +755,7 @@ struct DataBreakpointInfoArguments { /// The mode of the desired breakpoint. If defined, this must be one of the /// `breakpointModes` the debug adapter advertised in its `Capabilities`. - std::optional<std::string> mode; + std::optional<String> mode; }; bool fromJSON(const llvm::json::Value &, DataBreakpointInfoArguments &, llvm::json::Path); @@ -770,11 +770,11 @@ struct DataBreakpointInfoResponseBody { /// for details. Breakpoints set using the `dataId` in the /// `setDataBreakpoints` request may outlive the lifetime of the associated /// `dataId`. - std::optional<std::string> dataId; + std::optional<String> dataId; /// UI string that describes on what data the breakpoint is set on or why a /// data breakpoint is not available. - std::string description; + String description; /// Attribute lists the available access types for a potential data /// breakpoint. A UI client could surface this information. @@ -808,7 +808,7 @@ struct SetExceptionBreakpointsArguments { /// Set of exception filters specified by their ID. The set of all possible /// exception filters is defined by the `exceptionBreakpointFilters` /// capability. The `filter` and `filterOptions` sets are additive. - std::vector<std::string> filters; + std::vector<String> filters; /// Set of exception filters and their options. The set of all possible /// exception filters is defined by the `exceptionBreakpointFilters` @@ -1015,7 +1015,7 @@ struct WriteMemoryArguments { bool allowPartial = false; /// Bytes to write, encoded using base64. - std::string data; + String data; }; bool fromJSON(const llvm::json::Value &, WriteMemoryArguments &, llvm::json::Path); @@ -1030,10 +1030,10 @@ llvm::json::Value toJSON(const WriteMemoryResponseBody &); struct ModuleSymbolsArguments { /// The module UUID for which to retrieve symbols. - std::string moduleId; + String moduleId; /// The module path. - std::string moduleName; + String moduleName; /// The index of the first symbol to return; if omitted, start at the /// beginning. @@ -1061,10 +1061,10 @@ bool fromJSON(const llvm::json::Value &, ExceptionInfoArguments &, struct ExceptionInfoResponseBody { /// ID of the exception that was thrown. - std::string exceptionId; + String exceptionId; /// Descriptive text for the exception. - std::string description; + String description; /// Mode that caused the exception notification to be raised. ExceptionBreakMode breakMode = eExceptionBreakModeNever; @@ -1097,7 +1097,7 @@ enum EvaluateContext : unsigned { /// Arguments for `evaluate` request. struct EvaluateArguments { /// The expression to evaluate. - std::string expression; + String expression; /// Evaluate the expression in the scope of this stack frame. If not /// specified, the expression is evaluated in the global scope. @@ -1143,12 +1143,12 @@ bool fromJSON(const llvm::json::Value &, EvaluateArguments &, llvm::json::Path); /// Response to 'evaluate' request. struct EvaluateResponseBody { /// The result of the evaluate request. - std::string result; + String result; /// The type of the evaluate result. /// This attribute should only be returned by a debug adapter if the /// corresponding capability `supportsVariableType` is true. - std::string type; + String type; /// Properties of an evaluate result that can be used to determine how to /// render the result in the UI. @@ -1177,7 +1177,7 @@ struct EvaluateResponseBody { /// memory address contained in the pointer. /// This attribute may be returned by a debug adapter if corresponding /// capability `supportsMemoryReferences` is true. - std::string memoryReference; + String memoryReference; /// A reference that allows the client to request the location where the /// returned value is declared. For example, if a function pointer is @@ -1240,7 +1240,7 @@ llvm::json::Value toJSON(const LocationsResponseBody &); /// Arguments for `compileUnits` request. struct CompileUnitsArguments { /// The ID of the module. - std::string moduleId; + String moduleId; }; bool fromJSON(const llvm::json::Value &, CompileUnitsArguments &, llvm::json::Path); diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h index 2a23871621cca..a50f4c85d4fff 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -21,13 +21,13 @@ #define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H #include "Protocol/DAPTypes.h" +#include "Protocol/ProtocolBase.h" #include "lldb/lldb-defines.h" #include "lldb/lldb-types.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Support/JSON.h" #include <cstdint> #include <optional> -#include <string> #define LLDB_DAP_INVALID_SRC_REF 0 #define LLDB_DAP_INVALID_VALUE_LOC 0 @@ -40,14 +40,14 @@ namespace lldb_dap::protocol { struct ExceptionBreakpointsFilter { /// The internal ID of the filter option. This value is passed to the /// `setExceptionBreakpoints` request. - std::string filter; + String filter; /// The name of the filter option. This is shown in the UI. - std::string label; + 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::string description; + String description; /// Initial value of the filter option. If not specified a value false is /// assumed. @@ -59,7 +59,7 @@ struct ExceptionBreakpointsFilter { /// 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::string conditionDescription; + String conditionDescription; }; bool fromJSON(const llvm::json::Value &, ExceptionBreakpointsFilter &, llvm::json::Path); @@ -81,14 +81,14 @@ llvm::json::Value toJSON(const ColumnType &); /// customization. struct ColumnDescriptor { /// Name of the attribute rendered in this column. - std::string attributeName; + String attributeName; /// Header UI label of column. - std::string label; + String label; /// Format to use for the rendered values in this column. TBD how the format /// strings looks like. - std::optional<std::string> format; + std::optional<String> format; /// Datatype of values in this column. Defaults to `string` if not specified. /// Values: 'string', 'number', 'boolean', 'unixTimestampUTC'. @@ -143,19 +143,19 @@ llvm::json::Value toJSON(const CompletionItemType &); struct CompletionItem { /// The label of this completion item. By default this is also the text that /// is inserted when selecting this completion. - std::string label; + String label; /// If text is returned and not an empty string, then it is inserted instead /// of the label. - std::string text; + String text; /// A string that should be used when comparing this item with other items. If /// not returned or an empty string, the `label` is used instead. - std::string sortText; + String sortText; /// A human-readable string with additional information about this item, like /// type or symbol information. - std::string detail; + String detail; /// The item's type. Typically the client uses this information to render the /// item in the UI with an icon. @@ -211,14 +211,14 @@ llvm::json::Value toJSON(const BreakpointModeApplicability &); struct BreakpointMode { /// The internal ID of the mode. This value is passed to the `setBreakpoints` /// request. - std::string mode; + String mode; /// The name of the breakpoint mode. This is shown in the UI. - std::string label; + 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; + std::optional<String> description; /// Describes one or more type of breakpoint this mode applies to. std::vector<BreakpointModeApplicability> appliesTo; @@ -342,7 +342,7 @@ struct Capabilities { /// The set of characters that should trigger completion in a REPL. If not /// specified, the UI should assume the `.` character. - std::vector<std::string> completionTriggerCharacters; + std::vector<String> completionTriggerCharacters; /// The set of additional module information exposed by the debug adapter. std::vector<ColumnDescriptor> additionalModuleColumns; @@ -362,7 +362,7 @@ struct Capabilities { /// @{ /// The version of the adapter. - std::string lldbExtVersion; + String lldbExtVersion; /// @} }; @@ -374,16 +374,16 @@ llvm::json::Value toJSON(const Capabilities &); struct ExceptionFilterOptions { /// ID of an exception filter returned by the `exceptionBreakpointFilters` /// capability. - std::string filterId; + String filterId; /// An expression for conditional exceptions. /// The exception breaks into the debugger if the result of the condition is /// true. - std::string condition; + String condition; /// The mode of this exception breakpoint. If defined, this must be one of the /// `breakpointModes` the debug adapter advertised in its `Capabilities`. - std::string mode; + String mode; }; bool fromJSON(const llvm::json::Value &, ExceptionFilterOptions &, llvm::json::Path); @@ -402,12 +402,12 @@ struct Source { /// 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. - std::optional<std::string> name; + std::optional<String> name; /// The path of the source to be shown in the UI. It is only used to locate /// and load the content of the source if no `sourceReference` is specified /// (or its value is 0). - std::optional<std::string> path; + std::optional<String> path; /// If the value > 0 the contents of the source must be retrieved through the /// `source` request (even if a path is specified). Since a `sourceReference` @@ -445,7 +445,7 @@ struct Scope { /// Name of the scope such as 'Arguments', 'Locals', or 'Registers'. This /// string is shown in the UI as is and can be translated. //// - std::string name; + String name; /// A hint for how to present this scope in the UI. If this attribute is /// missing, the scope is shown with a generic UI. @@ -530,7 +530,7 @@ struct StepInTarget { lldb::addr_t id = LLDB_INVALID_ADDRESS; /// The name of the step-in target (shown in the UI). - std::string label; + String label; /// The line of the step-in target. uint32_t line = LLDB_INVALID_LINE_NUMBER; @@ -556,7 +556,7 @@ struct Thread { /// Unique identifier for the thread. lldb::tid_t id = LLDB_INVALID_THREAD_ID; /// The name of the thread. - std::string name; + String name; }; bool fromJSON(const llvm::json::Value &, Thread &, llvm::json::Path); llvm::json::Value toJSON(const Thread &); @@ -616,7 +616,7 @@ struct Breakpoint { /// A message about the state of the breakpoint. /// This is shown to the user and can be used to explain why a breakpoint /// could not be verified. - std::optional<std::string> message; + std::optional<String> message; /// The source where the breakpoint is located. std::optional<Source> source; @@ -639,7 +639,7 @@ struct Breakpoint { std::optional<uint32_t> endColumn; /// A memory reference to where the breakpoint is set. - std::optional<std::string> instructionReference; + std::optional<String> instructionReference; /// The offset from the instruction reference. /// This can be negative. @@ -667,7 +667,7 @@ struct SourceBreakpoint { /// The expression for conditional breakpoints. /// It is only honored by a debug adapter if the corresponding capability /// `supportsConditionalBreakpoints` is true. - std::optional<std::string> condition; + std::optional<String> condition; /// The expression that controls how many hits of the breakpoint are ignored. /// The debug adapter is expected to interpret the expression as needed. @@ -676,7 +676,7 @@ struct SourceBreakpoint { /// If both this property and `condition` are specified, `hitCondition` should /// be evaluated only if the `condition` is met, and the debug adapter should /// stop only if both conditions are met. - std::optional<std::string> hitCondition; + std::optional<String> hitCondition; /// If this attribute exists and is non-empty, the debug adapter must not /// 'break' (stop) @@ -685,11 +685,11 @@ struct SourceBreakpoint { /// capability `supportsLogPoints` is true. /// If either `hitCondition` or `condition` is specified, then the message /// should only be logged if those conditions are met. - std::optional<std::string> logMessage; + std::optional<String> logMessage; /// The mode of this breakpoint. If defined, this must be one of the /// `breakpointModes` the debug adapter advertised in its `Capabilities`. - std::optional<std::string> mode; + std::optional<String> mode; }; bool fromJSON(const llvm::json::Value &, SourceBreakpoint &, llvm::json::Path); llvm::json::Value toJSON(const SourceBreakpoint &); @@ -697,18 +697,18 @@ llvm::json::Value toJSON(const SourceBreakpoint &); /// Properties of a breakpoint passed to the `setFunctionBreakpoints` request. struct FunctionBreakpoint { /// The name of the function. - std::string name; + String name; /// An expression for conditional breakpoints. /// It is only honored by a debug adapter if the corresponding capability /// `supportsConditionalBreakpoints` is true. - std::optional<std::string> condition; + std::optional<String> condition; /// An expression that controls how many hits of the breakpoint are ignored. /// The debug adapter is expected to interpret the expression as needed. /// The attribute is only honored by a debug adapter if the corresponding /// capability `supportsHitConditionalBreakpoints` is true. - std::optional<std::string> hitCondition; + std::optional<String> hitCondition; }; bool fromJSON(const llvm::json::Value &, FunctionBreakpoint &, llvm::json::Path); @@ -729,17 +729,17 @@ llvm::json::Value toJSON(const DataBreakpointAccessType &); struct DataBreakpoint { /// An id representing the data. This id is returned from the /// `dataBreakpointInfo` request. - std::string dataId; + String dataId; /// The access type of the data. std::optional<DataBreakpointAccessType> accessType; /// An expression for conditional breakpoints. - std::optional<std::string> condition; + std::optional<String> condition; /// An expression that controls how many hits of the breakpoint are ignored. /// The debug adapter is expected to interpret the expression as needed. - std::optional<std::string> hitCondition; + std::optional<String> hitCondition; }; bool fromJSON(const llvm::json::Value &, DataBreakpoint &, llvm::json::Path); llvm::json::Value toJSON(const DataBreakpoint &); @@ -750,7 +750,7 @@ struct InstructionBreakpoint { /// This should be a memory or instruction pointer reference from an /// `EvaluateResponse`, `Variable`, `StackFrame`, `GotoTarget`, or /// `Breakpoint`. - std::string instructionReference; + String instructionReference; /// The offset from the instruction reference in bytes. /// This can be negative. @@ -759,17 +759,17 @@ struct InstructionBreakpoint { /// An expression for conditional breakpoints. /// It is only honored by a debug adapter if the corresponding capability /// `supportsConditionalBreakpoints` is true. - std::optional<std::string> condition; + std::optional<String> condition; /// An expression that controls how many hits of the breakpoint are ignored. /// The debug adapter is expected to interpret the expression as needed. /// The attribute is only honored by a debug adapter if the corresponding /// capability `supportsHitConditionalBreakpoints` is true. - std::optional<std::string> hitCondition; + std::optional<String> hitCondition; /// The mode of this breakpoint. If defined, this must be one of the /// `breakpointModes` the debug adapter advertised in its `Capabilities`. - std::optional<std::string> mode; + std::optional<String> mode; }; bool fromJSON(const llvm::json::Value &, InstructionBreakpoint &, llvm::json::Path); @@ -788,15 +788,15 @@ struct DisassembledInstruction { /// Raw bytes representing the instruction and its operands, in an /// implementation-defined format. - std::optional<std::string> instructionBytes; + std::optional<String> instructionBytes; /// Text representing the instruction and its operands, in an /// implementation-defined format. - std::string instruction; + String instruction; /// Name of the symbol that corresponds with the location of this instruction, /// if any. - std::optional<std::string> symbol; + std::optional<String> symbol; /// Source location that corresponds to this instruction, if any. /// Should always be set (if available) on the first instruction returned, @@ -834,15 +834,15 @@ llvm::json::Value toJSON(const DisassembledInstruction &); struct Module { /// Unique identifier for the module. - std::string id; + String id; /// A name of the module. - std::string name; + String name; /// Logical full path to the module. The exact definition is implementation /// defined, but usually this would be a full path to the on-disk file for the /// module. - std::string path; + String path; /// True if the module is optimized. bool isOptimized = false; @@ -852,21 +852,21 @@ struct Module { bool isUserCode = false; /// Version of Module. - std::string version; + String version; /// User-understandable description of if symbols were found for the module /// (ex: 'Symbols Loaded', 'Symbols not found', etc.) - std::string symbolStatus; + String symbolStatus; /// Logical full path to the symbol file. The exact definition is /// implementation defined. - std::string symbolFilePath; + String symbolFilePath; /// Module created or modified, encoded as an RFC 3339 timestamp. - std::string dateTimeStamp; + String dateTimeStamp; /// Address range covered by this module. - std::string addressRange; + String addressRange; /// Custom fields /// @{ @@ -883,15 +883,15 @@ llvm::json::Value toJSON(const Module &); struct VariablePresentationHint { /// The kind of variable. Before introducing additional values, try to use the /// listed values. - std::string kind; + String kind; /// Set of attributes represented as an array of strings. Before introducing /// additional values, try to use the listed values. - std::vector<std::string> attributes; + std::vector<String> attributes; /// Visibility of variable. Before introducing additional values, try to use /// the listed values. - std::string visibility; + String visibility; /// If true, clients can present the variable with a UI that supports a /// specific gesture to trigger its evaluation. @@ -929,7 +929,7 @@ bool fromJSON(const llvm::json::Value &, VariablePresentationHint &, /// and fetch them in chunks. struct Variable { /// The variable's name. - std::string name; + String name; /// The variable's value. /// @@ -941,14 +941,14 @@ struct Variable { /// its children are not yet visible. /// /// An empty string can be used if no value should be shown in the UI. - std::string value; + String value; /// The type of the variable's value. Typically shown in the UI when hovering /// over the value. /// /// This attribute should only be returned by a debug adapter if the /// corresponding capability `supportsVariableType` is true. - std::string type; + String type; /// Properties of a variable that can be used to determine how to render the /// variable in the UI. @@ -956,7 +956,7 @@ struct Variable { /// The evaluatable name of this variable which can be passed to the /// `evaluate` request to fetch the variable's value. - std::string evaluateName; + String evaluateName; /// If `variablesReference` is > 0, the variable is structured and its /// children can be retrieved by passing `variablesReference` to the @@ -1019,20 +1019,20 @@ llvm::json::Value toJSON(ExceptionBreakMode); struct ExceptionDetails { /// Message contained in the exception. - std::string message; + String message; /// Short type name of the exception object. - std::string typeName; + String typeName; /// Fully-qualified type name of the exception object. - std::string fullTypeName; + String fullTypeName; /// An expression that can be evaluated in the current scope to obtain the /// exception object. - std::string evaluateName; + String evaluateName; /// Stack trace at the time the exception was thrown. - std::string stackTrace; + String stackTrace; /// Details of the exception contained by this exception, if any. std::vector<ExceptionDetails> innerException; @@ -1041,7 +1041,7 @@ llvm::json::Value toJSON(const ExceptionDetails &); struct CompileUnit { /// Path of compile unit. - std::string compileUnitPath; + String compileUnitPath; }; llvm::json::Value toJSON(const CompileUnit &); @@ -1086,7 +1086,7 @@ struct StackFrame { lldb::tid_t id = LLDB_DAP_INVALID_STACK_FRAME_ID; /// The name of the stack frame, typically a method name. - std::string name; + String name; /// The source of the frame. std::optional<Source> source; @@ -1120,7 +1120,7 @@ struct StackFrame { lldb::addr_t instructionPointerReference = LLDB_INVALID_ADDRESS; /// The module associated with this frame, if any. - std::optional<std::string> moduleId; + std::optional<String> moduleId; /// A hint for how to present this frame in the UI. A value of `label` can be /// used to indicate that the frame is an artificial frame that is used as a diff --git a/lldb/tools/lldb-dap/ProtocolUtils.cpp b/lldb/tools/lldb-dap/ProtocolUtils.cpp index d18c040b119da..99bd5abf0b756 100644 --- a/lldb/tools/lldb-dap/ProtocolUtils.cpp +++ b/lldb/tools/lldb-dap/ProtocolUtils.cpp @@ -203,10 +203,9 @@ protocol::Thread CreateThread(lldb::SBThread &thread, lldb::SBFormat &format) { queue_kind_label = " (concurrent)"; name = llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(), - queue_name, queue_kind_label) - .str(); + queue_name, queue_kind_label); } else { - name = llvm::formatv("Thread {0}", thread.GetIndexID()).str(); + name = llvm::formatv("Thread {0}", thread.GetIndexID()); } } return protocol::Thread{thread.GetThreadID(), name}; diff --git a/lldb/tools/lldb-dap/tool/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp index af5c743a5edf1..afbc2c7f4c28c 100644 --- a/lldb/tools/lldb-dap/tool/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/tool/lldb-dap.cpp @@ -411,7 +411,7 @@ validateConnection(llvm::StringRef conn) { static llvm::Error serveConnection( const Socket::SocketProtocol &protocol, llvm::StringRef name, Log &log, const ReplMode default_repl_mode, - const std::vector<std::string> &pre_init_commands, bool no_lldbinit, + const std::vector<protocol::String> &pre_init_commands, bool no_lldbinit, std::optional<std::chrono::seconds> connection_timeout_seconds) { Status status; static std::unique_ptr<Socket> listener = Socket::Create(protocol, status); @@ -705,7 +705,7 @@ int main(int argc, char *argv[]) { lldb::SBDebugger::Terminate(); }); - std::vector<std::string> pre_init_commands; + std::vector<protocol::String> pre_init_commands; for (const std::string &arg : input_args.getAllArgValues(OPT_pre_init_command)) { pre_init_commands.push_back(arg); diff --git a/lldb/unittests/DAP/CMakeLists.txt b/lldb/unittests/DAP/CMakeLists.txt index 97f9cad7477ed..53e0fde5687ec 100644 --- a/lldb/unittests/DAP/CMakeLists.txt +++ b/lldb/unittests/DAP/CMakeLists.txt @@ -10,6 +10,7 @@ add_lldb_unittest(DAPTests Handler/ContinueTest.cpp JSONUtilsTest.cpp LLDBUtilsTest.cpp + ProtocolBaseTest.cpp ProtocolEventsTest.cpp ProtocolRequestsTest.cpp ProtocolTypesTest.cpp diff --git a/lldb/unittests/DAP/ProtocolBaseTest.cpp b/lldb/unittests/DAP/ProtocolBaseTest.cpp new file mode 100644 index 0000000000000..b22ef916bc00a --- /dev/null +++ b/lldb/unittests/DAP/ProtocolBaseTest.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// 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 "Protocol/ProtocolBase.h" +#include "TestingSupport/TestUtilities.h" +#include "llvm/Testing/Support/Error.h" +#include <gtest/gtest.h> + +using namespace llvm; +using namespace lldb_dap::protocol; +using lldb_private::PrettyPrint; +using llvm::json::parse; +using llvm::json::Value; + +TEST(ProtocolBaseTest, SanitizedString) { + for (auto [input, json] : std::vector<std::pair<const char *, const char *>>{ + {"valid str", R"("valid str")"}, + {"lone trailing \x81\x82 bytes", R"("lone trailing οΏ½οΏ½ bytes")"}}) { + String str = input; + Expected<Value> expected_str = parse(json); + ASSERT_THAT_EXPECTED(expected_str, llvm::Succeeded()); + EXPECT_EQ(PrettyPrint(*expected_str), PrettyPrint(str)); + } +} diff --git a/lldb/unittests/DAP/ProtocolTypesTest.cpp b/lldb/unittests/DAP/ProtocolTypesTest.cpp index 863fd3c4fe3a3..56ef6cdbb813f 100644 --- a/lldb/unittests/DAP/ProtocolTypesTest.cpp +++ b/lldb/unittests/DAP/ProtocolTypesTest.cpp @@ -707,7 +707,7 @@ TEST(ProtocolTypesTest, SetExceptionBreakpointsArguments) { /*filterOptions=*/testing::IsEmpty()))); EXPECT_THAT_EXPECTED( parse<SetExceptionBreakpointsArguments>(R"({"filters":["abc"]})"), - HasValue(testing::FieldsAre(/*filters=*/std::vector<std::string>{"abc"}, + HasValue(testing::FieldsAre(/*filters=*/std::vector<String>{"abc"}, /*filterOptions=*/testing::IsEmpty()))); EXPECT_THAT_EXPECTED( parse<SetExceptionBreakpointsArguments>( diff --git a/lldb/unittests/DAP/TestBase.cpp b/lldb/unittests/DAP/TestBase.cpp index 1afac18833a03..39634593779ad 100644 --- a/lldb/unittests/DAP/TestBase.cpp +++ b/lldb/unittests/DAP/TestBase.cpp @@ -41,7 +41,7 @@ void TransportBase::SetUp() { dap = std::make_unique<DAP>( /*log=*/*log, /*default_repl_mode=*/ReplMode::Auto, - /*pre_init_commands=*/std::vector<std::string>(), + /*pre_init_commands=*/std::vector<String>(), /*no_lldbinit=*/false, /*client_name=*/"test_client", /*transport=*/*to_client, /*loop=*/loop); diff --git a/lldb/unittests/DAP/VariablesTest.cpp b/lldb/unittests/DAP/VariablesTest.cpp index 10dd567a4ecca..8a1c9ce2c502f 100644 --- a/lldb/unittests/DAP/VariablesTest.cpp +++ b/lldb/unittests/DAP/VariablesTest.cpp @@ -29,7 +29,8 @@ class VariablesTest : public ::testing::Test { VariableReferenceStorage vars; static const protocol::Scope * - FindScope(const std::vector<protocol::Scope> &scopes, llvm::StringRef name) { + FindScope(const std::vector<protocol::Scope> &scopes, + const protocol::String &name) { for (const auto &scope : scopes) { if (scope.name == name) return &scope; _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
