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