| //===-- JSONGenerator.h ----------------------------------------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLDB_TOOLS_DEBUGSERVER_SOURCE_JSONGENERATOR_H | |
| #define LLDB_TOOLS_DEBUGSERVER_SOURCE_JSONGENERATOR_H | |
| #include <iomanip> | |
| #include <sstream> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| /// \class JSONGenerator JSONGenerator.h | |
| /// A class which can construct structured data for the sole purpose | |
| /// of printing it in JSON format. | |
| /// | |
| /// A stripped down version of lldb's StructuredData objects which are much | |
| /// general purpose. This variant is intended only for assembling information | |
| /// and printing it as a JSON string. | |
| class JSONGenerator { | |
| public: | |
| class Object; | |
| class Array; | |
| class Integer; | |
| class Float; | |
| class Boolean; | |
| class String; | |
| class Dictionary; | |
| class Generic; | |
| typedef std::shared_ptr<Object> ObjectSP; | |
| typedef std::shared_ptr<Array> ArraySP; | |
| typedef std::shared_ptr<Integer> IntegerSP; | |
| typedef std::shared_ptr<Float> FloatSP; | |
| typedef std::shared_ptr<Boolean> BooleanSP; | |
| typedef std::shared_ptr<String> StringSP; | |
| typedef std::shared_ptr<Dictionary> DictionarySP; | |
| typedef std::shared_ptr<Generic> GenericSP; | |
| enum class Type { | |
| eTypeInvalid = -1, | |
| eTypeNull = 0, | |
| eTypeGeneric, | |
| eTypeArray, | |
| eTypeInteger, | |
| eTypeFloat, | |
| eTypeBoolean, | |
| eTypeString, | |
| eTypeDictionary | |
| }; | |
| class Object : public std::enable_shared_from_this<Object> { | |
| public: | |
| Object(Type t = Type::eTypeInvalid) : m_type(t) {} | |
| virtual ~Object() {} | |
| virtual bool IsValid() const { return true; } | |
| virtual void Clear() { m_type = Type::eTypeInvalid; } | |
| Type GetType() const { return m_type; } | |
| void SetType(Type t) { m_type = t; } | |
| Array *GetAsArray() { | |
| if (m_type == Type::eTypeArray) | |
| return (Array *)this; | |
| return NULL; | |
| } | |
| Dictionary *GetAsDictionary() { | |
| if (m_type == Type::eTypeDictionary) | |
| return (Dictionary *)this; | |
| return NULL; | |
| } | |
| Integer *GetAsInteger() { | |
| if (m_type == Type::eTypeInteger) | |
| return (Integer *)this; | |
| return NULL; | |
| } | |
| Float *GetAsFloat() { | |
| if (m_type == Type::eTypeFloat) | |
| return (Float *)this; | |
| return NULL; | |
| } | |
| Boolean *GetAsBoolean() { | |
| if (m_type == Type::eTypeBoolean) | |
| return (Boolean *)this; | |
| return NULL; | |
| } | |
| String *GetAsString() { | |
| if (m_type == Type::eTypeString) | |
| return (String *)this; | |
| return NULL; | |
| } | |
| Generic *GetAsGeneric() { | |
| if (m_type == Type::eTypeGeneric) | |
| return (Generic *)this; | |
| return NULL; | |
| } | |
| virtual void Dump(std::ostream &s) const = 0; | |
| virtual void DumpBinaryEscaped(std::ostream &s) const = 0; | |
| private: | |
| Type m_type; | |
| }; | |
| class Array : public Object { | |
| public: | |
| Array() : Object(Type::eTypeArray) {} | |
| virtual ~Array() {} | |
| void AddItem(ObjectSP item) { m_items.push_back(item); } | |
| bool empty() const { return m_items.empty(); } | |
| void AddIntegerItem(uint64_t value) { | |
| AddItem(ObjectSP(new Integer(value))); | |
| } | |
| void Dump(std::ostream &s) const override { | |
| s << "["; | |
| const size_t arrsize = m_items.size(); | |
| for (size_t i = 0; i < arrsize; ++i) { | |
| m_items[i]->Dump(s); | |
| if (i + 1 < arrsize) | |
| s << ","; | |
| } | |
| s << "]"; | |
| } | |
| void DumpBinaryEscaped(std::ostream &s) const override { | |
| s << "["; | |
| const size_t arrsize = m_items.size(); | |
| for (size_t i = 0; i < arrsize; ++i) { | |
| m_items[i]->DumpBinaryEscaped(s); | |
| if (i + 1 < arrsize) | |
| s << ","; | |
| } | |
| s << "]"; | |
| } | |
| protected: | |
| typedef std::vector<ObjectSP> collection; | |
| collection m_items; | |
| }; | |
| class Integer : public Object { | |
| public: | |
| Integer(uint64_t value = 0) : Object(Type::eTypeInteger), m_value(value) {} | |
| virtual ~Integer() {} | |
| void SetValue(uint64_t value) { m_value = value; } | |
| uint64_t GetValue() const { return m_value; } | |
| void Dump(std::ostream &s) const override { s << m_value; } | |
| void DumpBinaryEscaped(std::ostream &s) const override { Dump(s); } | |
| protected: | |
| uint64_t m_value; | |
| }; | |
| class Float : public Object { | |
| public: | |
| Float(double d = 0.0) : Object(Type::eTypeFloat), m_value(d) {} | |
| virtual ~Float() {} | |
| void SetValue(double value) { m_value = value; } | |
| void Dump(std::ostream &s) const override { s << m_value; } | |
| void DumpBinaryEscaped(std::ostream &s) const override { Dump(s); } | |
| protected: | |
| double m_value; | |
| }; | |
| class Boolean : public Object { | |
| public: | |
| Boolean(bool b = false) : Object(Type::eTypeBoolean), m_value(b) {} | |
| virtual ~Boolean() {} | |
| void SetValue(bool value) { m_value = value; } | |
| void Dump(std::ostream &s) const override { | |
| if (m_value) | |
| s << "true"; | |
| else | |
| s << "false"; | |
| } | |
| void DumpBinaryEscaped(std::ostream &s) const override { Dump(s); } | |
| protected: | |
| bool m_value; | |
| }; | |
| class String : public Object { | |
| public: | |
| String() : Object(Type::eTypeString), m_value() {} | |
| String(const std::string &s) : Object(Type::eTypeString), m_value(s) {} | |
| String(const std::string &&s) : Object(Type::eTypeString), m_value(s) {} | |
| void SetValue(const std::string &string) { m_value = string; } | |
| void Dump(std::ostream &s) const override { | |
| s << '"'; | |
| const size_t strsize = m_value.size(); | |
| for (size_t i = 0; i < strsize; ++i) { | |
| char ch = m_value[i]; | |
| if (ch == '"') | |
| s << '\\'; | |
| s << ch; | |
| } | |
| s << '"'; | |
| } | |
| void DumpBinaryEscaped(std::ostream &s) const override { | |
| s << '"'; | |
| const size_t strsize = m_value.size(); | |
| for (size_t i = 0; i < strsize; ++i) { | |
| char ch = m_value[i]; | |
| if (ch == '"') | |
| s << '\\'; | |
| // gdb remote serial protocol binary escaping | |
| if (ch == '#' || ch == '$' || ch == '}' || ch == '*') { | |
| s << '}'; // 0x7d next character is escaped | |
| s << static_cast<char>(ch ^ 0x20); | |
| } else { | |
| s << ch; | |
| } | |
| } | |
| s << '"'; | |
| } | |
| protected: | |
| std::string m_value; | |
| }; | |
| class Dictionary : public Object { | |
| public: | |
| Dictionary() : Object(Type::eTypeDictionary), m_dict() {} | |
| virtual ~Dictionary() {} | |
| void AddItem(std::string key, ObjectSP value) { | |
| m_dict.push_back(Pair(key, value)); | |
| } | |
| void AddIntegerItem(std::string key, uint64_t value) { | |
| AddItem(key, ObjectSP(new Integer(value))); | |
| } | |
| void AddFloatItem(std::string key, double value) { | |
| AddItem(key, ObjectSP(new Float(value))); | |
| } | |
| void AddStringItem(std::string key, std::string value) { | |
| AddItem(key, ObjectSP(new String(std::move(value)))); | |
| } | |
| void AddBytesAsHexASCIIString(std::string key, const uint8_t *src, | |
| size_t src_len) { | |
| if (src && src_len) { | |
| std::ostringstream strm; | |
| for (size_t i = 0; i < src_len; i++) | |
| strm << std::setfill('0') << std::hex << std::right << std::setw(2) | |
| << ((uint32_t)(src[i])); | |
| AddItem(key, ObjectSP(new String(std::move(strm.str())))); | |
| } else { | |
| AddItem(key, ObjectSP(new String())); | |
| } | |
| } | |
| void AddBooleanItem(std::string key, bool value) { | |
| AddItem(key, ObjectSP(new Boolean(value))); | |
| } | |
| ObjectSP GetValueForKey(const std::string &key) const { | |
| for (const auto &kv : m_dict) | |
| if (kv.first == key) | |
| return kv.second; | |
| return {}; | |
| } | |
| void Dump(std::ostream &s) const override { | |
| bool have_printed_one_elem = false; | |
| s << "{"; | |
| for (collection::const_iterator iter = m_dict.begin(); | |
| iter != m_dict.end(); ++iter) { | |
| if (!have_printed_one_elem) { | |
| have_printed_one_elem = true; | |
| } else { | |
| s << ","; | |
| } | |
| s << "\"" << iter->first.c_str() << "\":"; | |
| iter->second->Dump(s); | |
| } | |
| s << "}"; | |
| } | |
| void DumpBinaryEscaped(std::ostream &s) const override { | |
| bool have_printed_one_elem = false; | |
| s << "{"; | |
| for (collection::const_iterator iter = m_dict.begin(); | |
| iter != m_dict.end(); ++iter) { | |
| if (!have_printed_one_elem) { | |
| have_printed_one_elem = true; | |
| } else { | |
| s << ","; | |
| } | |
| s << "\"" << binary_encode_string(iter->first) << "\":"; | |
| iter->second->DumpBinaryEscaped(s); | |
| } | |
| // '}' must be escaped for the gdb remote serial | |
| // protocol. | |
| s << "}"; | |
| s << static_cast<char>('}' ^ 0x20); | |
| } | |
| protected: | |
| std::string binary_encode_string(const std::string &s) const { | |
| std::string output; | |
| const size_t s_size = s.size(); | |
| const char *s_chars = s.c_str(); | |
| for (size_t i = 0; i < s_size; i++) { | |
| unsigned char ch = *(s_chars + i); | |
| if (ch == '#' || ch == '$' || ch == '}' || ch == '*') { | |
| output.push_back('}'); // 0x7d | |
| output.push_back(ch ^ 0x20); | |
| } else { | |
| output.push_back(ch); | |
| } | |
| } | |
| return output; | |
| } | |
| // Keep the dictionary as a vector so the dictionary doesn't reorder itself | |
| // when you dump it | |
| // We aren't accessing keys by name, so this won't affect performance | |
| typedef std::pair<std::string, ObjectSP> Pair; | |
| typedef std::vector<Pair> collection; | |
| collection m_dict; | |
| }; | |
| class Null : public Object { | |
| public: | |
| Null() : Object(Type::eTypeNull) {} | |
| virtual ~Null() {} | |
| bool IsValid() const override { return false; } | |
| void Dump(std::ostream &s) const override { s << "null"; } | |
| void DumpBinaryEscaped(std::ostream &s) const override { Dump(s); } | |
| protected: | |
| }; | |
| class Generic : public Object { | |
| public: | |
| explicit Generic(void *object = nullptr) | |
| : Object(Type::eTypeGeneric), m_object(object) {} | |
| void SetValue(void *value) { m_object = value; } | |
| void *GetValue() const { return m_object; } | |
| bool IsValid() const override { return m_object != nullptr; } | |
| void Dump(std::ostream &s) const override; | |
| void DumpBinaryEscaped(std::ostream &s) const override; | |
| private: | |
| void *m_object; | |
| }; | |
| }; // class JSONGenerator | |
| #endif // LLDB_TOOLS_DEBUGSERVER_SOURCE_JSONGENERATOR_H |