1 //===-- JSONUtils.cpp -------------------------------------------*- 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 #include <algorithm>
10 
11 #include "llvm/ADT/Optional.h"
12 
13 #include "llvm/Support/FormatAdapters.h"
14 #include "llvm/Support/Path.h"
15 
16 #include "lldb/API/SBBreakpoint.h"
17 #include "lldb/API/SBBreakpointLocation.h"
18 #include "lldb/API/SBValue.h"
19 #include "lldb/Host/PosixApi.h"
20 
21 #include "ExceptionBreakpoint.h"
22 #include "JSONUtils.h"
23 #include "LLDBUtils.h"
24 #include "VSCode.h"
25 
26 namespace lldb_vscode {
27 
28 void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
29                        llvm::StringRef str) {
30   if (LLVM_LIKELY(llvm::json::isUTF8(str)))
31     obj.try_emplace(key, str.str());
32   else
33     obj.try_emplace(key, llvm::json::fixUTF8(str));
34 }
35 
36 llvm::StringRef GetAsString(const llvm::json::Value &value) {
37   if (auto s = value.getAsString())
38     return *s;
39   return llvm::StringRef();
40 }
41 
42 // Gets a string from a JSON object using the key, or returns an empty string.
43 llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key) {
44   if (auto value = obj.getString(key))
45     return GetAsString(*value);
46   return llvm::StringRef();
47 }
48 
49 llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) {
50   if (obj == nullptr)
51     return llvm::StringRef();
52   return GetString(*obj, key);
53 }
54 
55 // Gets an unsigned integer from a JSON object using the key, or returns the
56 // specified fail value.
57 uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key,
58                      uint64_t fail_value) {
59   if (auto value = obj.getInteger(key))
60     return (uint64_t)*value;
61   return fail_value;
62 }
63 
64 uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key,
65                      uint64_t fail_value) {
66   if (obj == nullptr)
67     return fail_value;
68   return GetUnsigned(*obj, key, fail_value);
69 }
70 
71 bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key,
72                 bool fail_value) {
73   if (auto value = obj.getBoolean(key))
74     return *value;
75   if (auto value = obj.getInteger(key))
76     return *value != 0;
77   return fail_value;
78 }
79 
80 bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key,
81                 bool fail_value) {
82   if (obj == nullptr)
83     return fail_value;
84   return GetBoolean(*obj, key, fail_value);
85 }
86 
87 int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key,
88                   int64_t fail_value) {
89   if (auto value = obj.getInteger(key))
90     return *value;
91   return fail_value;
92 }
93 
94 int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key,
95                   int64_t fail_value) {
96   if (obj == nullptr)
97     return fail_value;
98   return GetSigned(*obj, key, fail_value);
99 }
100 
101 bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) {
102   return obj.find(key) != obj.end();
103 }
104 
105 std::vector<std::string> GetStrings(const llvm::json::Object *obj,
106                                     llvm::StringRef key) {
107   std::vector<std::string> strs;
108   auto json_array = obj->getArray(key);
109   if (!json_array)
110     return strs;
111   for (const auto &value : *json_array) {
112     switch (value.kind()) {
113     case llvm::json::Value::String:
114       strs.push_back(value.getAsString()->str());
115       break;
116     case llvm::json::Value::Number:
117     case llvm::json::Value::Boolean: {
118       std::string s;
119       llvm::raw_string_ostream strm(s);
120       strm << value;
121       strs.push_back(strm.str());
122       break;
123     }
124     case llvm::json::Value::Null:
125     case llvm::json::Value::Object:
126     case llvm::json::Value::Array:
127       break;
128     }
129   }
130   return strs;
131 }
132 
133 void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object,
134                     llvm::StringRef key) {
135 
136   llvm::StringRef value = v.GetValue();
137   llvm::StringRef summary = v.GetSummary();
138   llvm::StringRef type_name = v.GetType().GetDisplayTypeName();
139 
140   std::string result;
141   llvm::raw_string_ostream strm(result);
142   if (!value.empty()) {
143     strm << value;
144     if (!summary.empty())
145       strm << ' ' << summary;
146   } else if (!summary.empty()) {
147     strm << ' ' << summary;
148   } else if (!type_name.empty()) {
149     strm << type_name;
150     lldb::addr_t address = v.GetLoadAddress();
151     if (address != LLDB_INVALID_ADDRESS)
152       strm << " @ " << llvm::format_hex(address, 0);
153   }
154   strm.flush();
155   EmplaceSafeString(object, key, result);
156 }
157 
158 void FillResponse(const llvm::json::Object &request,
159                   llvm::json::Object &response) {
160   // Fill in all of the needed response fields to a "request" and set "success"
161   // to true by default.
162   response.try_emplace("type", "response");
163   response.try_emplace("seq", (int64_t)0);
164   EmplaceSafeString(response, "command", GetString(request, "command"));
165   const int64_t seq = GetSigned(request, "seq", 0);
166   response.try_emplace("request_seq", seq);
167   response.try_emplace("success", true);
168 }
169 
170 // "Scope": {
171 //   "type": "object",
172 //   "description": "A Scope is a named container for variables. Optionally
173 //                   a scope can map to a source or a range within a source.",
174 //   "properties": {
175 //     "name": {
176 //       "type": "string",
177 //       "description": "Name of the scope such as 'Arguments', 'Locals'."
178 //     },
179 //     "variablesReference": {
180 //       "type": "integer",
181 //       "description": "The variables of this scope can be retrieved by
182 //                       passing the value of variablesReference to the
183 //                       VariablesRequest."
184 //     },
185 //     "namedVariables": {
186 //       "type": "integer",
187 //       "description": "The number of named variables in this scope. The
188 //                       client can use this optional information to present
189 //                       the variables in a paged UI and fetch them in chunks."
190 //     },
191 //     "indexedVariables": {
192 //       "type": "integer",
193 //       "description": "The number of indexed variables in this scope. The
194 //                       client can use this optional information to present
195 //                       the variables in a paged UI and fetch them in chunks."
196 //     },
197 //     "expensive": {
198 //       "type": "boolean",
199 //       "description": "If true, the number of variables in this scope is
200 //                       large or expensive to retrieve."
201 //     },
202 //     "source": {
203 //       "$ref": "#/definitions/Source",
204 //       "description": "Optional source for this scope."
205 //     },
206 //     "line": {
207 //       "type": "integer",
208 //       "description": "Optional start line of the range covered by this
209 //                       scope."
210 //     },
211 //     "column": {
212 //       "type": "integer",
213 //       "description": "Optional start column of the range covered by this
214 //                       scope."
215 //     },
216 //     "endLine": {
217 //       "type": "integer",
218 //       "description": "Optional end line of the range covered by this scope."
219 //     },
220 //     "endColumn": {
221 //       "type": "integer",
222 //       "description": "Optional end column of the range covered by this
223 //                       scope."
224 //     }
225 //   },
226 //   "required": [ "name", "variablesReference", "expensive" ]
227 // }
228 llvm::json::Value CreateScope(const llvm::StringRef name,
229                               int64_t variablesReference,
230                               int64_t namedVariables, bool expensive) {
231   llvm::json::Object object;
232   EmplaceSafeString(object, "name", name.str());
233   object.try_emplace("variablesReference", variablesReference);
234   object.try_emplace("expensive", expensive);
235   object.try_emplace("namedVariables", namedVariables);
236   return llvm::json::Value(std::move(object));
237 }
238 
239 // "Breakpoint": {
240 //   "type": "object",
241 //   "description": "Information about a Breakpoint created in setBreakpoints
242 //                   or setFunctionBreakpoints.",
243 //   "properties": {
244 //     "id": {
245 //       "type": "integer",
246 //       "description": "An optional unique identifier for the breakpoint."
247 //     },
248 //     "verified": {
249 //       "type": "boolean",
250 //       "description": "If true breakpoint could be set (but not necessarily
251 //                       at the desired location)."
252 //     },
253 //     "message": {
254 //       "type": "string",
255 //       "description": "An optional message about the state of the breakpoint.
256 //                       This is shown to the user and can be used to explain
257 //                       why a breakpoint could not be verified."
258 //     },
259 //     "source": {
260 //       "$ref": "#/definitions/Source",
261 //       "description": "The source where the breakpoint is located."
262 //     },
263 //     "line": {
264 //       "type": "integer",
265 //       "description": "The start line of the actual range covered by the
266 //                       breakpoint."
267 //     },
268 //     "column": {
269 //       "type": "integer",
270 //       "description": "An optional start column of the actual range covered
271 //                       by the breakpoint."
272 //     },
273 //     "endLine": {
274 //       "type": "integer",
275 //       "description": "An optional end line of the actual range covered by
276 //                       the breakpoint."
277 //     },
278 //     "endColumn": {
279 //       "type": "integer",
280 //       "description": "An optional end column of the actual range covered by
281 //                       the breakpoint. If no end line is given, then the end
282 //                       column is assumed to be in the start line."
283 //     }
284 //   },
285 //   "required": [ "verified" ]
286 // }
287 llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp,
288                                    llvm::Optional<llvm::StringRef> request_path,
289                                    llvm::Optional<uint32_t> request_line) {
290   // Each breakpoint location is treated as a separate breakpoint for VS code.
291   // They don't have the notion of a single breakpoint with multiple locations.
292   llvm::json::Object object;
293   if (!bp.IsValid())
294     return llvm::json::Value(std::move(object));
295 
296   object.try_emplace("verified", bp.GetNumResolvedLocations() > 0);
297   object.try_emplace("id", bp.GetID());
298   // VS Code DAP doesn't currently allow one breakpoint to have multiple
299   // locations so we just report the first one. If we report all locations
300   // then the IDE starts showing the wrong line numbers and locations for
301   // other source file and line breakpoints in the same file.
302 
303   // Below we search for the first resolved location in a breakpoint and report
304   // this as the breakpoint location since it will have a complete location
305   // that is at least loaded in the current process.
306   lldb::SBBreakpointLocation bp_loc;
307   const auto num_locs = bp.GetNumLocations();
308   for (size_t i = 0; i < num_locs; ++i) {
309     bp_loc = bp.GetLocationAtIndex(i);
310     if (bp_loc.IsResolved())
311       break;
312   }
313   // If not locations are resolved, use the first location.
314   if (!bp_loc.IsResolved())
315     bp_loc = bp.GetLocationAtIndex(0);
316   auto bp_addr = bp_loc.GetAddress();
317 
318   if (request_path)
319     object.try_emplace("source", CreateSource(*request_path));
320 
321   if (bp_addr.IsValid()) {
322     auto line_entry = bp_addr.GetLineEntry();
323     const auto line = line_entry.GetLine();
324     if (line != UINT32_MAX)
325       object.try_emplace("line", line);
326     object.try_emplace("source", CreateSource(line_entry));
327   }
328   // We try to add request_line as a fallback
329   if (request_line)
330     object.try_emplace("line", *request_line);
331   return llvm::json::Value(std::move(object));
332 }
333 
334 void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints,
335                       llvm::Optional<llvm::StringRef> request_path,
336                       llvm::Optional<uint32_t> request_line) {
337   breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line));
338 }
339 
340 // "Event": {
341 //   "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
342 //     "type": "object",
343 //     "description": "Server-initiated event.",
344 //     "properties": {
345 //       "type": {
346 //         "type": "string",
347 //         "enum": [ "event" ]
348 //       },
349 //       "event": {
350 //         "type": "string",
351 //         "description": "Type of event."
352 //       },
353 //       "body": {
354 //         "type": [ "array", "boolean", "integer", "null", "number" ,
355 //                   "object", "string" ],
356 //         "description": "Event-specific information."
357 //       }
358 //     },
359 //     "required": [ "type", "event" ]
360 //   }]
361 // },
362 // "ProtocolMessage": {
363 //   "type": "object",
364 //   "description": "Base class of requests, responses, and events.",
365 //   "properties": {
366 //         "seq": {
367 //           "type": "integer",
368 //           "description": "Sequence number."
369 //         },
370 //         "type": {
371 //           "type": "string",
372 //           "description": "Message type.",
373 //           "_enum": [ "request", "response", "event" ]
374 //         }
375 //   },
376 //   "required": [ "seq", "type" ]
377 // }
378 llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
379   llvm::json::Object event;
380   event.try_emplace("seq", 0);
381   event.try_emplace("type", "event");
382   EmplaceSafeString(event, "event", event_name);
383   return event;
384 }
385 
386 // "ExceptionBreakpointsFilter": {
387 //   "type": "object",
388 //   "description": "An ExceptionBreakpointsFilter is shown in the UI as an
389 //                   option for configuring how exceptions are dealt with.",
390 //   "properties": {
391 //     "filter": {
392 //       "type": "string",
393 //       "description": "The internal ID of the filter. This value is passed
394 //                       to the setExceptionBreakpoints request."
395 //     },
396 //     "label": {
397 //       "type": "string",
398 //       "description": "The name of the filter. This will be shown in the UI."
399 //     },
400 //     "default": {
401 //       "type": "boolean",
402 //       "description": "Initial value of the filter. If not specified a value
403 //                       'false' is assumed."
404 //     }
405 //   },
406 //   "required": [ "filter", "label" ]
407 // }
408 llvm::json::Value
409 CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
410   llvm::json::Object object;
411   EmplaceSafeString(object, "filter", bp.filter);
412   EmplaceSafeString(object, "label", bp.label);
413   object.try_emplace("default", bp.default_value);
414   return llvm::json::Value(std::move(object));
415 }
416 
417 // "Source": {
418 //   "type": "object",
419 //   "description": "A Source is a descriptor for source code. It is returned
420 //                   from the debug adapter as part of a StackFrame and it is
421 //                   used by clients when specifying breakpoints.",
422 //   "properties": {
423 //     "name": {
424 //       "type": "string",
425 //       "description": "The short name of the source. Every source returned
426 //                       from the debug adapter has a name. When sending a
427 //                       source to the debug adapter this name is optional."
428 //     },
429 //     "path": {
430 //       "type": "string",
431 //       "description": "The path of the source to be shown in the UI. It is
432 //                       only used to locate and load the content of the
433 //                       source if no sourceReference is specified (or its
434 //                       value is 0)."
435 //     },
436 //     "sourceReference": {
437 //       "type": "number",
438 //       "description": "If sourceReference > 0 the contents of the source must
439 //                       be retrieved through the SourceRequest (even if a path
440 //                       is specified). A sourceReference is only valid for a
441 //                       session, so it must not be used to persist a source."
442 //     },
443 //     "presentationHint": {
444 //       "type": "string",
445 //       "description": "An optional hint for how to present the source in the
446 //                       UI. A value of 'deemphasize' can be used to indicate
447 //                       that the source is not available or that it is
448 //                       skipped on stepping.",
449 //       "enum": [ "normal", "emphasize", "deemphasize" ]
450 //     },
451 //     "origin": {
452 //       "type": "string",
453 //       "description": "The (optional) origin of this source: possible values
454 //                       'internal module', 'inlined content from source map',
455 //                       etc."
456 //     },
457 //     "sources": {
458 //       "type": "array",
459 //       "items": {
460 //         "$ref": "#/definitions/Source"
461 //       },
462 //       "description": "An optional list of sources that are related to this
463 //                       source. These may be the source that generated this
464 //                       source."
465 //     },
466 //     "adapterData": {
467 //       "type":["array","boolean","integer","null","number","object","string"],
468 //       "description": "Optional data that a debug adapter might want to loop
469 //                       through the client. The client should leave the data
470 //                       intact and persist it across sessions. The client
471 //                       should not interpret the data."
472 //     },
473 //     "checksums": {
474 //       "type": "array",
475 //       "items": {
476 //         "$ref": "#/definitions/Checksum"
477 //       },
478 //       "description": "The checksums associated with this file."
479 //     }
480 //   }
481 // }
482 llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
483   llvm::json::Object object;
484   lldb::SBFileSpec file = line_entry.GetFileSpec();
485   if (file.IsValid()) {
486     const char *name = file.GetFilename();
487     if (name)
488       EmplaceSafeString(object, "name", name);
489     char path[PATH_MAX] = "";
490     file.GetPath(path, sizeof(path));
491     if (path[0]) {
492       EmplaceSafeString(object, "path", std::string(path));
493     }
494   }
495   return llvm::json::Value(std::move(object));
496 }
497 
498 llvm::json::Value CreateSource(llvm::StringRef source_path) {
499   llvm::json::Object source;
500   llvm::StringRef name = llvm::sys::path::filename(source_path);
501   EmplaceSafeString(source, "name", name);
502   EmplaceSafeString(source, "path", source_path);
503   return llvm::json::Value(std::move(source));
504 }
505 
506 llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) {
507   disasm_line = 0;
508   auto line_entry = frame.GetLineEntry();
509   if (line_entry.GetFileSpec().IsValid())
510     return CreateSource(line_entry);
511 
512   llvm::json::Object object;
513   const auto pc = frame.GetPC();
514 
515   lldb::SBInstructionList insts;
516   lldb::SBFunction function = frame.GetFunction();
517   lldb::addr_t low_pc = LLDB_INVALID_ADDRESS;
518   if (function.IsValid()) {
519     low_pc = function.GetStartAddress().GetLoadAddress(g_vsc.target);
520     auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
521     if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
522       // We have this disassembly cached already, return the existing
523       // sourceReference
524       object.try_emplace("sourceReference", addr_srcref->second);
525       disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
526     } else {
527       insts = function.GetInstructions(g_vsc.target);
528     }
529   } else {
530     lldb::SBSymbol symbol = frame.GetSymbol();
531     if (symbol.IsValid()) {
532       low_pc = symbol.GetStartAddress().GetLoadAddress(g_vsc.target);
533       auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
534       if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
535         // We have this disassembly cached already, return the existing
536         // sourceReference
537         object.try_emplace("sourceReference", addr_srcref->second);
538         disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
539       } else {
540         insts = symbol.GetInstructions(g_vsc.target);
541       }
542     }
543   }
544   const auto num_insts = insts.GetSize();
545   if (low_pc != LLDB_INVALID_ADDRESS && num_insts > 0) {
546     EmplaceSafeString(object, "name", frame.GetFunctionName());
547     SourceReference source;
548     llvm::raw_string_ostream src_strm(source.content);
549     std::string line;
550     for (size_t i = 0; i < num_insts; ++i) {
551       lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
552       const auto inst_addr = inst.GetAddress().GetLoadAddress(g_vsc.target);
553       const char *m = inst.GetMnemonic(g_vsc.target);
554       const char *o = inst.GetOperands(g_vsc.target);
555       const char *c = inst.GetComment(g_vsc.target);
556       if (pc == inst_addr)
557         disasm_line = i + 1;
558       const auto inst_offset = inst_addr - low_pc;
559       int spaces = 0;
560       if (inst_offset < 10)
561         spaces = 3;
562       else if (inst_offset < 100)
563         spaces = 2;
564       else if (inst_offset < 1000)
565         spaces = 1;
566       line.clear();
567       llvm::raw_string_ostream line_strm(line);
568       line_strm << llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr,
569                                  inst_offset, llvm::fmt_repeat(' ', spaces), m,
570                                  o);
571 
572       // If there is a comment append it starting at column 60 or after one
573       // space past the last char
574       const uint32_t comment_row = std::max(line_strm.str().size(), (size_t)60);
575       if (c && c[0]) {
576         if (line.size() < comment_row)
577           line_strm.indent(comment_row - line_strm.str().size());
578         line_strm << " # " << c;
579       }
580       src_strm << line_strm.str() << "\n";
581       source.addr_to_line[inst_addr] = i + 1;
582     }
583     // Flush the source stream
584     src_strm.str();
585     auto sourceReference = VSCode::GetNextSourceReference();
586     g_vsc.source_map[sourceReference] = std::move(source);
587     g_vsc.addr_to_source_ref[low_pc] = sourceReference;
588     object.try_emplace("sourceReference", sourceReference);
589   }
590   return llvm::json::Value(std::move(object));
591 }
592 
593 // "StackFrame": {
594 //   "type": "object",
595 //   "description": "A Stackframe contains the source location.",
596 //   "properties": {
597 //     "id": {
598 //       "type": "integer",
599 //       "description": "An identifier for the stack frame. It must be unique
600 //                       across all threads. This id can be used to retrieve
601 //                       the scopes of the frame with the 'scopesRequest' or
602 //                       to restart the execution of a stackframe."
603 //     },
604 //     "name": {
605 //       "type": "string",
606 //       "description": "The name of the stack frame, typically a method name."
607 //     },
608 //     "source": {
609 //       "$ref": "#/definitions/Source",
610 //       "description": "The optional source of the frame."
611 //     },
612 //     "line": {
613 //       "type": "integer",
614 //       "description": "The line within the file of the frame. If source is
615 //                       null or doesn't exist, line is 0 and must be ignored."
616 //     },
617 //     "column": {
618 //       "type": "integer",
619 //       "description": "The column within the line. If source is null or
620 //                       doesn't exist, column is 0 and must be ignored."
621 //     },
622 //     "endLine": {
623 //       "type": "integer",
624 //       "description": "An optional end line of the range covered by the
625 //                       stack frame."
626 //     },
627 //     "endColumn": {
628 //       "type": "integer",
629 //       "description": "An optional end column of the range covered by the
630 //                       stack frame."
631 //     },
632 //     "moduleId": {
633 //       "type": ["integer", "string"],
634 //       "description": "The module associated with this frame, if any."
635 //     },
636 //     "presentationHint": {
637 //       "type": "string",
638 //       "enum": [ "normal", "label", "subtle" ],
639 //       "description": "An optional hint for how to present this frame in
640 //                       the UI. A value of 'label' can be used to indicate
641 //                       that the frame is an artificial frame that is used
642 //                       as a visual label or separator. A value of 'subtle'
643 //                       can be used to change the appearance of a frame in
644 //                       a 'subtle' way."
645 //     }
646 //   },
647 //   "required": [ "id", "name", "line", "column" ]
648 // }
649 llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
650   llvm::json::Object object;
651   int64_t frame_id = MakeVSCodeFrameID(frame);
652   object.try_emplace("id", frame_id);
653   EmplaceSafeString(object, "name", frame.GetFunctionName());
654   int64_t disasm_line = 0;
655   object.try_emplace("source", CreateSource(frame, disasm_line));
656 
657   auto line_entry = frame.GetLineEntry();
658   if (disasm_line > 0) {
659     object.try_emplace("line", disasm_line);
660   } else {
661     auto line = line_entry.GetLine();
662     if (line == UINT32_MAX)
663       line = 0;
664     object.try_emplace("line", line);
665   }
666   object.try_emplace("column", line_entry.GetColumn());
667   return llvm::json::Value(std::move(object));
668 }
669 
670 // "Thread": {
671 //   "type": "object",
672 //   "description": "A Thread",
673 //   "properties": {
674 //     "id": {
675 //       "type": "integer",
676 //       "description": "Unique identifier for the thread."
677 //     },
678 //     "name": {
679 //       "type": "string",
680 //       "description": "A name of the thread."
681 //     }
682 //   },
683 //   "required": [ "id", "name" ]
684 // }
685 llvm::json::Value CreateThread(lldb::SBThread &thread) {
686   llvm::json::Object object;
687   object.try_emplace("id", (int64_t)thread.GetThreadID());
688   char thread_str[64];
689   snprintf(thread_str, sizeof(thread_str), "Thread #%u", thread.GetIndexID());
690   const char *name = thread.GetName();
691   if (name) {
692     std::string thread_with_name(thread_str);
693     thread_with_name += ' ';
694     thread_with_name += name;
695     EmplaceSafeString(object, "name", thread_with_name);
696   } else {
697     EmplaceSafeString(object, "name", std::string(thread_str));
698   }
699   return llvm::json::Value(std::move(object));
700 }
701 
702 // "StoppedEvent": {
703 //   "allOf": [ { "$ref": "#/definitions/Event" }, {
704 //     "type": "object",
705 //     "description": "Event message for 'stopped' event type. The event
706 //                     indicates that the execution of the debuggee has stopped
707 //                     due to some condition. This can be caused by a break
708 //                     point previously set, a stepping action has completed,
709 //                     by executing a debugger statement etc.",
710 //     "properties": {
711 //       "event": {
712 //         "type": "string",
713 //         "enum": [ "stopped" ]
714 //       },
715 //       "body": {
716 //         "type": "object",
717 //         "properties": {
718 //           "reason": {
719 //             "type": "string",
720 //             "description": "The reason for the event. For backward
721 //                             compatibility this string is shown in the UI if
722 //                             the 'description' attribute is missing (but it
723 //                             must not be translated).",
724 //             "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ]
725 //           },
726 //           "description": {
727 //             "type": "string",
728 //             "description": "The full reason for the event, e.g. 'Paused
729 //                             on exception'. This string is shown in the UI
730 //                             as is."
731 //           },
732 //           "threadId": {
733 //             "type": "integer",
734 //             "description": "The thread which was stopped."
735 //           },
736 //           "text": {
737 //             "type": "string",
738 //             "description": "Additional information. E.g. if reason is
739 //                             'exception', text contains the exception name.
740 //                             This string is shown in the UI."
741 //           },
742 //           "allThreadsStopped": {
743 //             "type": "boolean",
744 //             "description": "If allThreadsStopped is true, a debug adapter
745 //                             can announce that all threads have stopped.
746 //                             The client should use this information to
747 //                             enable that all threads can be expanded to
748 //                             access their stacktraces. If the attribute
749 //                             is missing or false, only the thread with the
750 //                             given threadId can be expanded."
751 //           }
752 //         },
753 //         "required": [ "reason" ]
754 //       }
755 //     },
756 //     "required": [ "event", "body" ]
757 //   }]
758 // }
759 llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
760                                       uint32_t stop_id) {
761   llvm::json::Object event(CreateEventObject("stopped"));
762   llvm::json::Object body;
763   switch (thread.GetStopReason()) {
764   case lldb::eStopReasonTrace:
765   case lldb::eStopReasonPlanComplete:
766     body.try_emplace("reason", "step");
767     break;
768   case lldb::eStopReasonBreakpoint: {
769     ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread);
770     if (exc_bp) {
771       body.try_emplace("reason", "exception");
772       EmplaceSafeString(body, "description", exc_bp->label);
773     } else {
774       body.try_emplace("reason", "breakpoint");
775       char desc_str[64];
776       uint64_t bp_id = thread.GetStopReasonDataAtIndex(0);
777       uint64_t bp_loc_id = thread.GetStopReasonDataAtIndex(1);
778       snprintf(desc_str, sizeof(desc_str), "breakpoint %" PRIu64 ".%" PRIu64,
779                bp_id, bp_loc_id);
780       EmplaceSafeString(body, "description", desc_str);
781     }
782   } break;
783   case lldb::eStopReasonWatchpoint:
784   case lldb::eStopReasonInstrumentation:
785     body.try_emplace("reason", "breakpoint");
786     break;
787   case lldb::eStopReasonSignal:
788     body.try_emplace("reason", "exception");
789     break;
790   case lldb::eStopReasonException:
791     body.try_emplace("reason", "exception");
792     break;
793   case lldb::eStopReasonExec:
794     body.try_emplace("reason", "entry");
795     break;
796   case lldb::eStopReasonThreadExiting:
797   case lldb::eStopReasonInvalid:
798   case lldb::eStopReasonNone:
799     break;
800   }
801   if (stop_id == 0)
802     body.try_emplace("reason", "entry");
803   const lldb::tid_t tid = thread.GetThreadID();
804   body.try_emplace("threadId", (int64_t)tid);
805   // If no description has been set, then set it to the default thread stopped
806   // description. If we have breakpoints that get hit and shouldn't be reported
807   // as breakpoints, then they will set the description above.
808   if (ObjectContainsKey(body, "description")) {
809     char description[1024];
810     if (thread.GetStopDescription(description, sizeof(description))) {
811       EmplaceSafeString(body, "description", std::string(description));
812     }
813   }
814   if (tid == g_vsc.focus_tid) {
815     body.try_emplace("threadCausedFocus", true);
816   }
817   body.try_emplace("preserveFocusHint", tid != g_vsc.focus_tid);
818   body.try_emplace("allThreadsStopped", true);
819   event.try_emplace("body", std::move(body));
820   return llvm::json::Value(std::move(event));
821 }
822 
823 // "Variable": {
824 //   "type": "object",
825 //   "description": "A Variable is a name/value pair. Optionally a variable
826 //                   can have a 'type' that is shown if space permits or when
827 //                   hovering over the variable's name. An optional 'kind' is
828 //                   used to render additional properties of the variable,
829 //                   e.g. different icons can be used to indicate that a
830 //                   variable is public or private. If the value is
831 //                   structured (has children), a handle is provided to
832 //                   retrieve the children with the VariablesRequest. If
833 //                   the number of named or indexed children is large, the
834 //                   numbers should be returned via the optional
835 //                   'namedVariables' and 'indexedVariables' attributes. The
836 //                   client can use this optional information to present the
837 //                   children in a paged UI and fetch them in chunks.",
838 //   "properties": {
839 //     "name": {
840 //       "type": "string",
841 //       "description": "The variable's name."
842 //     },
843 //     "value": {
844 //       "type": "string",
845 //       "description": "The variable's value. This can be a multi-line text,
846 //                       e.g. for a function the body of a function."
847 //     },
848 //     "type": {
849 //       "type": "string",
850 //       "description": "The type of the variable's value. Typically shown in
851 //                       the UI when hovering over the value."
852 //     },
853 //     "presentationHint": {
854 //       "$ref": "#/definitions/VariablePresentationHint",
855 //       "description": "Properties of a variable that can be used to determine
856 //                       how to render the variable in the UI."
857 //     },
858 //     "evaluateName": {
859 //       "type": "string",
860 //       "description": "Optional evaluatable name of this variable which can
861 //                       be passed to the 'EvaluateRequest' to fetch the
862 //                       variable's value."
863 //     },
864 //     "variablesReference": {
865 //       "type": "integer",
866 //       "description": "If variablesReference is > 0, the variable is
867 //                       structured and its children can be retrieved by
868 //                       passing variablesReference to the VariablesRequest."
869 //     },
870 //     "namedVariables": {
871 //       "type": "integer",
872 //       "description": "The number of named child variables. The client can
873 //                       use this optional information to present the children
874 //                       in a paged UI and fetch them in chunks."
875 //     },
876 //     "indexedVariables": {
877 //       "type": "integer",
878 //       "description": "The number of indexed child variables. The client
879 //                       can use this optional information to present the
880 //                       children in a paged UI and fetch them in chunks."
881 //     }
882 //   },
883 //   "required": [ "name", "value", "variablesReference" ]
884 // }
885 llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
886                                  int64_t varID, bool format_hex) {
887   llvm::json::Object object;
888   auto name = v.GetName();
889   EmplaceSafeString(object, "name", name ? name : "<null>");
890   if (format_hex)
891     v.SetFormat(lldb::eFormatHex);
892   SetValueForKey(v, object, "value");
893   auto type_cstr = v.GetType().GetDisplayTypeName();
894   EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME);
895   if (varID != INT64_MAX)
896     object.try_emplace("id", varID);
897   if (v.MightHaveChildren())
898     object.try_emplace("variablesReference", variablesReference);
899   else
900     object.try_emplace("variablesReference", (int64_t)0);
901   lldb::SBStream evaluateStream;
902   v.GetExpressionPath(evaluateStream);
903   const char *evaluateName = evaluateStream.GetData();
904   if (evaluateName && evaluateName[0])
905     EmplaceSafeString(object, "evaluateName", std::string(evaluateName));
906   return llvm::json::Value(std::move(object));
907 }
908 
909 } // namespace lldb_vscode
910