1 | //===-- JSONGenerator.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_TOOLS_DEBUGSERVER_SOURCE_JSONGENERATOR_H |
10 | #define LLDB_TOOLS_DEBUGSERVER_SOURCE_JSONGENERATOR_H |
11 | |
12 | #include <iomanip> |
13 | #include <sstream> |
14 | #include <string> |
15 | #include <utility> |
16 | #include <vector> |
17 | |
18 | /// \class JSONGenerator JSONGenerator.h |
19 | /// A class which can construct structured data for the sole purpose |
20 | /// of printing it in JSON format. |
21 | /// |
22 | /// A stripped down version of lldb's StructuredData objects which are much |
23 | /// general purpose. This variant is intended only for assembling information |
24 | /// and printing it as a JSON string. |
25 | |
26 | class JSONGenerator { |
27 | public: |
28 | class Object; |
29 | class Array; |
30 | class Integer; |
31 | class Float; |
32 | class Boolean; |
33 | class String; |
34 | class Dictionary; |
35 | class Generic; |
36 | |
37 | typedef std::shared_ptr<Object> ObjectSP; |
38 | typedef std::shared_ptr<Array> ArraySP; |
39 | typedef std::shared_ptr<Integer> IntegerSP; |
40 | typedef std::shared_ptr<Float> FloatSP; |
41 | typedef std::shared_ptr<Boolean> BooleanSP; |
42 | typedef std::shared_ptr<String> StringSP; |
43 | typedef std::shared_ptr<Dictionary> DictionarySP; |
44 | typedef std::shared_ptr<Generic> GenericSP; |
45 | |
46 | enum class Type { |
47 | eTypeInvalid = -1, |
48 | eTypeNull = 0, |
49 | eTypeGeneric, |
50 | eTypeArray, |
51 | eTypeInteger, |
52 | eTypeFloat, |
53 | eTypeBoolean, |
54 | eTypeString, |
55 | eTypeDictionary |
56 | }; |
57 | |
58 | class Object : public std::enable_shared_from_this<Object> { |
59 | public: |
60 | Object(Type t = Type::eTypeInvalid) : m_type(t) {} |
61 | |
62 | virtual ~Object() {} |
63 | |
64 | virtual bool IsValid() const { return true; } |
65 | |
66 | virtual void Clear() { m_type = Type::eTypeInvalid; } |
67 | |
68 | Type GetType() const { return m_type; } |
69 | |
70 | void SetType(Type t) { m_type = t; } |
71 | |
72 | Array *GetAsArray() { |
73 | if (m_type == Type::eTypeArray) |
74 | return (Array *)this; |
75 | return NULL; |
76 | } |
77 | |
78 | Dictionary *GetAsDictionary() { |
79 | if (m_type == Type::eTypeDictionary) |
80 | return (Dictionary *)this; |
81 | return NULL; |
82 | } |
83 | |
84 | Integer *GetAsInteger() { |
85 | if (m_type == Type::eTypeInteger) |
86 | return (Integer *)this; |
87 | return NULL; |
88 | } |
89 | |
90 | Float *GetAsFloat() { |
91 | if (m_type == Type::eTypeFloat) |
92 | return (Float *)this; |
93 | return NULL; |
94 | } |
95 | |
96 | Boolean *GetAsBoolean() { |
97 | if (m_type == Type::eTypeBoolean) |
98 | return (Boolean *)this; |
99 | return NULL; |
100 | } |
101 | |
102 | String *GetAsString() { |
103 | if (m_type == Type::eTypeString) |
104 | return (String *)this; |
105 | return NULL; |
106 | } |
107 | |
108 | Generic *GetAsGeneric() { |
109 | if (m_type == Type::eTypeGeneric) |
110 | return (Generic *)this; |
111 | return NULL; |
112 | } |
113 | |
114 | virtual void Dump(std::ostream &s) const = 0; |
115 | |
116 | virtual void DumpBinaryEscaped(std::ostream &s) const = 0; |
117 | |
118 | private: |
119 | Type m_type; |
120 | }; |
121 | |
122 | class Array : public Object { |
123 | public: |
124 | Array() : Object(Type::eTypeArray) {} |
125 | |
126 | virtual ~Array() {} |
127 | |
128 | void AddItem(ObjectSP item) { m_items.push_back(x: item); } |
129 | |
130 | void Dump(std::ostream &s) const override { |
131 | s << "[" ; |
132 | const size_t arrsize = m_items.size(); |
133 | for (size_t i = 0; i < arrsize; ++i) { |
134 | m_items[i]->Dump(s); |
135 | if (i + 1 < arrsize) |
136 | s << "," ; |
137 | } |
138 | s << "]" ; |
139 | } |
140 | |
141 | void DumpBinaryEscaped(std::ostream &s) const override { |
142 | s << "[" ; |
143 | const size_t arrsize = m_items.size(); |
144 | for (size_t i = 0; i < arrsize; ++i) { |
145 | m_items[i]->DumpBinaryEscaped(s); |
146 | if (i + 1 < arrsize) |
147 | s << "," ; |
148 | } |
149 | s << "]" ; |
150 | } |
151 | |
152 | protected: |
153 | typedef std::vector<ObjectSP> collection; |
154 | collection m_items; |
155 | }; |
156 | |
157 | class Integer : public Object { |
158 | public: |
159 | Integer(uint64_t value = 0) : Object(Type::eTypeInteger), m_value(value) {} |
160 | |
161 | virtual ~Integer() {} |
162 | |
163 | void SetValue(uint64_t value) { m_value = value; } |
164 | |
165 | void Dump(std::ostream &s) const override { s << m_value; } |
166 | |
167 | void DumpBinaryEscaped(std::ostream &s) const override { Dump(s); } |
168 | |
169 | protected: |
170 | uint64_t m_value; |
171 | }; |
172 | |
173 | class Float : public Object { |
174 | public: |
175 | Float(double d = 0.0) : Object(Type::eTypeFloat), m_value(d) {} |
176 | |
177 | virtual ~Float() {} |
178 | |
179 | void SetValue(double value) { m_value = value; } |
180 | |
181 | void Dump(std::ostream &s) const override { s << m_value; } |
182 | |
183 | void DumpBinaryEscaped(std::ostream &s) const override { Dump(s); } |
184 | |
185 | protected: |
186 | double m_value; |
187 | }; |
188 | |
189 | class Boolean : public Object { |
190 | public: |
191 | Boolean(bool b = false) : Object(Type::eTypeBoolean), m_value(b) {} |
192 | |
193 | virtual ~Boolean() {} |
194 | |
195 | void SetValue(bool value) { m_value = value; } |
196 | |
197 | void Dump(std::ostream &s) const override { |
198 | if (m_value) |
199 | s << "true" ; |
200 | else |
201 | s << "false" ; |
202 | } |
203 | |
204 | void DumpBinaryEscaped(std::ostream &s) const override { Dump(s); } |
205 | |
206 | protected: |
207 | bool m_value; |
208 | }; |
209 | |
210 | class String : public Object { |
211 | public: |
212 | String() : Object(Type::eTypeString), m_value() {} |
213 | |
214 | String(const std::string &s) : Object(Type::eTypeString), m_value(s) {} |
215 | |
216 | String(const std::string &&s) : Object(Type::eTypeString), m_value(s) {} |
217 | |
218 | void SetValue(const std::string &string) { m_value = string; } |
219 | |
220 | void Dump(std::ostream &s) const override { |
221 | s << '"'; |
222 | const size_t strsize = m_value.size(); |
223 | for (size_t i = 0; i < strsize; ++i) { |
224 | char ch = m_value[i]; |
225 | if (ch == '"') |
226 | s << '\\'; |
227 | s << ch; |
228 | } |
229 | s << '"'; |
230 | } |
231 | |
232 | void DumpBinaryEscaped(std::ostream &s) const override { |
233 | s << '"'; |
234 | const size_t strsize = m_value.size(); |
235 | for (size_t i = 0; i < strsize; ++i) { |
236 | char ch = m_value[i]; |
237 | if (ch == '"') |
238 | s << '\\'; |
239 | // gdb remote serial protocol binary escaping |
240 | if (ch == '#' || ch == '$' || ch == '}' || ch == '*') { |
241 | s << '}'; // 0x7d next character is escaped |
242 | s << static_cast<char>(ch ^ 0x20); |
243 | } else { |
244 | s << ch; |
245 | } |
246 | } |
247 | s << '"'; |
248 | } |
249 | |
250 | protected: |
251 | std::string m_value; |
252 | }; |
253 | |
254 | class Dictionary : public Object { |
255 | public: |
256 | Dictionary() : Object(Type::eTypeDictionary), m_dict() {} |
257 | |
258 | virtual ~Dictionary() {} |
259 | |
260 | void AddItem(std::string key, ObjectSP value) { |
261 | m_dict.push_back(x: Pair(key, value)); |
262 | } |
263 | |
264 | void AddIntegerItem(std::string key, uint64_t value) { |
265 | AddItem(key, value: ObjectSP(new Integer(value))); |
266 | } |
267 | |
268 | void AddFloatItem(std::string key, double value) { |
269 | AddItem(key, value: ObjectSP(new Float(value))); |
270 | } |
271 | |
272 | void AddStringItem(std::string key, std::string value) { |
273 | AddItem(key, value: ObjectSP(new String(std::move(value)))); |
274 | } |
275 | |
276 | void AddBytesAsHexASCIIString(std::string key, const uint8_t *src, |
277 | size_t src_len) { |
278 | if (src && src_len) { |
279 | std::ostringstream strm; |
280 | for (size_t i = 0; i < src_len; i++) |
281 | strm << std::setfill('0') << std::hex << std::right << std::setw(2) |
282 | << ((uint32_t)(src[i])); |
283 | AddItem(key, value: ObjectSP(new String(std::move(strm.str())))); |
284 | } else { |
285 | AddItem(key, value: ObjectSP(new String())); |
286 | } |
287 | } |
288 | |
289 | void AddBooleanItem(std::string key, bool value) { |
290 | AddItem(key, value: ObjectSP(new Boolean(value))); |
291 | } |
292 | |
293 | void Dump(std::ostream &s) const override { |
294 | bool have_printed_one_elem = false; |
295 | s << "{" ; |
296 | for (collection::const_iterator iter = m_dict.begin(); |
297 | iter != m_dict.end(); ++iter) { |
298 | if (!have_printed_one_elem) { |
299 | have_printed_one_elem = true; |
300 | } else { |
301 | s << "," ; |
302 | } |
303 | s << "\"" << iter->first.c_str() << "\":" ; |
304 | iter->second->Dump(s); |
305 | } |
306 | s << "}" ; |
307 | } |
308 | |
309 | void DumpBinaryEscaped(std::ostream &s) const override { |
310 | bool have_printed_one_elem = false; |
311 | s << "{" ; |
312 | for (collection::const_iterator iter = m_dict.begin(); |
313 | iter != m_dict.end(); ++iter) { |
314 | if (!have_printed_one_elem) { |
315 | have_printed_one_elem = true; |
316 | } else { |
317 | s << "," ; |
318 | } |
319 | s << "\"" << binary_encode_string(s: iter->first) << "\":" ; |
320 | iter->second->DumpBinaryEscaped(s); |
321 | } |
322 | // '}' must be escaped for the gdb remote serial |
323 | // protocol. |
324 | s << "}" ; |
325 | s << static_cast<char>('}' ^ 0x20); |
326 | } |
327 | |
328 | protected: |
329 | std::string binary_encode_string(const std::string &s) const { |
330 | std::string output; |
331 | const size_t s_size = s.size(); |
332 | const char *s_chars = s.c_str(); |
333 | |
334 | for (size_t i = 0; i < s_size; i++) { |
335 | unsigned char ch = *(s_chars + i); |
336 | if (ch == '#' || ch == '$' || ch == '}' || ch == '*') { |
337 | output.push_back(c: '}'); // 0x7d |
338 | output.push_back(c: ch ^ 0x20); |
339 | } else { |
340 | output.push_back(c: ch); |
341 | } |
342 | } |
343 | return output; |
344 | } |
345 | |
346 | // Keep the dictionary as a vector so the dictionary doesn't reorder itself |
347 | // when you dump it |
348 | // We aren't accessing keys by name, so this won't affect performance |
349 | typedef std::pair<std::string, ObjectSP> Pair; |
350 | typedef std::vector<Pair> collection; |
351 | collection m_dict; |
352 | }; |
353 | |
354 | class Null : public Object { |
355 | public: |
356 | Null() : Object(Type::eTypeNull) {} |
357 | |
358 | virtual ~Null() {} |
359 | |
360 | bool IsValid() const override { return false; } |
361 | |
362 | void Dump(std::ostream &s) const override { s << "null" ; } |
363 | |
364 | void DumpBinaryEscaped(std::ostream &s) const override { Dump(s); } |
365 | |
366 | protected: |
367 | }; |
368 | |
369 | class Generic : public Object { |
370 | public: |
371 | explicit Generic(void *object = nullptr) |
372 | : Object(Type::eTypeGeneric), m_object(object) {} |
373 | |
374 | void SetValue(void *value) { m_object = value; } |
375 | |
376 | void *GetValue() const { return m_object; } |
377 | |
378 | bool IsValid() const override { return m_object != nullptr; } |
379 | |
380 | void Dump(std::ostream &s) const override; |
381 | |
382 | void DumpBinaryEscaped(std::ostream &s) const override; |
383 | |
384 | private: |
385 | void *m_object; |
386 | }; |
387 | |
388 | }; // class JSONGenerator |
389 | |
390 | #endif // LLDB_TOOLS_DEBUGSERVER_SOURCE_JSONGENERATOR_H |
391 | |