586 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			586 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--------------------- 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 <assert.h>
 | 
						|
#include <limits.h>
 | 
						|
 | 
						|
// C++ includes
 | 
						|
#include "lldb/Host/StringConvert.h"
 | 
						|
#include <iomanip>
 | 
						|
#include <sstream>
 | 
						|
 | 
						|
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<double>::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<JSONString *>(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<JSONObject> 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<JSONArray> 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();
 | 
						|
}
 |