1 | //===-- CommandPlugins.cpp ------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "CommandPlugins.h" |
10 | #include "Handler/ResponseHandler.h" |
11 | #include "JSONUtils.h" |
12 | #include "lldb/API/SBStream.h" |
13 | |
14 | using namespace lldb_dap; |
15 | |
16 | bool StartDebuggingCommand::DoExecute(lldb::SBDebugger debugger, char **command, |
17 | lldb::SBCommandReturnObject &result) { |
18 | // Command format like: `start-debugging <launch|attach> <configuration>` |
19 | if (!command) { |
20 | result.SetError("Invalid use of start-debugging, expected format " |
21 | "`start-debugging <launch|attach> <configuration>`." ); |
22 | return false; |
23 | } |
24 | |
25 | if (!command[0] || llvm::StringRef(command[0]).empty()) { |
26 | result.SetError("start-debugging request type missing." ); |
27 | return false; |
28 | } |
29 | |
30 | if (!command[1] || llvm::StringRef(command[1]).empty()) { |
31 | result.SetError("start-debugging debug configuration missing." ); |
32 | return false; |
33 | } |
34 | |
35 | llvm::StringRef request{command[0]}; |
36 | std::string raw_configuration{command[1]}; |
37 | |
38 | llvm::Expected<llvm::json::Value> configuration = |
39 | llvm::json::parse(JSON: raw_configuration); |
40 | |
41 | if (!configuration) { |
42 | llvm::Error err = configuration.takeError(); |
43 | std::string msg = "Failed to parse json configuration: " + |
44 | llvm::toString(E: std::move(err)) + "\n\n" + |
45 | raw_configuration; |
46 | result.SetError(msg.c_str()); |
47 | return false; |
48 | } |
49 | |
50 | dap.SendReverseRequest<LogFailureResponseHandler>( |
51 | command: "startDebugging" , |
52 | arguments: llvm::json::Object{{.K: "request" , .V: request}, |
53 | {.K: "configuration" , .V: std::move(*configuration)}}); |
54 | |
55 | result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); |
56 | |
57 | return true; |
58 | } |
59 | |
60 | bool ReplModeCommand::DoExecute(lldb::SBDebugger debugger, char **command, |
61 | lldb::SBCommandReturnObject &result) { |
62 | // Command format like: `repl-mode <variable|command|auto>?` |
63 | // If a new mode is not specified report the current mode. |
64 | if (!command || llvm::StringRef(command[0]).empty()) { |
65 | std::string mode; |
66 | switch (dap.repl_mode) { |
67 | case ReplMode::Variable: |
68 | mode = "variable" ; |
69 | break; |
70 | case ReplMode::Command: |
71 | mode = "command" ; |
72 | break; |
73 | case ReplMode::Auto: |
74 | mode = "auto" ; |
75 | break; |
76 | } |
77 | |
78 | result.Printf(format: "lldb-dap repl-mode %s.\n" , mode.c_str()); |
79 | result.SetStatus(lldb::eReturnStatusSuccessFinishResult); |
80 | |
81 | return true; |
82 | } |
83 | |
84 | llvm::StringRef new_mode{command[0]}; |
85 | |
86 | if (new_mode == "variable" ) { |
87 | dap.repl_mode = ReplMode::Variable; |
88 | } else if (new_mode == "command" ) { |
89 | dap.repl_mode = ReplMode::Command; |
90 | } else if (new_mode == "auto" ) { |
91 | dap.repl_mode = ReplMode::Auto; |
92 | } else { |
93 | lldb::SBStream error_message; |
94 | error_message.Printf(format: "Invalid repl-mode '%s'. Expected one of 'variable', " |
95 | "'command' or 'auto'.\n" , |
96 | new_mode.data()); |
97 | result.SetError(error_message.GetData()); |
98 | return false; |
99 | } |
100 | |
101 | result.Printf(format: "lldb-dap repl-mode %s set.\n" , new_mode.data()); |
102 | result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); |
103 | return true; |
104 | } |
105 | |
106 | /// Sends a DAP event with an optional body. |
107 | /// |
108 | /// https://code.visualstudio.com/api/references/vscode-api#debug.onDidReceiveDebugSessionCustomEvent |
109 | bool SendEventCommand::DoExecute(lldb::SBDebugger debugger, char **command, |
110 | lldb::SBCommandReturnObject &result) { |
111 | // Command format like: `send-event <name> <body>?` |
112 | if (!command || !command[0] || llvm::StringRef(command[0]).empty()) { |
113 | result.SetError("Not enough arguments found, expected format " |
114 | "`lldb-dap send-event <name> <body>?`." ); |
115 | return false; |
116 | } |
117 | |
118 | llvm::StringRef name{command[0]}; |
119 | // Events that are stateful and should be handled by lldb-dap internally. |
120 | const std::array internal_events{"breakpoint" , "capabilities" , "continued" , |
121 | "exited" , "initialize" , "loadedSource" , |
122 | "module" , "process" , "stopped" , |
123 | "terminated" , "thread" }; |
124 | if (llvm::is_contained(Range: internal_events, Element: name)) { |
125 | std::string msg = |
126 | llvm::formatv(Fmt: "Invalid use of lldb-dap send-event, event \"{0}\" " |
127 | "should be handled by lldb-dap internally." , |
128 | Vals&: name) |
129 | .str(); |
130 | result.SetError(msg.c_str()); |
131 | return false; |
132 | } |
133 | |
134 | llvm::json::Object event(CreateEventObject(event_name: name)); |
135 | |
136 | if (command[1] && !llvm::StringRef(command[1]).empty()) { |
137 | // See if we have unused arguments. |
138 | if (command[2]) { |
139 | result.SetError( |
140 | "Additional arguments found, expected `lldb-dap send-event " |
141 | "<name> <body>?`." ); |
142 | return false; |
143 | } |
144 | |
145 | llvm::StringRef raw_body{command[1]}; |
146 | |
147 | llvm::Expected<llvm::json::Value> body = llvm::json::parse(JSON: raw_body); |
148 | |
149 | if (!body) { |
150 | llvm::Error err = body.takeError(); |
151 | std::string msg = "Failed to parse custom event body: " + |
152 | llvm::toString(E: std::move(err)); |
153 | result.SetError(msg.c_str()); |
154 | return false; |
155 | } |
156 | |
157 | event.try_emplace(K: "body" , Args: std::move(*body)); |
158 | } |
159 | |
160 | dap.SendJSON(json: llvm::json::Value(std::move(event))); |
161 | result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); |
162 | return true; |
163 | } |
164 | |