1//===--------------------- JSON.cpp -----------------------------*- 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#include "JSON.h"
10
11// C includes
12#include <cassert>
13#include <climits>
14
15// C++ includes
16#include "StringConvert.h"
17#include <iomanip>
18#include <sstream>
19
20std::string JSONString::json_string_quote_metachars(const std::string &s) {
21 if (s.find(c: '"') == std::string::npos)
22 return s;
23
24 std::string output;
25 const size_t s_size = s.size();
26 const char *s_chars = s.c_str();
27 for (size_t i = 0; i < s_size; i++) {
28 unsigned char ch = *(s_chars + i);
29 if (ch == '"') {
30 output.push_back(c: '\\');
31 }
32 output.push_back(c: ch);
33 }
34 return output;
35}
36
37JSONString::JSONString() : JSONValue(JSONValue::Kind::String), m_data() {}
38
39JSONString::JSONString(const char *s)
40 : JSONValue(JSONValue::Kind::String), m_data(s ? s : "") {}
41
42JSONString::JSONString(const std::string &s)
43 : JSONValue(JSONValue::Kind::String), m_data(s) {}
44
45void JSONString::Write(std::ostream &s) {
46 s << "\"" << json_string_quote_metachars(s: m_data).c_str() << "\"";
47}
48
49uint64_t JSONNumber::GetAsUnsigned() const {
50 switch (m_data_type) {
51 case DataType::Unsigned:
52 return m_data.m_unsigned;
53 case DataType::Signed:
54 return (uint64_t)m_data.m_signed;
55 case DataType::Double:
56 return (uint64_t)m_data.m_double;
57 }
58}
59
60int64_t JSONNumber::GetAsSigned() const {
61 switch (m_data_type) {
62 case DataType::Unsigned:
63 return (int64_t)m_data.m_unsigned;
64 case DataType::Signed:
65 return m_data.m_signed;
66 case DataType::Double:
67 return (int64_t)m_data.m_double;
68 }
69}
70
71double JSONNumber::GetAsDouble() const {
72 switch (m_data_type) {
73 case DataType::Unsigned:
74 return (double)m_data.m_unsigned;
75 case DataType::Signed:
76 return (double)m_data.m_signed;
77 case DataType::Double:
78 return m_data.m_double;
79 }
80}
81
82void JSONNumber::Write(std::ostream &s) {
83 switch (m_data_type) {
84 case DataType::Unsigned:
85 s << m_data.m_unsigned;
86 break;
87 case DataType::Signed:
88 s << m_data.m_signed;
89 break;
90 case DataType::Double:
91 // Set max precision to emulate %g.
92 s << std::setprecision(std::numeric_limits<double>::digits10 + 1);
93 s << m_data.m_double;
94 break;
95 }
96}
97
98JSONTrue::JSONTrue() : JSONValue(JSONValue::Kind::True) {}
99
100void JSONTrue::Write(std::ostream &s) { s << "true"; }
101
102JSONFalse::JSONFalse() : JSONValue(JSONValue::Kind::False) {}
103
104void JSONFalse::Write(std::ostream &s) { s << "false"; }
105
106JSONNull::JSONNull() : JSONValue(JSONValue::Kind::Null) {}
107
108void JSONNull::Write(std::ostream &s) { s << "null"; }
109
110JSONObject::JSONObject() : JSONValue(JSONValue::Kind::Object) {}
111
112void JSONObject::Write(std::ostream &s) {
113 bool first = true;
114 s << '{';
115 auto iter = m_elements.begin(), end = m_elements.end();
116 for (; iter != end; iter++) {
117 if (first)
118 first = false;
119 else
120 s << ',';
121 JSONString key(iter->first);
122 JSONValue::SP value(iter->second);
123 key.Write(s);
124 s << ':';
125 value->Write(s);
126 }
127 s << '}';
128}
129
130bool JSONObject::SetObject(const std::string &key, JSONValue::SP value) {
131 if (key.empty() || nullptr == value.get())
132 return false;
133 m_elements[key] = value;
134 return true;
135}
136
137JSONValue::SP JSONObject::GetObject(const std::string &key) const {
138 auto iter = m_elements.find(x: key), end = m_elements.end();
139 if (iter == end)
140 return JSONValue::SP();
141 return iter->second;
142}
143
144bool JSONObject::GetObjectAsBool(const std::string &key, bool &value) const {
145 auto value_sp = GetObject(key);
146 if (!value_sp) {
147 // The given key doesn't exist, so we have no value.
148 return false;
149 }
150
151 if (JSONTrue::classof(V: value_sp.get())) {
152 // We have the value, and it is true.
153 value = true;
154 return true;
155 } else if (JSONFalse::classof(V: value_sp.get())) {
156 // We have the value, and it is false.
157 value = false;
158 return true;
159 } else {
160 // We don't have a valid bool value for the given key.
161 return false;
162 }
163}
164
165bool JSONObject::GetObjectAsString(const std::string &key,
166 std::string &value) const {
167 auto value_sp = GetObject(key);
168 if (!value_sp) {
169 // The given key doesn't exist, so we have no value.
170 return false;
171 }
172
173 if (!JSONString::classof(V: value_sp.get()))
174 return false;
175
176 value = static_cast<JSONString *>(value_sp.get())->GetData();
177 return true;
178}
179
180JSONArray::JSONArray() : JSONValue(JSONValue::Kind::Array) {}
181
182void JSONArray::Write(std::ostream &s) {
183 bool first = true;
184 s << '[';
185 auto iter = m_elements.begin(), end = m_elements.end();
186 for (; iter != end; iter++) {
187 if (first)
188 first = false;
189 else
190 s << ',';
191 (*iter)->Write(s);
192 }
193 s << ']';
194}
195
196bool JSONArray::SetObject(Index i, JSONValue::SP value) {
197 if (value.get() == nullptr)
198 return false;
199 if (i < m_elements.size()) {
200 m_elements[i] = value;
201 return true;
202 }
203 if (i == m_elements.size()) {
204 m_elements.push_back(x: value);
205 return true;
206 }
207 return false;
208}
209
210bool JSONArray::AppendObject(JSONValue::SP value) {
211 if (value.get() == nullptr)
212 return false;
213 m_elements.push_back(x: value);
214 return true;
215}
216
217JSONValue::SP JSONArray::GetObject(Index i) {
218 if (i < m_elements.size())
219 return m_elements[i];
220 return JSONValue::SP();
221}
222
223JSONArray::Size JSONArray::GetNumElements() { return m_elements.size(); }
224
225JSONParser::JSONParser(const char *cstr) : StdStringExtractor(cstr) {}
226
227JSONParser::Token JSONParser::GetToken(std::string &value) {
228 std::ostringstream error;
229
230 value.clear();
231 SkipSpaces();
232 const uint64_t start_index = m_index;
233 const char ch = GetChar();
234 switch (ch) {
235 case '{':
236 return Token::ObjectStart;
237 case '}':
238 return Token::ObjectEnd;
239 case '[':
240 return Token::ArrayStart;
241 case ']':
242 return Token::ArrayEnd;
243 case ',':
244 return Token::Comma;
245 case ':':
246 return Token::Colon;
247 case '\0':
248 return Token::EndOfFile;
249 case 't':
250 if (GetChar() == 'r')
251 if (GetChar() == 'u')
252 if (GetChar() == 'e')
253 return Token::True;
254 break;
255
256 case 'f':
257 if (GetChar() == 'a')
258 if (GetChar() == 'l')
259 if (GetChar() == 's')
260 if (GetChar() == 'e')
261 return Token::False;
262 break;
263
264 case 'n':
265 if (GetChar() == 'u')
266 if (GetChar() == 'l')
267 if (GetChar() == 'l')
268 return Token::Null;
269 break;
270
271 case '"': {
272 while (true) {
273 bool was_escaped = false;
274 int escaped_ch = GetEscapedChar(was_escaped);
275 if (escaped_ch == -1) {
276 error << "error: an error occurred getting a character from offset "
277 << start_index;
278 value = error.str();
279 return Token::Status;
280
281 } else {
282 const bool is_end_quote = escaped_ch == '"';
283 const bool is_null = escaped_ch == 0;
284 if (was_escaped || (!is_end_quote && !is_null)) {
285 if (CHAR_MIN <= escaped_ch && escaped_ch <= CHAR_MAX) {
286 value.append(n: 1, c: (char)escaped_ch);
287 } else {
288 error << "error: wide character support is needed for unicode "
289 "character 0x"
290 << std::setprecision(4) << std::hex << escaped_ch;
291 error << " at offset " << start_index;
292 value = error.str();
293 return Token::Status;
294 }
295 } else if (is_end_quote) {
296 return Token::String;
297 } else if (is_null) {
298 value = "error: missing end quote for string";
299 return Token::Status;
300 }
301 }
302 }
303 } break;
304
305 case '-':
306 case '0':
307 case '1':
308 case '2':
309 case '3':
310 case '4':
311 case '5':
312 case '6':
313 case '7':
314 case '8':
315 case '9': {
316 bool done = false;
317 bool got_decimal_point = false;
318 uint64_t exp_index = 0;
319 bool got_int_digits = (ch >= '0') && (ch <= '9');
320 bool got_frac_digits = false;
321 bool got_exp_digits = false;
322 while (!done) {
323 const char next_ch = PeekChar();
324 switch (next_ch) {
325 case '0':
326 case '1':
327 case '2':
328 case '3':
329 case '4':
330 case '5':
331 case '6':
332 case '7':
333 case '8':
334 case '9':
335 if (exp_index != 0) {
336 got_exp_digits = true;
337 } else if (got_decimal_point) {
338 got_frac_digits = true;
339 } else {
340 got_int_digits = true;
341 }
342 ++m_index; // Skip this character
343 break;
344
345 case '.':
346 if (got_decimal_point) {
347 error << "error: extra decimal point found at offset " << start_index;
348 value = error.str();
349 return Token::Status;
350 } else {
351 got_decimal_point = true;
352 ++m_index; // Skip this character
353 }
354 break;
355
356 case 'e':
357 case 'E':
358 if (exp_index != 0) {
359 error << "error: extra exponent character found at offset "
360 << start_index;
361 value = error.str();
362 return Token::Status;
363 } else {
364 exp_index = m_index;
365 ++m_index; // Skip this character
366 }
367 break;
368
369 case '+':
370 case '-':
371 // The '+' and '-' can only come after an exponent character...
372 if (exp_index == m_index - 1) {
373 ++m_index; // Skip the exponent sign character
374 } else {
375 error << "error: unexpected " << next_ch << " character at offset "
376 << start_index;
377 value = error.str();
378 return Token::Status;
379 }
380 break;
381
382 default:
383 done = true;
384 break;
385 }
386 }
387
388 if (m_index > start_index) {
389 value = m_packet.substr(pos: start_index, n: m_index - start_index);
390 if (got_decimal_point) {
391 if (exp_index != 0) {
392 // We have an exponent, make sure we got exponent digits
393 if (got_exp_digits) {
394 return Token::Float;
395 } else {
396 error << "error: got exponent character but no exponent digits at "
397 "offset in float value \""
398 << value.c_str() << "\"";
399 value = error.str();
400 return Token::Status;
401 }
402 } else {
403 // No exponent, but we need at least one decimal after the decimal
404 // point
405 if (got_frac_digits) {
406 return Token::Float;
407 } else {
408 error << "error: no digits after decimal point \"" << value.c_str()
409 << "\"";
410 value = error.str();
411 return Token::Status;
412 }
413 }
414 } else {
415 // No decimal point
416 if (got_int_digits) {
417 // We need at least some integer digits to make an integer
418 return Token::Integer;
419 } else {
420 error << "error: no digits negate sign \"" << value.c_str() << "\"";
421 value = error.str();
422 return Token::Status;
423 }
424 }
425 } else {
426 error << "error: invalid number found at offset " << start_index;
427 value = error.str();
428 return Token::Status;
429 }
430 } break;
431 default:
432 break;
433 }
434 error << "error: failed to parse token at offset " << start_index
435 << " (around character '" << ch << "')";
436 value = error.str();
437 return Token::Status;
438}
439
440int JSONParser::GetEscapedChar(bool &was_escaped) {
441 was_escaped = false;
442 const char ch = GetChar();
443 if (ch == '\\') {
444 was_escaped = true;
445 const char ch2 = GetChar();
446 switch (ch2) {
447 case '"':
448 case '\\':
449 case '/':
450 default:
451 break;
452
453 case 'b':
454 return '\b';
455 case 'f':
456 return '\f';
457 case 'n':
458 return '\n';
459 case 'r':
460 return '\r';
461 case 't':
462 return '\t';
463 case 'u': {
464 const int hi_byte = DecodeHexU8();
465 const int lo_byte = DecodeHexU8();
466 if (hi_byte >= 0 && lo_byte >= 0)
467 return hi_byte << 8 | lo_byte;
468 return -1;
469 } break;
470 }
471 return ch2;
472 }
473 return ch;
474}
475
476JSONValue::SP JSONParser::ParseJSONObject() {
477 // The "JSONParser::Token::ObjectStart" token should have already been
478 // consumed
479 // by the time this function is called
480 std::unique_ptr<JSONObject> dict_up(new JSONObject());
481
482 std::string value;
483 std::string key;
484 while (true) {
485 JSONParser::Token token = GetToken(value);
486
487 if (token == JSONParser::Token::String) {
488 key.swap(s&: value);
489 token = GetToken(value);
490 if (token == JSONParser::Token::Colon) {
491 JSONValue::SP value_sp = ParseJSONValue();
492 if (value_sp)
493 dict_up->SetObject(key, value: value_sp);
494 else
495 break;
496 }
497 } else if (token == JSONParser::Token::ObjectEnd) {
498 return JSONValue::SP(dict_up.release());
499 } else if (token == JSONParser::Token::Comma) {
500 continue;
501 } else {
502 break;
503 }
504 }
505 return JSONValue::SP();
506}
507
508JSONValue::SP JSONParser::ParseJSONArray() {
509 // The "JSONParser::Token::ObjectStart" token should have already been
510 // consumed
511 // by the time this function is called
512 std::unique_ptr<JSONArray> array_up(new JSONArray());
513
514 std::string value;
515 std::string key;
516 while (true) {
517 JSONParser::Token token = GetToken(value);
518 if (token == JSONParser::Token::ArrayEnd)
519 return JSONValue::SP(array_up.release());
520 JSONValue::SP value_sp = ParseJSONValue(value, token);
521 if (value_sp)
522 array_up->AppendObject(value: value_sp);
523 else
524 break;
525
526 token = GetToken(value);
527 if (token == JSONParser::Token::Comma) {
528 continue;
529 } else if (token == JSONParser::Token::ArrayEnd) {
530 return JSONValue::SP(array_up.release());
531 } else {
532 break;
533 }
534 }
535 return JSONValue::SP();
536}
537
538JSONValue::SP JSONParser::ParseJSONValue() {
539 std::string value;
540 const JSONParser::Token token = GetToken(value);
541 return ParseJSONValue(value, token);
542}
543
544JSONValue::SP JSONParser::ParseJSONValue(const std::string &value,
545 const Token &token) {
546 switch (token) {
547 case JSONParser::Token::ObjectStart:
548 return ParseJSONObject();
549
550 case JSONParser::Token::ArrayStart:
551 return ParseJSONArray();
552
553 case JSONParser::Token::Integer: {
554 if (value.front() == '-') {
555 bool success = false;
556 int64_t sval = StringConvert::ToSInt64(s: value.c_str(), fail_value: 0, base: 0, success_ptr: &success);
557 if (success)
558 return JSONValue::SP(new JSONNumber(sval));
559 } else {
560 bool success = false;
561 uint64_t uval = StringConvert::ToUInt64(s: value.c_str(), fail_value: 0, base: 0, success_ptr: &success);
562 if (success)
563 return JSONValue::SP(new JSONNumber(uval));
564 }
565 } break;
566
567 case JSONParser::Token::Float: {
568 bool success = false;
569 double val = StringConvert::ToDouble(s: value.c_str(), fail_value: 0.0, success_ptr: &success);
570 if (success)
571 return JSONValue::SP(new JSONNumber(val));
572 } break;
573
574 case JSONParser::Token::String:
575 return JSONValue::SP(new JSONString(value));
576
577 case JSONParser::Token::True:
578 return JSONValue::SP(new JSONTrue());
579
580 case JSONParser::Token::False:
581 return JSONValue::SP(new JSONFalse());
582
583 case JSONParser::Token::Null:
584 return JSONValue::SP(new JSONNull());
585
586 default:
587 break;
588 }
589 return JSONValue::SP();
590}
591

source code of lldb/tools/debugserver/source/JSON.cpp