//===--------------------- JSON.cpp -----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "JSON.h" // C includes #include #include // C++ includes #include "lldb/Host/StringConvert.h" #include #include using namespace lldb_private; std::string JSONString::json_string_quote_metachars(const std::string &s) { if (s.find('"') == std::string::npos) return s; 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 == '"') { output.push_back('\\'); } output.push_back(ch); } return output; } JSONString::JSONString() : JSONValue(JSONValue::Kind::String), m_data() {} JSONString::JSONString(const char *s) : JSONValue(JSONValue::Kind::String), m_data(s ? s : "") {} JSONString::JSONString(const std::string &s) : JSONValue(JSONValue::Kind::String), m_data(s) {} void JSONString::Write(std::ostream &s) { s << "\"" << json_string_quote_metachars(m_data).c_str() << "\""; } uint64_t JSONNumber::GetAsUnsigned() const { switch (m_data_type) { case DataType::Unsigned: return m_data.m_unsigned; case DataType::Signed: return (uint64_t)m_data.m_signed; case DataType::Double: return (uint64_t)m_data.m_double; } } int64_t JSONNumber::GetAsSigned() const { switch (m_data_type) { case DataType::Unsigned: return (int64_t)m_data.m_unsigned; case DataType::Signed: return m_data.m_signed; case DataType::Double: return (int64_t)m_data.m_double; } } double JSONNumber::GetAsDouble() const { switch (m_data_type) { case DataType::Unsigned: return (double)m_data.m_unsigned; case DataType::Signed: return (double)m_data.m_signed; case DataType::Double: return m_data.m_double; } } void JSONNumber::Write(std::ostream &s) { switch (m_data_type) { case DataType::Unsigned: s << m_data.m_unsigned; break; case DataType::Signed: s << m_data.m_signed; break; case DataType::Double: // Set max precision to emulate %g. s << std::setprecision(std::numeric_limits::digits10 + 1); s << m_data.m_double; break; } } JSONTrue::JSONTrue() : JSONValue(JSONValue::Kind::True) {} void JSONTrue::Write(std::ostream &s) { s << "true"; } JSONFalse::JSONFalse() : JSONValue(JSONValue::Kind::False) {} void JSONFalse::Write(std::ostream &s) { s << "false"; } JSONNull::JSONNull() : JSONValue(JSONValue::Kind::Null) {} void JSONNull::Write(std::ostream &s) { s << "null"; } JSONObject::JSONObject() : JSONValue(JSONValue::Kind::Object) {} void JSONObject::Write(std::ostream &s) { bool first = true; s << '{'; auto iter = m_elements.begin(), end = m_elements.end(); for (; iter != end; iter++) { if (first) first = false; else s << ','; JSONString key(iter->first); JSONValue::SP value(iter->second); key.Write(s); s << ':'; value->Write(s); } s << '}'; } bool JSONObject::SetObject(const std::string &key, JSONValue::SP value) { if (key.empty() || nullptr == value.get()) return false; m_elements[key] = value; return true; } JSONValue::SP JSONObject::GetObject(const std::string &key) const { auto iter = m_elements.find(key), end = m_elements.end(); if (iter == end) return JSONValue::SP(); return iter->second; } bool JSONObject::GetObjectAsBool(const std::string &key, bool &value) const { auto value_sp = GetObject(key); if (!value_sp) { // The given key doesn't exist, so we have no value. return false; } if (JSONTrue::classof(value_sp.get())) { // We have the value, and it is true. value = true; return true; } else if (JSONFalse::classof(value_sp.get())) { // We have the value, and it is false. value = false; return true; } else { // We don't have a valid bool value for the given key. return false; } } bool JSONObject::GetObjectAsString(const std::string &key, std::string &value) const { auto value_sp = GetObject(key); if (!value_sp) { // The given key doesn't exist, so we have no value. return false; } if (!JSONString::classof(value_sp.get())) return false; value = static_cast(value_sp.get())->GetData(); return true; } JSONArray::JSONArray() : JSONValue(JSONValue::Kind::Array) {} void JSONArray::Write(std::ostream &s) { bool first = true; s << '['; auto iter = m_elements.begin(), end = m_elements.end(); for (; iter != end; iter++) { if (first) first = false; else s << ','; (*iter)->Write(s); } s << ']'; } bool JSONArray::SetObject(Index i, JSONValue::SP value) { if (value.get() == nullptr) return false; if (i < m_elements.size()) { m_elements[i] = value; return true; } if (i == m_elements.size()) { m_elements.push_back(value); return true; } return false; } bool JSONArray::AppendObject(JSONValue::SP value) { if (value.get() == nullptr) return false; m_elements.push_back(value); return true; } JSONValue::SP JSONArray::GetObject(Index i) { if (i < m_elements.size()) return m_elements[i]; return JSONValue::SP(); } JSONArray::Size JSONArray::GetNumElements() { return m_elements.size(); } JSONParser::JSONParser(const char *cstr) : StdStringExtractor(cstr) {} JSONParser::Token JSONParser::GetToken(std::string &value) { std::ostringstream error; value.clear(); SkipSpaces(); const uint64_t start_index = m_index; const char ch = GetChar(); switch (ch) { case '{': return Token::ObjectStart; case '}': return Token::ObjectEnd; case '[': return Token::ArrayStart; case ']': return Token::ArrayEnd; case ',': return Token::Comma; case ':': return Token::Colon; case '\0': return Token::EndOfFile; case 't': if (GetChar() == 'r') if (GetChar() == 'u') if (GetChar() == 'e') return Token::True; break; case 'f': if (GetChar() == 'a') if (GetChar() == 'l') if (GetChar() == 's') if (GetChar() == 'e') return Token::False; break; case 'n': if (GetChar() == 'u') if (GetChar() == 'l') if (GetChar() == 'l') return Token::Null; break; case '"': { while (1) { bool was_escaped = false; int escaped_ch = GetEscapedChar(was_escaped); if (escaped_ch == -1) { error << "error: an error occurred getting a character from offset " << start_index; value = error.str(); return Token::Status; } else { const bool is_end_quote = escaped_ch == '"'; const bool is_null = escaped_ch == 0; if (was_escaped || (!is_end_quote && !is_null)) { if (CHAR_MIN <= escaped_ch && escaped_ch <= CHAR_MAX) { value.append(1, (char)escaped_ch); } else { error << "error: wide character support is needed for unicode " "character 0x" << std::setprecision(4) << std::hex << escaped_ch; error << " at offset " << start_index; value = error.str(); return Token::Status; } } else if (is_end_quote) { return Token::String; } else if (is_null) { value = "error: missing end quote for string"; return Token::Status; } } } } break; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { bool done = false; bool got_decimal_point = false; uint64_t exp_index = 0; bool got_int_digits = (ch >= '0') && (ch <= '9'); bool got_frac_digits = false; bool got_exp_digits = false; while (!done) { const char next_ch = PeekChar(); switch (next_ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (exp_index != 0) { got_exp_digits = true; } else if (got_decimal_point) { got_frac_digits = true; } else { got_int_digits = true; } ++m_index; // Skip this character break; case '.': if (got_decimal_point) { error << "error: extra decimal point found at offset " << start_index; value = error.str(); return Token::Status; } else { got_decimal_point = true; ++m_index; // Skip this character } break; case 'e': case 'E': if (exp_index != 0) { error << "error: extra exponent character found at offset " << start_index; value = error.str(); return Token::Status; } else { exp_index = m_index; ++m_index; // Skip this character } break; case '+': case '-': // The '+' and '-' can only come after an exponent character... if (exp_index == m_index - 1) { ++m_index; // Skip the exponent sign character } else { error << "error: unexpected " << next_ch << " character at offset " << start_index; value = error.str(); return Token::Status; } break; default: done = true; break; } } if (m_index > start_index) { value = m_packet.substr(start_index, m_index - start_index); if (got_decimal_point) { if (exp_index != 0) { // We have an exponent, make sure we got exponent digits if (got_exp_digits) { return Token::Float; } else { error << "error: got exponent character but no exponent digits at " "offset in float value \"" << value.c_str() << "\""; value = error.str(); return Token::Status; } } else { // No exponent, but we need at least one decimal after the decimal // point if (got_frac_digits) { return Token::Float; } else { error << "error: no digits after decimal point \"" << value.c_str() << "\""; value = error.str(); return Token::Status; } } } else { // No decimal point if (got_int_digits) { // We need at least some integer digits to make an integer return Token::Integer; } else { error << "error: no digits negate sign \"" << value.c_str() << "\""; value = error.str(); return Token::Status; } } } else { error << "error: invalid number found at offset " << start_index; value = error.str(); return Token::Status; } } break; default: break; } error << "error: failed to parse token at offset " << start_index << " (around character '" << ch << "')"; value = error.str(); return Token::Status; } int JSONParser::GetEscapedChar(bool &was_escaped) { was_escaped = false; const char ch = GetChar(); if (ch == '\\') { was_escaped = true; const char ch2 = GetChar(); switch (ch2) { case '"': case '\\': case '/': default: break; case 'b': return '\b'; case 'f': return '\f'; case 'n': return '\n'; case 'r': return '\r'; case 't': return '\t'; case 'u': { const int hi_byte = DecodeHexU8(); const int lo_byte = DecodeHexU8(); if (hi_byte >= 0 && lo_byte >= 0) return hi_byte << 8 | lo_byte; return -1; } break; } return ch2; } return ch; } JSONValue::SP JSONParser::ParseJSONObject() { // The "JSONParser::Token::ObjectStart" token should have already been // consumed // by the time this function is called std::unique_ptr dict_up(new JSONObject()); std::string value; std::string key; while (1) { JSONParser::Token token = GetToken(value); if (token == JSONParser::Token::String) { key.swap(value); token = GetToken(value); if (token == JSONParser::Token::Colon) { JSONValue::SP value_sp = ParseJSONValue(); if (value_sp) dict_up->SetObject(key, value_sp); else break; } } else if (token == JSONParser::Token::ObjectEnd) { return JSONValue::SP(dict_up.release()); } else if (token == JSONParser::Token::Comma) { continue; } else { break; } } return JSONValue::SP(); } JSONValue::SP JSONParser::ParseJSONArray() { // The "JSONParser::Token::ObjectStart" token should have already been // consumed // by the time this function is called std::unique_ptr array_up(new JSONArray()); std::string value; std::string key; while (1) { JSONValue::SP value_sp = ParseJSONValue(); if (value_sp) array_up->AppendObject(value_sp); else break; JSONParser::Token token = GetToken(value); if (token == JSONParser::Token::Comma) { continue; } else if (token == JSONParser::Token::ArrayEnd) { return JSONValue::SP(array_up.release()); } else { break; } } return JSONValue::SP(); } JSONValue::SP JSONParser::ParseJSONValue() { std::string value; const JSONParser::Token token = GetToken(value); switch (token) { case JSONParser::Token::ObjectStart: return ParseJSONObject(); case JSONParser::Token::ArrayStart: return ParseJSONArray(); case JSONParser::Token::Integer: { if (value.front() == '-') { bool success = false; int64_t sval = StringConvert::ToSInt64(value.c_str(), 0, 0, &success); if (success) return JSONValue::SP(new JSONNumber(sval)); } else { bool success = false; uint64_t uval = StringConvert::ToUInt64(value.c_str(), 0, 0, &success); if (success) return JSONValue::SP(new JSONNumber(uval)); } } break; case JSONParser::Token::Float: { bool success = false; double val = StringConvert::ToDouble(value.c_str(), 0.0, &success); if (success) return JSONValue::SP(new JSONNumber(val)); } break; case JSONParser::Token::String: return JSONValue::SP(new JSONString(value)); case JSONParser::Token::True: return JSONValue::SP(new JSONTrue()); case JSONParser::Token::False: return JSONValue::SP(new JSONFalse()); case JSONParser::Token::Null: return JSONValue::SP(new JSONNull()); default: break; } return JSONValue::SP(); }