1//===-- StructuredData.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_UTILITY_STRUCTUREDDATA_H
10#define LLDB_UTILITY_STRUCTUREDDATA_H
11
12#include "llvm/ADT/StringMap.h"
13#include "llvm/ADT/StringRef.h"
14#include "llvm/Support/JSON.h"
15
16#include "lldb/Utility/FileSpec.h"
17#include "lldb/Utility/Stream.h"
18#include "lldb/lldb-enumerations.h"
19
20#include <cassert>
21#include <cstddef>
22#include <cstdint>
23#include <functional>
24#include <map>
25#include <memory>
26#include <optional>
27#include <string>
28#include <type_traits>
29#include <utility>
30#include <variant>
31#include <vector>
32
33namespace lldb_private {
34class Status;
35}
36
37namespace lldb_private {
38
39/// \class StructuredData StructuredData.h "lldb/Utility/StructuredData.h"
40/// A class which can hold structured data
41///
42/// The StructuredData class is designed to hold the data from a JSON or plist
43/// style file -- a serialized data structure with dictionaries (maps,
44/// hashes), arrays, and concrete values like integers, floating point
45/// numbers, strings, booleans.
46///
47/// StructuredData does not presuppose any knowledge of the schema for the
48/// data it is holding; it can parse JSON data, for instance, and other parts
49/// of lldb can iterate through the parsed data set to find keys and values
50/// that may be present.
51
52class StructuredData {
53 template <typename N> class Integer;
54
55public:
56 class Object;
57 class Array;
58 using UnsignedInteger = Integer<uint64_t>;
59 using SignedInteger = Integer<int64_t>;
60 class Float;
61 class Boolean;
62 class String;
63 class Dictionary;
64 class Generic;
65
66 typedef std::shared_ptr<Object> ObjectSP;
67 typedef std::shared_ptr<Array> ArraySP;
68 typedef std::shared_ptr<UnsignedInteger> UnsignedIntegerSP;
69 typedef std::shared_ptr<SignedInteger> SignedIntegerSP;
70 typedef std::shared_ptr<Float> FloatSP;
71 typedef std::shared_ptr<Boolean> BooleanSP;
72 typedef std::shared_ptr<String> StringSP;
73 typedef std::shared_ptr<Dictionary> DictionarySP;
74 typedef std::shared_ptr<Generic> GenericSP;
75
76 typedef std::variant<UnsignedIntegerSP, SignedIntegerSP> IntegerSP;
77
78 class Object : public std::enable_shared_from_this<Object> {
79 public:
80 Object(lldb::StructuredDataType t = lldb::eStructuredDataTypeInvalid)
81 : m_type(t) {}
82
83 virtual ~Object() = default;
84
85 virtual bool IsValid() const { return true; }
86
87 virtual void Clear() { m_type = lldb::eStructuredDataTypeInvalid; }
88
89 lldb::StructuredDataType GetType() const { return m_type; }
90
91 void SetType(lldb::StructuredDataType t) { m_type = t; }
92
93 Array *GetAsArray() {
94 return ((m_type == lldb::eStructuredDataTypeArray)
95 ? static_cast<Array *>(this)
96 : nullptr);
97 }
98
99 Dictionary *GetAsDictionary() {
100 return ((m_type == lldb::eStructuredDataTypeDictionary)
101 ? static_cast<Dictionary *>(this)
102 : nullptr);
103 }
104
105 UnsignedInteger *GetAsUnsignedInteger() {
106 // NOTE: For backward compatibility, eStructuredDataTypeInteger is
107 // the same as eStructuredDataTypeUnsignedInteger.
108 return ((m_type == lldb::eStructuredDataTypeInteger ||
109 m_type == lldb::eStructuredDataTypeUnsignedInteger)
110 ? static_cast<UnsignedInteger *>(this)
111 : nullptr);
112 }
113
114 SignedInteger *GetAsSignedInteger() {
115 return ((m_type == lldb::eStructuredDataTypeSignedInteger)
116 ? static_cast<SignedInteger *>(this)
117 : nullptr);
118 }
119
120 uint64_t GetUnsignedIntegerValue(uint64_t fail_value = 0) {
121 UnsignedInteger *integer = GetAsUnsignedInteger();
122 return ((integer != nullptr) ? integer->GetValue() : fail_value);
123 }
124
125 int64_t GetSignedIntegerValue(int64_t fail_value = 0) {
126 SignedInteger *integer = GetAsSignedInteger();
127 return ((integer != nullptr) ? integer->GetValue() : fail_value);
128 }
129
130 Float *GetAsFloat() {
131 return ((m_type == lldb::eStructuredDataTypeFloat)
132 ? static_cast<Float *>(this)
133 : nullptr);
134 }
135
136 double GetFloatValue(double fail_value = 0.0) {
137 Float *f = GetAsFloat();
138 return ((f != nullptr) ? f->GetValue() : fail_value);
139 }
140
141 Boolean *GetAsBoolean() {
142 return ((m_type == lldb::eStructuredDataTypeBoolean)
143 ? static_cast<Boolean *>(this)
144 : nullptr);
145 }
146
147 bool GetBooleanValue(bool fail_value = false) {
148 Boolean *b = GetAsBoolean();
149 return ((b != nullptr) ? b->GetValue() : fail_value);
150 }
151
152 String *GetAsString() {
153 return ((m_type == lldb::eStructuredDataTypeString)
154 ? static_cast<String *>(this)
155 : nullptr);
156 }
157
158 llvm::StringRef GetStringValue(const char *fail_value = nullptr) {
159 String *s = GetAsString();
160 if (s)
161 return s->GetValue();
162
163 return fail_value;
164 }
165
166 Generic *GetAsGeneric() {
167 return ((m_type == lldb::eStructuredDataTypeGeneric)
168 ? static_cast<Generic *>(this)
169 : nullptr);
170 }
171
172 ObjectSP GetObjectForDotSeparatedPath(llvm::StringRef path);
173
174 void DumpToStdout(bool pretty_print = true) const;
175
176 virtual void Serialize(llvm::json::OStream &s) const = 0;
177
178 void Dump(lldb_private::Stream &s, bool pretty_print = true) const {
179 llvm::json::OStream jso(s.AsRawOstream(), pretty_print ? 2 : 0);
180 Serialize(s&: jso);
181 }
182
183 virtual void GetDescription(lldb_private::Stream &s) const {
184 s.IndentMore();
185 Dump(s, pretty_print: false);
186 s.IndentLess();
187 }
188
189 private:
190 lldb::StructuredDataType m_type;
191 };
192
193 class Array : public Object {
194 public:
195 Array() : Object(lldb::eStructuredDataTypeArray) {}
196
197 ~Array() override = default;
198
199 bool
200 ForEach(std::function<bool(Object *object)> const &foreach_callback) const {
201 for (const auto &object_sp : m_items) {
202 if (!foreach_callback(object_sp.get()))
203 return false;
204 }
205 return true;
206 }
207
208 size_t GetSize() const { return m_items.size(); }
209
210 ObjectSP operator[](size_t idx) {
211 if (idx < m_items.size())
212 return m_items[idx];
213 return ObjectSP();
214 }
215
216 ObjectSP GetItemAtIndex(size_t idx) const {
217 assert(idx < GetSize());
218 if (idx < m_items.size())
219 return m_items[idx];
220 return ObjectSP();
221 }
222
223 template <class IntType>
224 std::optional<IntType> GetItemAtIndexAsInteger(size_t idx) const {
225 if (auto item_sp = GetItemAtIndex(idx)) {
226 if constexpr (std::numeric_limits<IntType>::is_signed) {
227 if (auto *signed_value = item_sp->GetAsSignedInteger())
228 return static_cast<IntType>(signed_value->GetValue());
229 } else {
230 if (auto *unsigned_value = item_sp->GetAsUnsignedInteger())
231 return static_cast<IntType>(unsigned_value->GetValue());
232 }
233 }
234 return {};
235 }
236
237 std::optional<llvm::StringRef> GetItemAtIndexAsString(size_t idx) const {
238 if (auto item_sp = GetItemAtIndex(idx)) {
239 if (auto *string_value = item_sp->GetAsString())
240 return string_value->GetValue();
241 }
242 return {};
243 }
244
245 /// Retrieves the element at index \a idx from a StructuredData::Array if it
246 /// is a Dictionary.
247 ///
248 /// \param[in] idx
249 /// The index of the element to retrieve.
250 ///
251 /// \return
252 /// If the element at index \a idx is a Dictionary, this method returns a
253 /// valid pointer to the Dictionary wrapped in a std::optional. If the
254 /// element is not a Dictionary or the index is invalid, this returns
255 /// std::nullopt. Note that the underlying Dictionary pointer is never
256 /// nullptr.
257 std::optional<Dictionary *> GetItemAtIndexAsDictionary(size_t idx) const {
258 if (auto item_sp = GetItemAtIndex(idx)) {
259 if (auto *dict = item_sp->GetAsDictionary())
260 return dict;
261 }
262 return {};
263 }
264
265 void Push(const ObjectSP &item) { m_items.push_back(x: item); }
266
267 void AddItem(const ObjectSP &item) { m_items.push_back(x: item); }
268
269 template <typename T> void AddIntegerItem(T value) {
270 static_assert(std::is_integral<T>::value ||
271 std::is_floating_point<T>::value,
272 "value type should be integral");
273 if constexpr (std::numeric_limits<T>::is_signed)
274 AddItem(item: std::make_shared<SignedInteger>(value));
275 else
276 AddItem(item: std::make_shared<UnsignedInteger>(value));
277 }
278
279 void AddFloatItem(double value) { AddItem(item: std::make_shared<Float>(args&: value)); }
280
281 void AddStringItem(llvm::StringRef value) {
282 AddItem(item: std::make_shared<String>(args: std::move(value)));
283 }
284
285 void AddBooleanItem(bool value) {
286 AddItem(item: std::make_shared<Boolean>(args&: value));
287 }
288
289 void Serialize(llvm::json::OStream &s) const override;
290
291 void GetDescription(lldb_private::Stream &s) const override;
292
293 protected:
294 typedef std::vector<ObjectSP> collection;
295 collection m_items;
296 };
297
298private:
299 template <typename N> class Integer : public Object {
300 static_assert(std::is_integral<N>::value, "N must be an integral type");
301
302 public:
303 Integer(N i = 0)
304 : Object(std::numeric_limits<N>::is_signed
305 ? lldb::eStructuredDataTypeSignedInteger
306 : lldb::eStructuredDataTypeUnsignedInteger),
307 m_value(i) {}
308 ~Integer() override = default;
309
310 void SetValue(N value) { m_value = value; }
311
312 N GetValue() { return m_value; }
313
314 void Serialize(llvm::json::OStream &s) const override {
315 s.value(V: static_cast<N>(m_value));
316 }
317
318 void GetDescription(lldb_private::Stream &s) const override {
319 s.Printf(format: std::numeric_limits<N>::is_signed ? "%" PRId64 : "%" PRIu64,
320 static_cast<N>(m_value));
321 }
322
323 protected:
324 N m_value;
325 };
326
327public:
328 class Float : public Object {
329 public:
330 Float(double d = 0.0)
331 : Object(lldb::eStructuredDataTypeFloat), m_value(d) {}
332
333 ~Float() override = default;
334
335 void SetValue(double value) { m_value = value; }
336
337 double GetValue() { return m_value; }
338
339 void Serialize(llvm::json::OStream &s) const override;
340
341 void GetDescription(lldb_private::Stream &s) const override;
342
343 protected:
344 double m_value;
345 };
346
347 class Boolean : public Object {
348 public:
349 Boolean(bool b = false)
350 : Object(lldb::eStructuredDataTypeBoolean), m_value(b) {}
351
352 ~Boolean() override = default;
353
354 void SetValue(bool value) { m_value = value; }
355
356 bool GetValue() { return m_value; }
357
358 void Serialize(llvm::json::OStream &s) const override;
359
360 void GetDescription(lldb_private::Stream &s) const override;
361
362 protected:
363 bool m_value;
364 };
365
366 class String : public Object {
367 public:
368 String() : Object(lldb::eStructuredDataTypeString) {}
369 explicit String(llvm::StringRef S)
370 : Object(lldb::eStructuredDataTypeString), m_value(S) {}
371
372 void SetValue(llvm::StringRef S) { m_value = std::string(S); }
373
374 llvm::StringRef GetValue() { return m_value; }
375
376 void Serialize(llvm::json::OStream &s) const override;
377
378 void GetDescription(lldb_private::Stream &s) const override;
379
380 protected:
381 std::string m_value;
382 };
383
384 class Dictionary : public Object {
385 public:
386 Dictionary() : Object(lldb::eStructuredDataTypeDictionary) {}
387
388 Dictionary(ObjectSP obj_sp) : Object(lldb::eStructuredDataTypeDictionary) {
389 if (!obj_sp || obj_sp->GetType() != lldb::eStructuredDataTypeDictionary) {
390 SetType(lldb::eStructuredDataTypeInvalid);
391 return;
392 }
393
394 Dictionary *dict = obj_sp->GetAsDictionary();
395 m_dict = dict->m_dict;
396 }
397
398 ~Dictionary() override = default;
399
400 size_t GetSize() const { return m_dict.size(); }
401
402 void ForEach(std::function<bool(llvm::StringRef key, Object *object)> const
403 &callback) const {
404 for (const auto &pair : m_dict) {
405 if (!callback(pair.first(), pair.second.get()))
406 break;
407 }
408 }
409
410 ArraySP GetKeys() const {
411 auto array_sp = std::make_shared<Array>();
412 for (auto iter = m_dict.begin(); iter != m_dict.end(); ++iter) {
413 auto key_object_sp = std::make_shared<String>(args: iter->first());
414 array_sp->Push(item: key_object_sp);
415 }
416 return array_sp;
417 }
418
419 ObjectSP GetValueForKey(llvm::StringRef key) const {
420 return m_dict.lookup(Key: key);
421 }
422
423 bool GetValueForKeyAsBoolean(llvm::StringRef key, bool &result) const {
424 bool success = false;
425 ObjectSP value_sp = GetValueForKey(key);
426 if (value_sp.get()) {
427 Boolean *result_ptr = value_sp->GetAsBoolean();
428 if (result_ptr) {
429 result = result_ptr->GetValue();
430 success = true;
431 }
432 }
433 return success;
434 }
435
436 template <class IntType>
437 bool GetValueForKeyAsInteger(llvm::StringRef key, IntType &result) const {
438 ObjectSP value_sp = GetValueForKey(key);
439 if (value_sp) {
440 if constexpr (std::numeric_limits<IntType>::is_signed) {
441 if (auto signed_value = value_sp->GetAsSignedInteger()) {
442 result = static_cast<IntType>(signed_value->GetValue());
443 return true;
444 }
445 } else {
446 if (auto unsigned_value = value_sp->GetAsUnsignedInteger()) {
447 result = static_cast<IntType>(unsigned_value->GetValue());
448 return true;
449 }
450 }
451 }
452 return false;
453 }
454
455 template <class IntType>
456 bool GetValueForKeyAsInteger(llvm::StringRef key, IntType &result,
457 IntType default_val) const {
458 bool success = GetValueForKeyAsInteger<IntType>(key, result);
459 if (!success)
460 result = default_val;
461 return success;
462 }
463
464 bool GetValueForKeyAsString(llvm::StringRef key,
465 llvm::StringRef &result) const {
466 ObjectSP value_sp = GetValueForKey(key);
467 if (value_sp.get()) {
468 if (auto string_value = value_sp->GetAsString()) {
469 result = string_value->GetValue();
470 return true;
471 }
472 }
473 return false;
474 }
475
476 bool GetValueForKeyAsString(llvm::StringRef key, llvm::StringRef &result,
477 const char *default_val) const {
478 bool success = GetValueForKeyAsString(key, result);
479 if (!success) {
480 if (default_val)
481 result = default_val;
482 else
483 result = llvm::StringRef();
484 }
485 return success;
486 }
487
488 bool GetValueForKeyAsDictionary(llvm::StringRef key,
489 Dictionary *&result) const {
490 result = nullptr;
491 ObjectSP value_sp = GetValueForKey(key);
492 if (value_sp.get()) {
493 result = value_sp->GetAsDictionary();
494 return (result != nullptr);
495 }
496 return false;
497 }
498
499 bool GetValueForKeyAsArray(llvm::StringRef key, Array *&result) const {
500 result = nullptr;
501 ObjectSP value_sp = GetValueForKey(key);
502 if (value_sp.get()) {
503 result = value_sp->GetAsArray();
504 return (result != nullptr);
505 }
506 return false;
507 }
508
509 bool HasKey(llvm::StringRef key) const { return m_dict.contains(Key: key); }
510
511 void AddItem(llvm::StringRef key, ObjectSP value_sp) {
512 m_dict.insert_or_assign(Key: key, Val: std::move(value_sp));
513 }
514
515 template <typename T> void AddIntegerItem(llvm::StringRef key, T value) {
516 static_assert(std::is_integral<T>::value ||
517 std::is_floating_point<T>::value,
518 "value type should be integral");
519 if constexpr (std::numeric_limits<T>::is_signed)
520 AddItem(key, value_sp: std::make_shared<SignedInteger>(value));
521 else
522 AddItem(key, value_sp: std::make_shared<UnsignedInteger>(value));
523 }
524
525 void AddFloatItem(llvm::StringRef key, double value) {
526 AddItem(key, value_sp: std::make_shared<Float>(args&: value));
527 }
528
529 void AddStringItem(llvm::StringRef key, llvm::StringRef value) {
530 AddItem(key, value_sp: std::make_shared<String>(args: std::move(value)));
531 }
532
533 void AddBooleanItem(llvm::StringRef key, bool value) {
534 AddItem(key, value_sp: std::make_shared<Boolean>(args&: value));
535 }
536
537 void Serialize(llvm::json::OStream &s) const override;
538
539 void GetDescription(lldb_private::Stream &s) const override;
540
541 protected:
542 llvm::StringMap<ObjectSP> m_dict;
543 };
544
545 class Null : public Object {
546 public:
547 Null() : Object(lldb::eStructuredDataTypeNull) {}
548
549 ~Null() override = default;
550
551 bool IsValid() const override { return false; }
552
553 void Serialize(llvm::json::OStream &s) const override;
554
555 void GetDescription(lldb_private::Stream &s) const override;
556 };
557
558 class Generic : public Object {
559 public:
560 explicit Generic(void *object = nullptr)
561 : Object(lldb::eStructuredDataTypeGeneric), m_object(object) {}
562
563 void SetValue(void *value) { m_object = value; }
564
565 void *GetValue() const { return m_object; }
566
567 bool IsValid() const override { return m_object != nullptr; }
568
569 void Serialize(llvm::json::OStream &s) const override;
570
571 void GetDescription(lldb_private::Stream &s) const override;
572
573 private:
574 void *m_object;
575 };
576
577 static ObjectSP ParseJSON(llvm::StringRef json_text);
578 static ObjectSP ParseJSONFromFile(const FileSpec &file, Status &error);
579 static bool IsRecordType(const ObjectSP object_sp);
580};
581
582} // namespace lldb_private
583
584#endif // LLDB_UTILITY_STRUCTUREDDATA_H
585

source code of lldb/include/lldb/Utility/StructuredData.h