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 
33 namespace lldb_private {
34 class Status;
35 }
36 
37 namespace 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 
52 class StructuredData {
53   template <typename N> class Integer;
54 
55 public:
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)
m_type(t)81         : m_type(t) {}
82 
83     virtual ~Object() = default;
84 
IsValid()85     virtual bool IsValid() const { return true; }
86 
Clear()87     virtual void Clear() { m_type = lldb::eStructuredDataTypeInvalid; }
88 
GetType()89     lldb::StructuredDataType GetType() const { return m_type; }
90 
SetType(lldb::StructuredDataType t)91     void SetType(lldb::StructuredDataType t) { m_type = t; }
92 
GetAsArray()93     Array *GetAsArray() {
94       return ((m_type == lldb::eStructuredDataTypeArray)
95                   ? static_cast<Array *>(this)
96                   : nullptr);
97     }
98 
GetAsDictionary()99     Dictionary *GetAsDictionary() {
100       return ((m_type == lldb::eStructuredDataTypeDictionary)
101                   ? static_cast<Dictionary *>(this)
102                   : nullptr);
103     }
104 
GetAsUnsignedInteger()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 
GetAsSignedInteger()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 
GetAsFloat()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 
GetAsBoolean()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 
GetAsString()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 
GetAsGeneric()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(jso);
181     }
182 
GetDescription(lldb_private::Stream & s)183     virtual void GetDescription(lldb_private::Stream &s) const {
184       s.IndentMore();
185       Dump(s, false);
186       s.IndentLess();
187     }
188 
189   private:
190     lldb::StructuredDataType m_type;
191   };
192 
193   class Array : public Object {
194   public:
Array()195     Array() : Object(lldb::eStructuredDataTypeArray) {}
196 
197     ~Array() override = default;
198 
199     bool
ForEach(std::function<bool (Object * object)> const & foreach_callback)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 
GetSize()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 
GetItemAtIndex(size_t idx)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>
GetItemAtIndexAsInteger(size_t idx,IntType & result)224     bool GetItemAtIndexAsInteger(size_t idx, IntType &result) const {
225       ObjectSP value_sp = GetItemAtIndex(idx);
226       if (value_sp.get()) {
227         if constexpr (std::numeric_limits<IntType>::is_signed) {
228           if (auto signed_value = value_sp->GetAsSignedInteger()) {
229             result = static_cast<IntType>(signed_value->GetValue());
230             return true;
231           }
232         } else {
233           if (auto unsigned_value = value_sp->GetAsUnsignedInteger()) {
234             result = static_cast<IntType>(unsigned_value->GetValue());
235             return true;
236           }
237         }
238       }
239       return false;
240     }
241 
242     template <class IntType>
GetItemAtIndexAsInteger(size_t idx,IntType & result,IntType default_val)243     bool GetItemAtIndexAsInteger(size_t idx, IntType &result,
244                                  IntType default_val) const {
245       bool success = GetItemAtIndexAsInteger(idx, result);
246       if (!success)
247         result = default_val;
248       return success;
249     }
250 
GetItemAtIndexAsString(size_t idx)251     std::optional<llvm::StringRef> GetItemAtIndexAsString(size_t idx) const {
252       if (auto item_sp = GetItemAtIndex(idx)) {
253         if (auto *string_value = item_sp->GetAsString())
254           return string_value->GetValue();
255       }
256       return {};
257     }
258 
259     /// Retrieves the element at index \a idx from a StructuredData::Array if it
260     /// is a Dictionary.
261     ///
262     /// \param[in] idx
263     ///   The index of the element to retrieve.
264     ///
265     /// \return
266     ///   If the element at index \a idx is a Dictionary, this method returns a
267     ///   valid pointer to the Dictionary wrapped in a std::optional. If the
268     ///   element is not a Dictionary or the index is invalid, this returns
269     ///   std::nullopt. Note that the underlying Dictionary pointer is never
270     ///   nullptr.
GetItemAtIndexAsDictionary(size_t idx)271     std::optional<Dictionary *> GetItemAtIndexAsDictionary(size_t idx) const {
272       if (auto item_sp = GetItemAtIndex(idx)) {
273         if (auto *dict = item_sp->GetAsDictionary())
274           return dict;
275       }
276       return {};
277     }
278 
Push(const ObjectSP & item)279     void Push(const ObjectSP &item) { m_items.push_back(item); }
280 
AddItem(const ObjectSP & item)281     void AddItem(const ObjectSP &item) { m_items.push_back(item); }
282 
AddIntegerItem(T value)283     template <typename T> void AddIntegerItem(T value) {
284       static_assert(std::is_integral<T>::value ||
285                         std::is_floating_point<T>::value,
286                     "value type should be integral");
287       if constexpr (std::numeric_limits<T>::is_signed)
288         AddItem(std::make_shared<SignedInteger>(value));
289       else
290         AddItem(std::make_shared<UnsignedInteger>(value));
291     }
292 
AddFloatItem(double value)293     void AddFloatItem(double value) { AddItem(std::make_shared<Float>(value)); }
294 
AddStringItem(llvm::StringRef value)295     void AddStringItem(llvm::StringRef value) {
296       AddItem(std::make_shared<String>(std::move(value)));
297     }
298 
AddBooleanItem(bool value)299     void AddBooleanItem(bool value) {
300       AddItem(std::make_shared<Boolean>(value));
301     }
302 
303     void Serialize(llvm::json::OStream &s) const override;
304 
305     void GetDescription(lldb_private::Stream &s) const override;
306 
307   protected:
308     typedef std::vector<ObjectSP> collection;
309     collection m_items;
310   };
311 
312 private:
313   template <typename N> class Integer : public Object {
314     static_assert(std::is_integral<N>::value, "N must be an integral type");
315 
316   public:
317     Integer(N i = 0)
318         : Object(std::numeric_limits<N>::is_signed
319                      ? lldb::eStructuredDataTypeSignedInteger
320                      : lldb::eStructuredDataTypeUnsignedInteger),
321           m_value(i) {}
322     ~Integer() override = default;
323 
SetValue(N value)324     void SetValue(N value) { m_value = value; }
325 
GetValue()326     N GetValue() { return m_value; }
327 
Serialize(llvm::json::OStream & s)328     void Serialize(llvm::json::OStream &s) const override {
329       s.value(static_cast<N>(m_value));
330     }
331 
GetDescription(lldb_private::Stream & s)332     void GetDescription(lldb_private::Stream &s) const override {
333       s.Printf(std::numeric_limits<N>::is_signed ? "%" PRId64 : "%" PRIu64,
334                static_cast<N>(m_value));
335     }
336 
337   protected:
338     N m_value;
339   };
340 
341 public:
342   class Float : public Object {
343   public:
344     Float(double d = 0.0)
Object(lldb::eStructuredDataTypeFloat)345         : Object(lldb::eStructuredDataTypeFloat), m_value(d) {}
346 
347     ~Float() override = default;
348 
SetValue(double value)349     void SetValue(double value) { m_value = value; }
350 
GetValue()351     double GetValue() { return m_value; }
352 
353     void Serialize(llvm::json::OStream &s) const override;
354 
355     void GetDescription(lldb_private::Stream &s) const override;
356 
357   protected:
358     double m_value;
359   };
360 
361   class Boolean : public Object {
362   public:
363     Boolean(bool b = false)
Object(lldb::eStructuredDataTypeBoolean)364         : Object(lldb::eStructuredDataTypeBoolean), m_value(b) {}
365 
366     ~Boolean() override = default;
367 
SetValue(bool value)368     void SetValue(bool value) { m_value = value; }
369 
GetValue()370     bool GetValue() { return m_value; }
371 
372     void Serialize(llvm::json::OStream &s) const override;
373 
374     void GetDescription(lldb_private::Stream &s) const override;
375 
376   protected:
377     bool m_value;
378   };
379 
380   class String : public Object {
381   public:
String()382     String() : Object(lldb::eStructuredDataTypeString) {}
String(llvm::StringRef S)383     explicit String(llvm::StringRef S)
384         : Object(lldb::eStructuredDataTypeString), m_value(S) {}
385 
SetValue(llvm::StringRef S)386     void SetValue(llvm::StringRef S) { m_value = std::string(S); }
387 
GetValue()388     llvm::StringRef GetValue() { return m_value; }
389 
390     void Serialize(llvm::json::OStream &s) const override;
391 
392     void GetDescription(lldb_private::Stream &s) const override;
393 
394   protected:
395     std::string m_value;
396   };
397 
398   class Dictionary : public Object {
399   public:
Dictionary()400     Dictionary() : Object(lldb::eStructuredDataTypeDictionary) {}
401 
Dictionary(ObjectSP obj_sp)402     Dictionary(ObjectSP obj_sp) : Object(lldb::eStructuredDataTypeDictionary) {
403       if (!obj_sp || obj_sp->GetType() != lldb::eStructuredDataTypeDictionary) {
404         SetType(lldb::eStructuredDataTypeInvalid);
405         return;
406       }
407 
408       Dictionary *dict = obj_sp->GetAsDictionary();
409       m_dict = dict->m_dict;
410     }
411 
412     ~Dictionary() override = default;
413 
GetSize()414     size_t GetSize() const { return m_dict.size(); }
415 
ForEach(std::function<bool (llvm::StringRef key,Object * object)> const & callback)416     void ForEach(std::function<bool(llvm::StringRef key, Object *object)> const
417                      &callback) const {
418       for (const auto &pair : m_dict) {
419         if (!callback(pair.first(), pair.second.get()))
420           break;
421       }
422     }
423 
GetKeys()424     ArraySP GetKeys() const {
425       auto array_sp = std::make_shared<Array>();
426       for (auto iter = m_dict.begin(); iter != m_dict.end(); ++iter) {
427         auto key_object_sp = std::make_shared<String>(iter->first());
428         array_sp->Push(key_object_sp);
429       }
430       return array_sp;
431     }
432 
GetValueForKey(llvm::StringRef key)433     ObjectSP GetValueForKey(llvm::StringRef key) const {
434       return m_dict.lookup(key);
435     }
436 
GetValueForKeyAsBoolean(llvm::StringRef key,bool & result)437     bool GetValueForKeyAsBoolean(llvm::StringRef key, bool &result) const {
438       bool success = false;
439       ObjectSP value_sp = GetValueForKey(key);
440       if (value_sp.get()) {
441         Boolean *result_ptr = value_sp->GetAsBoolean();
442         if (result_ptr) {
443           result = result_ptr->GetValue();
444           success = true;
445         }
446       }
447       return success;
448     }
449 
450     template <class IntType>
GetValueForKeyAsInteger(llvm::StringRef key,IntType & result)451     bool GetValueForKeyAsInteger(llvm::StringRef key, IntType &result) const {
452       ObjectSP value_sp = GetValueForKey(key);
453       if (value_sp) {
454         if constexpr (std::numeric_limits<IntType>::is_signed) {
455           if (auto signed_value = value_sp->GetAsSignedInteger()) {
456             result = static_cast<IntType>(signed_value->GetValue());
457             return true;
458           }
459         } else {
460           if (auto unsigned_value = value_sp->GetAsUnsignedInteger()) {
461             result = static_cast<IntType>(unsigned_value->GetValue());
462             return true;
463           }
464         }
465       }
466       return false;
467     }
468 
469     template <class IntType>
GetValueForKeyAsInteger(llvm::StringRef key,IntType & result,IntType default_val)470     bool GetValueForKeyAsInteger(llvm::StringRef key, IntType &result,
471                                  IntType default_val) const {
472       bool success = GetValueForKeyAsInteger<IntType>(key, result);
473       if (!success)
474         result = default_val;
475       return success;
476     }
477 
GetValueForKeyAsString(llvm::StringRef key,llvm::StringRef & result)478     bool GetValueForKeyAsString(llvm::StringRef key,
479                                 llvm::StringRef &result) const {
480       ObjectSP value_sp = GetValueForKey(key);
481       if (value_sp.get()) {
482         if (auto string_value = value_sp->GetAsString()) {
483           result = string_value->GetValue();
484           return true;
485         }
486       }
487       return false;
488     }
489 
GetValueForKeyAsString(llvm::StringRef key,llvm::StringRef & result,const char * default_val)490     bool GetValueForKeyAsString(llvm::StringRef key, llvm::StringRef &result,
491                                 const char *default_val) const {
492       bool success = GetValueForKeyAsString(key, result);
493       if (!success) {
494         if (default_val)
495           result = default_val;
496         else
497           result = llvm::StringRef();
498       }
499       return success;
500     }
501 
GetValueForKeyAsDictionary(llvm::StringRef key,Dictionary * & result)502     bool GetValueForKeyAsDictionary(llvm::StringRef key,
503                                     Dictionary *&result) const {
504       result = nullptr;
505       ObjectSP value_sp = GetValueForKey(key);
506       if (value_sp.get()) {
507         result = value_sp->GetAsDictionary();
508         return (result != nullptr);
509       }
510       return false;
511     }
512 
GetValueForKeyAsArray(llvm::StringRef key,Array * & result)513     bool GetValueForKeyAsArray(llvm::StringRef key, Array *&result) const {
514       result = nullptr;
515       ObjectSP value_sp = GetValueForKey(key);
516       if (value_sp.get()) {
517         result = value_sp->GetAsArray();
518         return (result != nullptr);
519       }
520       return false;
521     }
522 
HasKey(llvm::StringRef key)523     bool HasKey(llvm::StringRef key) const { return m_dict.contains(key); }
524 
AddItem(llvm::StringRef key,ObjectSP value_sp)525     void AddItem(llvm::StringRef key, ObjectSP value_sp) {
526       m_dict.insert_or_assign(key, std::move(value_sp));
527     }
528 
AddIntegerItem(llvm::StringRef key,T value)529     template <typename T> void AddIntegerItem(llvm::StringRef key, T value) {
530       static_assert(std::is_integral<T>::value ||
531                         std::is_floating_point<T>::value,
532                     "value type should be integral");
533       if constexpr (std::numeric_limits<T>::is_signed)
534         AddItem(key, std::make_shared<SignedInteger>(value));
535       else
536         AddItem(key, std::make_shared<UnsignedInteger>(value));
537     }
538 
AddFloatItem(llvm::StringRef key,double value)539     void AddFloatItem(llvm::StringRef key, double value) {
540       AddItem(key, std::make_shared<Float>(value));
541     }
542 
AddStringItem(llvm::StringRef key,llvm::StringRef value)543     void AddStringItem(llvm::StringRef key, llvm::StringRef value) {
544       AddItem(key, std::make_shared<String>(std::move(value)));
545     }
546 
AddBooleanItem(llvm::StringRef key,bool value)547     void AddBooleanItem(llvm::StringRef key, bool value) {
548       AddItem(key, std::make_shared<Boolean>(value));
549     }
550 
551     void Serialize(llvm::json::OStream &s) const override;
552 
553     void GetDescription(lldb_private::Stream &s) const override;
554 
555   protected:
556     llvm::StringMap<ObjectSP> m_dict;
557   };
558 
559   class Null : public Object {
560   public:
Null()561     Null() : Object(lldb::eStructuredDataTypeNull) {}
562 
563     ~Null() override = default;
564 
IsValid()565     bool IsValid() const override { return false; }
566 
567     void Serialize(llvm::json::OStream &s) const override;
568 
569     void GetDescription(lldb_private::Stream &s) const override;
570   };
571 
572   class Generic : public Object {
573   public:
574     explicit Generic(void *object = nullptr)
Object(lldb::eStructuredDataTypeGeneric)575         : Object(lldb::eStructuredDataTypeGeneric), m_object(object) {}
576 
SetValue(void * value)577     void SetValue(void *value) { m_object = value; }
578 
GetValue()579     void *GetValue() const { return m_object; }
580 
IsValid()581     bool IsValid() const override { return m_object != nullptr; }
582 
583     void Serialize(llvm::json::OStream &s) const override;
584 
585     void GetDescription(lldb_private::Stream &s) const override;
586 
587   private:
588     void *m_object;
589   };
590 
591   static ObjectSP ParseJSON(llvm::StringRef json_text);
592   static ObjectSP ParseJSONFromFile(const FileSpec &file, Status &error);
593   static bool IsRecordType(const ObjectSP object_sp);
594 };
595 
596 } // namespace lldb_private
597 
598 #endif // LLDB_UTILITY_STRUCTUREDDATA_H
599