1 | //===-- JSONUtils.h ---------------------------------------------*- C++ -*-===// |
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 | #ifndef LLDB_TOOLS_LLDB_DAP_JSONUTILS_H |
10 | #define LLDB_TOOLS_LLDB_DAP_JSONUTILS_H |
11 | |
12 | #include "DAPForward.h" |
13 | #include "Protocol/ProtocolTypes.h" |
14 | #include "lldb/API/SBCompileUnit.h" |
15 | #include "lldb/API/SBFormat.h" |
16 | #include "lldb/API/SBType.h" |
17 | #include "lldb/API/SBValue.h" |
18 | #include "lldb/lldb-types.h" |
19 | #include "llvm/ADT/StringMap.h" |
20 | #include "llvm/ADT/StringRef.h" |
21 | #include "llvm/Support/JSON.h" |
22 | #include <cstdint> |
23 | #include <optional> |
24 | #include <string> |
25 | #include <unordered_map> |
26 | #include <utility> |
27 | #include <vector> |
28 | |
29 | namespace lldb_dap { |
30 | |
31 | /// Emplace a StringRef in a json::Object after enusring that the |
32 | /// string is valid UTF8. If not, first call llvm::json::fixUTF8 |
33 | /// before emplacing. |
34 | /// |
35 | /// \param[in] obj |
36 | /// A JSON object that we will attempt to emplace the value in |
37 | /// |
38 | /// \param[in] key |
39 | /// The key to use when emplacing the value |
40 | /// |
41 | /// \param[in] str |
42 | /// The string to emplace |
43 | void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key, |
44 | llvm::StringRef str); |
45 | |
46 | /// Extract simple values as a string. |
47 | /// |
48 | /// \param[in] value |
49 | /// A JSON value to extract the string from. |
50 | /// |
51 | /// \return |
52 | /// A llvm::StringRef that contains the string value, or an empty |
53 | /// string if \a value isn't a string. |
54 | llvm::StringRef GetAsString(const llvm::json::Value &value); |
55 | |
56 | /// Extract the string value for the specified key from the |
57 | /// specified object. |
58 | /// |
59 | /// \param[in] obj |
60 | /// A JSON object that we will attempt to extract the value from |
61 | /// |
62 | /// \param[in] key |
63 | /// The key to use when extracting the value |
64 | /// |
65 | /// \return |
66 | /// A llvm::StringRef that contains the string value for the |
67 | /// specified \a key, or \a std::nullopt if there is no key that |
68 | /// matches or if the value is not a string. |
69 | std::optional<llvm::StringRef> GetString(const llvm::json::Object &obj, |
70 | llvm::StringRef key); |
71 | std::optional<llvm::StringRef> GetString(const llvm::json::Object *obj, |
72 | llvm::StringRef key); |
73 | |
74 | /// Extract the integer value for the specified key from the specified object |
75 | /// and return it as the specified integer type T. |
76 | /// |
77 | /// \param[in] obj |
78 | /// A JSON object that we will attempt to extract the value from |
79 | /// |
80 | /// \param[in] key |
81 | /// The key to use when extracting the value |
82 | /// |
83 | /// \return |
84 | /// The integer value for the specified \a key, or std::nullopt if there is |
85 | /// no key that matches or if the value is not an integer. |
86 | /// @{ |
87 | template <typename T> |
88 | std::optional<T> GetInteger(const llvm::json::Object &obj, |
89 | llvm::StringRef key) { |
90 | return obj.getInteger(K: key); |
91 | } |
92 | |
93 | template <typename T> |
94 | std::optional<T> GetInteger(const llvm::json::Object *obj, |
95 | llvm::StringRef key) { |
96 | if (obj != nullptr) |
97 | return GetInteger<T>(*obj, key); |
98 | return std::nullopt; |
99 | } |
100 | /// @} |
101 | |
102 | /// Extract the boolean value for the specified key from the |
103 | /// specified object. |
104 | /// |
105 | /// \param[in] obj |
106 | /// A JSON object that we will attempt to extract the value from |
107 | /// |
108 | /// \param[in] key |
109 | /// The key to use when extracting the value |
110 | /// |
111 | /// \return |
112 | /// The boolean value for the specified \a key, or std::nullopt |
113 | /// if there is no key that matches or if the value is not a |
114 | /// boolean value of an integer. |
115 | /// @{ |
116 | std::optional<bool> GetBoolean(const llvm::json::Object &obj, |
117 | llvm::StringRef key); |
118 | std::optional<bool> GetBoolean(const llvm::json::Object *obj, |
119 | llvm::StringRef key); |
120 | /// @} |
121 | |
122 | /// Check if the specified key exists in the specified object. |
123 | /// |
124 | /// \param[in] obj |
125 | /// A JSON object that we will attempt to extract the value from |
126 | /// |
127 | /// \param[in] key |
128 | /// The key to check for |
129 | /// |
130 | /// \return |
131 | /// \b True if the key exists in the \a obj, \b False otherwise. |
132 | bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key); |
133 | |
134 | /// Encodes a memory reference |
135 | std::string EncodeMemoryReference(lldb::addr_t addr); |
136 | |
137 | /// Decodes a memory reference |
138 | std::optional<lldb::addr_t> |
139 | DecodeMemoryReference(llvm::StringRef memoryReference); |
140 | |
141 | /// Extract an array of strings for the specified key from an object. |
142 | /// |
143 | /// String values in the array will be extracted without any quotes |
144 | /// around them. Numbers and Booleans will be converted into |
145 | /// strings. Any NULL, array or objects values in the array will be |
146 | /// ignored. |
147 | /// |
148 | /// \param[in] obj |
149 | /// A JSON object that we will attempt to extract the array from |
150 | /// |
151 | /// \param[in] key |
152 | /// The key to use when extracting the value |
153 | /// |
154 | /// \return |
155 | /// An array of string values for the specified \a key, or |
156 | /// \a fail_value if there is no key that matches or if the |
157 | /// value is not an array or all items in the array are not |
158 | /// strings, numbers or booleans. |
159 | std::vector<std::string> GetStrings(const llvm::json::Object *obj, |
160 | llvm::StringRef key); |
161 | |
162 | /// Extract an object of key value strings for the specified key from an object. |
163 | /// |
164 | /// String values in the object will be extracted without any quotes |
165 | /// around them. Numbers and Booleans will be converted into |
166 | /// strings. Any NULL, array or objects values in the array will be |
167 | /// ignored. |
168 | /// |
169 | /// \param[in] obj |
170 | /// A JSON object that we will attempt to extract the array from |
171 | /// |
172 | /// \param[in] key |
173 | /// The key to use when extracting the value |
174 | /// |
175 | /// \return |
176 | /// An object of key value strings for the specified \a key, or |
177 | /// \a fail_value if there is no key that matches or if the |
178 | /// value is not an object or key and values in the object are not |
179 | /// strings, numbers or booleans. |
180 | std::unordered_map<std::string, std::string> |
181 | GetStringMap(const llvm::json::Object &obj, llvm::StringRef key); |
182 | |
183 | /// Fill a response object given the request object. |
184 | /// |
185 | /// The \a response object will get its "type" set to "response", |
186 | /// the "seq" set to zero, "response_seq" set to the "seq" value from |
187 | /// \a request, "command" set to the "command" from \a request, |
188 | /// and "success" set to true. |
189 | /// |
190 | /// \param[in] request |
191 | /// The request object received from a call to DAP::ReadJSON(). |
192 | /// |
193 | /// \param[in,out] response |
194 | /// An empty llvm::json::Object object that will be filled |
195 | /// in as noted in description. |
196 | void FillResponse(const llvm::json::Object &request, |
197 | llvm::json::Object &response); |
198 | |
199 | /// Converts a LLDB module to a VS Code DAP module for use in "modules" events. |
200 | /// |
201 | /// \param[in] target |
202 | /// A LLDB target object to convert into a JSON value. |
203 | /// |
204 | /// \param[in] module |
205 | /// A LLDB module object to convert into a JSON value |
206 | /// |
207 | /// \param[in] id_only |
208 | /// Only include the module ID in the JSON value. This is used when sending |
209 | /// a "removed" module event. |
210 | /// |
211 | /// \return |
212 | /// A "Module" JSON object with that follows the formal JSON |
213 | /// definition outlined by Microsoft. |
214 | llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module, |
215 | bool id_only = false); |
216 | |
217 | /// Create a "Event" JSON object using \a event_name as the event name |
218 | /// |
219 | /// \param[in] event_name |
220 | /// The string value to use for the "event" key in the JSON object. |
221 | /// |
222 | /// \return |
223 | /// A "Event" JSON object with that follows the formal JSON |
224 | /// definition outlined by Microsoft. |
225 | llvm::json::Object CreateEventObject(const llvm::StringRef event_name); |
226 | |
227 | /// Create a "ExceptionBreakpointsFilter" JSON object as described in |
228 | /// the debug adapter definition. |
229 | /// |
230 | /// \param[in] bp |
231 | /// The exception breakpoint object to use |
232 | /// |
233 | /// \return |
234 | /// A "ExceptionBreakpointsFilter" JSON object with that follows |
235 | /// the formal JSON definition outlined by Microsoft. |
236 | protocol::ExceptionBreakpointsFilter |
237 | CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp); |
238 | |
239 | /// Create a "StackFrame" object for a LLDB frame object. |
240 | /// |
241 | /// This function will fill in the following keys in the returned |
242 | /// object: |
243 | /// "id" - the stack frame ID as an integer |
244 | /// "name" - the function name as a string |
245 | /// "source" - source file information as a "Source" DAP object |
246 | /// "line" - the source file line number as an integer |
247 | /// "column" - the source file column number as an integer |
248 | /// |
249 | /// \param[in] frame |
250 | /// The LLDB stack frame to use when populating out the "StackFrame" |
251 | /// object. |
252 | /// |
253 | /// \param[in] format |
254 | /// The LLDB format to use when populating out the "StackFrame" |
255 | /// object. |
256 | /// |
257 | /// \return |
258 | /// A "StackFrame" JSON object with that follows the formal JSON |
259 | /// definition outlined by Microsoft. |
260 | llvm::json::Value CreateStackFrame(lldb::SBFrame &frame, |
261 | lldb::SBFormat &format); |
262 | |
263 | /// Create a "StackFrame" label object for a LLDB thread. |
264 | /// |
265 | /// This function will fill in the following keys in the returned |
266 | /// object: |
267 | /// "id" - the thread ID as an integer |
268 | /// "name" - the thread name as a string which combines the LLDB |
269 | /// thread index ID along with the string name of the thread |
270 | /// from the OS if it has a name. |
271 | /// "presentationHint" - "label" |
272 | /// |
273 | /// \param[in] thread |
274 | /// The LLDB thread to use when populating out the "Thread" |
275 | /// object. |
276 | /// |
277 | /// \param[in] format |
278 | /// The configured formatter for the DAP session. |
279 | /// |
280 | /// \return |
281 | /// A "StackFrame" JSON object with that follows the formal JSON |
282 | /// definition outlined by Microsoft. |
283 | llvm::json::Value CreateExtendedStackFrameLabel(lldb::SBThread &thread, |
284 | lldb::SBFormat &format); |
285 | |
286 | /// Create a "StoppedEvent" object for a LLDB thread object. |
287 | /// |
288 | /// This function will fill in the following keys in the returned |
289 | /// object's "body" object: |
290 | /// "reason" - With a valid stop reason enumeration string value |
291 | /// that Microsoft specifies |
292 | /// "threadId" - The thread ID as an integer |
293 | /// "description" - a stop description (like "breakpoint 12.3") as a |
294 | /// string |
295 | /// "preserveFocusHint" - a boolean value that states if this thread |
296 | /// should keep the focus in the GUI. |
297 | /// "allThreadsStopped" - set to True to indicate that all threads |
298 | /// stop when any thread stops. |
299 | /// |
300 | /// \param[in] dap |
301 | /// The DAP session associated with the stopped thread. |
302 | /// |
303 | /// \param[in] thread |
304 | /// The LLDB thread to use when populating out the "StoppedEvent" |
305 | /// object. |
306 | /// |
307 | /// \param[in] stop_id |
308 | /// The stop id for this event. |
309 | /// |
310 | /// \return |
311 | /// A "StoppedEvent" JSON object with that follows the formal JSON |
312 | /// definition outlined by Microsoft. |
313 | llvm::json::Value CreateThreadStopped(DAP &dap, lldb::SBThread &thread, |
314 | uint32_t stop_id); |
315 | |
316 | /// \return |
317 | /// The variable name of \a value or a default placeholder. |
318 | const char *GetNonNullVariableName(lldb::SBValue &value); |
319 | |
320 | /// VSCode can't display two variables with the same name, so we need to |
321 | /// distinguish them by using a suffix. |
322 | /// |
323 | /// If the source and line information is present, we use it as the suffix. |
324 | /// Otherwise, we fallback to the variable address or register location. |
325 | std::string CreateUniqueVariableNameForDisplay(lldb::SBValue &v, |
326 | bool is_name_duplicated); |
327 | |
328 | /// Helper struct that parses the metadata of an \a lldb::SBValue and produces |
329 | /// a canonical set of properties that can be sent to DAP clients. |
330 | struct VariableDescription { |
331 | // The error message if SBValue.GetValue() fails. |
332 | std::optional<std::string> error; |
333 | // The display description to show on the IDE. |
334 | std::string display_value; |
335 | // The display name to show on the IDE. |
336 | std::string name; |
337 | // The variable path for this variable. |
338 | std::string evaluate_name; |
339 | // The output of SBValue.GetValue() if it doesn't fail. It might be empty. |
340 | std::string value; |
341 | // The summary string of this variable. It might be empty. |
342 | std::string summary; |
343 | // The auto summary if using `enableAutoVariableSummaries`. |
344 | std::optional<std::string> auto_summary; |
345 | // The type of this variable. |
346 | lldb::SBType type_obj; |
347 | // The display type name of this variable. |
348 | std::string display_type_name; |
349 | /// The SBValue for this variable. |
350 | lldb::SBValue v; |
351 | |
352 | VariableDescription(lldb::SBValue v, bool auto_variable_summaries, |
353 | bool format_hex = false, bool is_name_duplicated = false, |
354 | std::optional<std::string> custom_name = {}); |
355 | |
356 | /// Create a JSON object that represents these extensions to the DAP variable |
357 | /// response. |
358 | llvm::json::Object GetVariableExtensionsJSON(); |
359 | |
360 | /// Returns a description of the value appropriate for the specified context. |
361 | std::string GetResult(llvm::StringRef context); |
362 | }; |
363 | |
364 | /// Does the given variable have an associated value location? |
365 | bool ValuePointsToCode(lldb::SBValue v); |
366 | |
367 | /// Pack a location into a single integer which we can send via |
368 | /// the debug adapter protocol. |
369 | int64_t PackLocation(int64_t var_ref, bool is_value_location); |
370 | |
371 | /// Reverse of `PackLocation` |
372 | std::pair<int64_t, bool> UnpackLocation(int64_t location_id); |
373 | |
374 | /// Create a "Variable" object for a LLDB thread object. |
375 | /// |
376 | /// This function will fill in the following keys in the returned |
377 | /// object: |
378 | /// "name" - the name of the variable |
379 | /// "value" - the value of the variable as a string |
380 | /// "type" - the typename of the variable as a string |
381 | /// "id" - a unique identifier for a value in case there are multiple |
382 | /// variables with the same name. Other parts of the DAP |
383 | /// protocol refer to values by name so this can help |
384 | /// disambiguate such cases if a IDE passes this "id" value |
385 | /// back down. |
386 | /// "variablesReference" - Zero if the variable has no children, |
387 | /// non-zero integer otherwise which can be used to expand |
388 | /// the variable. |
389 | /// "evaluateName" - The name of the variable to use in expressions |
390 | /// as a string. |
391 | /// |
392 | /// \param[in] v |
393 | /// The LLDB value to use when populating out the "Variable" |
394 | /// object. |
395 | /// |
396 | /// \param[in] var_ref |
397 | /// The variable reference. Used to identify the value, e.g. |
398 | /// in the `variablesReference` or `declarationLocationReference` |
399 | /// properties. |
400 | /// |
401 | /// \param[in] format_hex |
402 | /// If set to true the variable will be formatted as hex in |
403 | /// the "value" key value pair for the value of the variable. |
404 | /// |
405 | /// \param[in] auto_variable_summaries |
406 | /// IF set to true the variable will create an automatic variable summary. |
407 | /// |
408 | /// \param[in] is_name_duplicated |
409 | /// Whether the same variable name appears multiple times within the same |
410 | /// context (e.g. locals). This can happen due to shadowed variables in |
411 | /// nested blocks. |
412 | /// |
413 | /// As VSCode doesn't render two of more variables with the same name, we |
414 | /// apply a suffix to distinguish duplicated variables. |
415 | /// |
416 | /// \param[in] custom_name |
417 | /// A provided custom name that is used instead of the SBValue's when |
418 | /// creating the JSON representation. |
419 | /// |
420 | /// \return |
421 | /// A "Variable" JSON object with that follows the formal JSON |
422 | /// definition outlined by Microsoft. |
423 | llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref, |
424 | bool format_hex, bool auto_variable_summaries, |
425 | bool synthetic_child_debugging, |
426 | bool is_name_duplicated = false, |
427 | std::optional<std::string> custom_name = {}); |
428 | |
429 | llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit); |
430 | |
431 | /// Create a runInTerminal reverse request object |
432 | /// |
433 | /// \param[in] program |
434 | /// Path to the program to run in the terminal. |
435 | /// |
436 | /// \param[in] args |
437 | /// The arguments for the program. |
438 | /// |
439 | /// \param[in] env |
440 | /// The environment variables to set in the terminal. |
441 | /// |
442 | /// \param[in] cwd |
443 | /// The working directory for the run in terminal request. |
444 | /// |
445 | /// \param[in] comm_file |
446 | /// The fifo file used to communicate the with the target launcher. |
447 | /// |
448 | /// \param[in] debugger_pid |
449 | /// The PID of the lldb-dap instance that will attach to the target. The |
450 | /// launcher uses it on Linux tell the kernel that it should allow the |
451 | /// debugger process to attach. |
452 | /// |
453 | /// \return |
454 | /// A "runInTerminal" JSON object that follows the specification outlined by |
455 | /// Microsoft. |
456 | llvm::json::Object CreateRunInTerminalReverseRequest( |
457 | llvm::StringRef program, const std::vector<std::string> &args, |
458 | const llvm::StringMap<std::string> &env, llvm::StringRef cwd, |
459 | llvm::StringRef comm_file, lldb::pid_t debugger_pid); |
460 | |
461 | /// Create a "Terminated" JSON object that contains statistics |
462 | /// |
463 | /// \return |
464 | /// A body JSON object with debug info and breakpoint info |
465 | llvm::json::Object CreateTerminatedEventObject(lldb::SBTarget &target); |
466 | |
467 | /// Convert a given JSON object to a string. |
468 | std::string JSONToString(const llvm::json::Value &json); |
469 | |
470 | } // namespace lldb_dap |
471 | |
472 | #endif |
473 | |