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