xref: /openbsd-src/gnu/llvm/lldb/tools/debugserver/source/JSON.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
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 
json_string_quote_metachars(const std::string & s)20 std::string JSONString::json_string_quote_metachars(const std::string &s) {
21   if (s.find('"') == 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('\\');
31     }
32     output.push_back(ch);
33   }
34   return output;
35 }
36 
JSONString()37 JSONString::JSONString() : JSONValue(JSONValue::Kind::String), m_data() {}
38 
JSONString(const char * s)39 JSONString::JSONString(const char *s)
40     : JSONValue(JSONValue::Kind::String), m_data(s ? s : "") {}
41 
JSONString(const std::string & s)42 JSONString::JSONString(const std::string &s)
43     : JSONValue(JSONValue::Kind::String), m_data(s) {}
44 
Write(std::ostream & s)45 void JSONString::Write(std::ostream &s) {
46   s << "\"" << json_string_quote_metachars(m_data).c_str() << "\"";
47 }
48 
GetAsUnsigned() const49 uint64_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 
GetAsSigned() const60 int64_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 
GetAsDouble() const71 double 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 
Write(std::ostream & s)82 void 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 
JSONTrue()98 JSONTrue::JSONTrue() : JSONValue(JSONValue::Kind::True) {}
99 
Write(std::ostream & s)100 void JSONTrue::Write(std::ostream &s) { s << "true"; }
101 
JSONFalse()102 JSONFalse::JSONFalse() : JSONValue(JSONValue::Kind::False) {}
103 
Write(std::ostream & s)104 void JSONFalse::Write(std::ostream &s) { s << "false"; }
105 
JSONNull()106 JSONNull::JSONNull() : JSONValue(JSONValue::Kind::Null) {}
107 
Write(std::ostream & s)108 void JSONNull::Write(std::ostream &s) { s << "null"; }
109 
JSONObject()110 JSONObject::JSONObject() : JSONValue(JSONValue::Kind::Object) {}
111 
Write(std::ostream & s)112 void 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 
SetObject(const std::string & key,JSONValue::SP value)130 bool 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 
GetObject(const std::string & key) const137 JSONValue::SP JSONObject::GetObject(const std::string &key) const {
138   auto iter = m_elements.find(key), end = m_elements.end();
139   if (iter == end)
140     return JSONValue::SP();
141   return iter->second;
142 }
143 
GetObjectAsBool(const std::string & key,bool & value) const144 bool 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(value_sp.get())) {
152     // We have the value, and it is true.
153     value = true;
154     return true;
155   } else if (JSONFalse::classof(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 
GetObjectAsString(const std::string & key,std::string & value) const165 bool 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(value_sp.get()))
174     return false;
175 
176   value = static_cast<JSONString *>(value_sp.get())->GetData();
177   return true;
178 }
179 
JSONArray()180 JSONArray::JSONArray() : JSONValue(JSONValue::Kind::Array) {}
181 
Write(std::ostream & s)182 void 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 
SetObject(Index i,JSONValue::SP value)196 bool 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(value);
205     return true;
206   }
207   return false;
208 }
209 
AppendObject(JSONValue::SP value)210 bool JSONArray::AppendObject(JSONValue::SP value) {
211   if (value.get() == nullptr)
212     return false;
213   m_elements.push_back(value);
214   return true;
215 }
216 
GetObject(Index i)217 JSONValue::SP JSONArray::GetObject(Index i) {
218   if (i < m_elements.size())
219     return m_elements[i];
220   return JSONValue::SP();
221 }
222 
GetNumElements()223 JSONArray::Size JSONArray::GetNumElements() { return m_elements.size(); }
224 
JSONParser(const char * cstr)225 JSONParser::JSONParser(const char *cstr) : StdStringExtractor(cstr) {}
226 
GetToken(std::string & value)227 JSONParser::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(1, (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(start_index, 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 
GetEscapedChar(bool & was_escaped)440 int 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 
ParseJSONObject()476 JSONValue::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(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_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 
ParseJSONArray()508 JSONValue::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_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 
ParseJSONValue()538 JSONValue::SP JSONParser::ParseJSONValue() {
539   std::string value;
540   const JSONParser::Token token = GetToken(value);
541   return ParseJSONValue(value, token);
542 }
543 
ParseJSONValue(const std::string & value,const Token & token)544 JSONValue::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(value.c_str(), 0, 0, &success);
557       if (success)
558         return JSONValue::SP(new JSONNumber(sval));
559     } else {
560       bool success = false;
561       uint64_t uval = StringConvert::ToUInt64(value.c_str(), 0, 0, &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(value.c_str(), 0.0, &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