forked from OSchip/llvm-project
[JSON] Remove Utility/JSON.{h|cpp}
This patch is the final step in my quest to get rid of the JSON parser in LLDB. Vedant's coverage report [1] shows that it was mostly untested. Furthermore, the LLVM implementation has a much nicer API and using it means one less thing to maintain for LLDB. [1] http://lab.llvm.org:8080/coverage/coverage-reports/index.html Differential revision: https://reviews.llvm.org/D68305 llvm-svn: 373501
This commit is contained in:
parent
4f8151e62e
commit
2a0c8b1143
|
@ -1,283 +0,0 @@
|
|||
//===---------------------JSON.h --------------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef utility_JSON_h_
|
||||
#define utility_JSON_h_
|
||||
|
||||
#include "lldb/Utility/StringExtractor.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace lldb_private {
|
||||
class Stream;
|
||||
|
||||
class JSONValue {
|
||||
public:
|
||||
virtual void Write(Stream &s) = 0;
|
||||
|
||||
typedef std::shared_ptr<JSONValue> SP;
|
||||
|
||||
enum class Kind { String, Number, True, False, Null, Object, Array };
|
||||
|
||||
JSONValue(Kind k) : m_kind(k) {}
|
||||
|
||||
Kind GetKind() const { return m_kind; }
|
||||
|
||||
virtual ~JSONValue() = default;
|
||||
|
||||
private:
|
||||
const Kind m_kind;
|
||||
};
|
||||
|
||||
class JSONString : public JSONValue {
|
||||
public:
|
||||
JSONString();
|
||||
JSONString(const char *s);
|
||||
JSONString(const std::string &s);
|
||||
|
||||
JSONString(const JSONString &s) = delete;
|
||||
JSONString &operator=(const JSONString &s) = delete;
|
||||
|
||||
void Write(Stream &s) override;
|
||||
|
||||
typedef std::shared_ptr<JSONString> SP;
|
||||
|
||||
std::string GetData() { return m_data; }
|
||||
|
||||
static bool classof(const JSONValue *V) {
|
||||
return V->GetKind() == JSONValue::Kind::String;
|
||||
}
|
||||
|
||||
~JSONString() override = default;
|
||||
|
||||
private:
|
||||
static std::string json_string_quote_metachars(const std::string &);
|
||||
|
||||
std::string m_data;
|
||||
};
|
||||
|
||||
class JSONNumber : public JSONValue {
|
||||
public:
|
||||
typedef std::shared_ptr<JSONNumber> SP;
|
||||
|
||||
// We cretae a constructor for all integer and floating point type with using
|
||||
// templates and
|
||||
// SFINAE to avoid having ambiguous overloads because of the implicit type
|
||||
// promotion. If we
|
||||
// would have constructors only with int64_t, uint64_t and double types then
|
||||
// constructing a JSONNumber from an int32_t (or any other similar type)
|
||||
// would fail to compile.
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
std::is_integral<T>::value &&
|
||||
std::is_unsigned<T>::value>::type * = nullptr>
|
||||
explicit JSONNumber(T u)
|
||||
: JSONValue(JSONValue::Kind::Number), m_data_type(DataType::Unsigned) {
|
||||
m_data.m_unsigned = u;
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_signed<T>::value>::type * = nullptr>
|
||||
explicit JSONNumber(T s)
|
||||
: JSONValue(JSONValue::Kind::Number), m_data_type(DataType::Signed) {
|
||||
m_data.m_signed = s;
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
std::is_floating_point<T>::value>::type * = nullptr>
|
||||
explicit JSONNumber(T d)
|
||||
: JSONValue(JSONValue::Kind::Number), m_data_type(DataType::Double) {
|
||||
m_data.m_double = d;
|
||||
}
|
||||
|
||||
~JSONNumber() override = default;
|
||||
|
||||
JSONNumber(const JSONNumber &s) = delete;
|
||||
JSONNumber &operator=(const JSONNumber &s) = delete;
|
||||
|
||||
void Write(Stream &s) override;
|
||||
|
||||
uint64_t GetAsUnsigned() const;
|
||||
|
||||
int64_t GetAsSigned() const;
|
||||
|
||||
double GetAsDouble() const;
|
||||
|
||||
static bool classof(const JSONValue *V) {
|
||||
return V->GetKind() == JSONValue::Kind::Number;
|
||||
}
|
||||
|
||||
private:
|
||||
enum class DataType : uint8_t { Unsigned, Signed, Double } m_data_type;
|
||||
|
||||
union {
|
||||
uint64_t m_unsigned;
|
||||
int64_t m_signed;
|
||||
double m_double;
|
||||
} m_data;
|
||||
};
|
||||
|
||||
class JSONTrue : public JSONValue {
|
||||
public:
|
||||
JSONTrue();
|
||||
|
||||
JSONTrue(const JSONTrue &s) = delete;
|
||||
JSONTrue &operator=(const JSONTrue &s) = delete;
|
||||
|
||||
void Write(Stream &s) override;
|
||||
|
||||
typedef std::shared_ptr<JSONTrue> SP;
|
||||
|
||||
static bool classof(const JSONValue *V) {
|
||||
return V->GetKind() == JSONValue::Kind::True;
|
||||
}
|
||||
|
||||
~JSONTrue() override = default;
|
||||
};
|
||||
|
||||
class JSONFalse : public JSONValue {
|
||||
public:
|
||||
JSONFalse();
|
||||
|
||||
JSONFalse(const JSONFalse &s) = delete;
|
||||
JSONFalse &operator=(const JSONFalse &s) = delete;
|
||||
|
||||
void Write(Stream &s) override;
|
||||
|
||||
typedef std::shared_ptr<JSONFalse> SP;
|
||||
|
||||
static bool classof(const JSONValue *V) {
|
||||
return V->GetKind() == JSONValue::Kind::False;
|
||||
}
|
||||
|
||||
~JSONFalse() override = default;
|
||||
};
|
||||
|
||||
class JSONNull : public JSONValue {
|
||||
public:
|
||||
JSONNull();
|
||||
|
||||
JSONNull(const JSONNull &s) = delete;
|
||||
JSONNull &operator=(const JSONNull &s) = delete;
|
||||
|
||||
void Write(Stream &s) override;
|
||||
|
||||
typedef std::shared_ptr<JSONNull> SP;
|
||||
|
||||
static bool classof(const JSONValue *V) {
|
||||
return V->GetKind() == JSONValue::Kind::Null;
|
||||
}
|
||||
|
||||
~JSONNull() override = default;
|
||||
};
|
||||
|
||||
class JSONObject : public JSONValue {
|
||||
public:
|
||||
JSONObject();
|
||||
|
||||
JSONObject(const JSONObject &s) = delete;
|
||||
JSONObject &operator=(const JSONObject &s) = delete;
|
||||
|
||||
void Write(Stream &s) override;
|
||||
|
||||
typedef std::shared_ptr<JSONObject> SP;
|
||||
|
||||
static bool classof(const JSONValue *V) {
|
||||
return V->GetKind() == JSONValue::Kind::Object;
|
||||
}
|
||||
|
||||
bool SetObject(const std::string &key, JSONValue::SP value);
|
||||
|
||||
JSONValue::SP GetObject(const std::string &key);
|
||||
|
||||
~JSONObject() override = default;
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, JSONValue::SP> Map;
|
||||
typedef Map::iterator Iterator;
|
||||
Map m_elements;
|
||||
};
|
||||
|
||||
class JSONArray : public JSONValue {
|
||||
public:
|
||||
JSONArray();
|
||||
|
||||
JSONArray(const JSONArray &s) = delete;
|
||||
JSONArray &operator=(const JSONArray &s) = delete;
|
||||
|
||||
void Write(Stream &s) override;
|
||||
|
||||
typedef std::shared_ptr<JSONArray> SP;
|
||||
|
||||
static bool classof(const JSONValue *V) {
|
||||
return V->GetKind() == JSONValue::Kind::Array;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::vector<JSONValue::SP> Vector;
|
||||
typedef Vector::iterator Iterator;
|
||||
typedef Vector::size_type Index;
|
||||
typedef Vector::size_type Size;
|
||||
|
||||
public:
|
||||
bool SetObject(Index i, JSONValue::SP value);
|
||||
|
||||
bool AppendObject(JSONValue::SP value);
|
||||
|
||||
JSONValue::SP GetObject(Index i);
|
||||
|
||||
Size GetNumElements();
|
||||
|
||||
~JSONArray() override = default;
|
||||
|
||||
Vector m_elements;
|
||||
};
|
||||
|
||||
class JSONParser : public StringExtractor {
|
||||
public:
|
||||
enum Token {
|
||||
Invalid,
|
||||
Status,
|
||||
ObjectStart,
|
||||
ObjectEnd,
|
||||
ArrayStart,
|
||||
ArrayEnd,
|
||||
Comma,
|
||||
Colon,
|
||||
String,
|
||||
Integer,
|
||||
Float,
|
||||
True,
|
||||
False,
|
||||
Null,
|
||||
EndOfFile
|
||||
};
|
||||
|
||||
JSONParser(llvm::StringRef data);
|
||||
|
||||
int GetEscapedChar(bool &was_escaped);
|
||||
|
||||
Token GetToken(std::string &value);
|
||||
|
||||
JSONValue::SP ParseJSONValue();
|
||||
|
||||
protected:
|
||||
JSONValue::SP ParseJSONObject();
|
||||
|
||||
JSONValue::SP ParseJSONArray();
|
||||
};
|
||||
} // namespace lldb_private
|
||||
|
||||
#endif // utility_JSON_h_
|
|
@ -27,7 +27,6 @@ add_lldb_library(lldbUtility
|
|||
FileSpec.cpp
|
||||
GDBRemote.cpp
|
||||
IOObject.cpp
|
||||
JSON.cpp
|
||||
LLDBAssert.cpp
|
||||
Listener.cpp
|
||||
Log.cpp
|
||||
|
|
|
@ -1,550 +0,0 @@
|
|||
//===--------------------- JSON.cpp -----------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lldb/Utility/JSON.h"
|
||||
|
||||
#include "lldb/Utility/Stream.h"
|
||||
#include "lldb/Utility/StreamString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <utility>
|
||||
|
||||
using namespace lldb_private;
|
||||
|
||||
std::string JSONString::json_string_quote_metachars(const std::string &s) {
|
||||
if (s.find_first_of("\\\n\"") == 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 == '"' || ch == '\\' || ch == '\n') {
|
||||
output.push_back('\\');
|
||||
if (ch == '\n') ch = 'n';
|
||||
}
|
||||
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(Stream &s) {
|
||||
s.Printf("\"%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 static_cast<uint64_t>(m_data.m_signed);
|
||||
case DataType::Double:
|
||||
return static_cast<uint64_t>(m_data.m_double);
|
||||
}
|
||||
llvm_unreachable("Unhandled data type");
|
||||
}
|
||||
|
||||
int64_t JSONNumber::GetAsSigned() const {
|
||||
switch (m_data_type) {
|
||||
case DataType::Unsigned:
|
||||
return static_cast<int64_t>(m_data.m_unsigned);
|
||||
case DataType::Signed:
|
||||
return m_data.m_signed;
|
||||
case DataType::Double:
|
||||
return static_cast<int64_t>(m_data.m_double);
|
||||
}
|
||||
llvm_unreachable("Unhandled data type");
|
||||
}
|
||||
|
||||
double JSONNumber::GetAsDouble() const {
|
||||
switch (m_data_type) {
|
||||
case DataType::Unsigned:
|
||||
return static_cast<double>(m_data.m_unsigned);
|
||||
case DataType::Signed:
|
||||
return static_cast<double>(m_data.m_signed);
|
||||
case DataType::Double:
|
||||
return m_data.m_double;
|
||||
}
|
||||
llvm_unreachable("Unhandled data type");
|
||||
}
|
||||
|
||||
void JSONNumber::Write(Stream &s) {
|
||||
switch (m_data_type) {
|
||||
case DataType::Unsigned:
|
||||
s.Printf("%" PRIu64, m_data.m_unsigned);
|
||||
break;
|
||||
case DataType::Signed:
|
||||
s.Printf("%" PRId64, m_data.m_signed);
|
||||
break;
|
||||
case DataType::Double:
|
||||
s.Printf("%g", m_data.m_double);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
JSONTrue::JSONTrue() : JSONValue(JSONValue::Kind::True) {}
|
||||
|
||||
void JSONTrue::Write(Stream &s) { s.Printf("true"); }
|
||||
|
||||
JSONFalse::JSONFalse() : JSONValue(JSONValue::Kind::False) {}
|
||||
|
||||
void JSONFalse::Write(Stream &s) { s.Printf("false"); }
|
||||
|
||||
JSONNull::JSONNull() : JSONValue(JSONValue::Kind::Null) {}
|
||||
|
||||
void JSONNull::Write(Stream &s) { s.Printf("null"); }
|
||||
|
||||
JSONObject::JSONObject() : JSONValue(JSONValue::Kind::Object) {}
|
||||
|
||||
void JSONObject::Write(Stream &s) {
|
||||
bool first = true;
|
||||
s.PutChar('{');
|
||||
auto iter = m_elements.begin(), end = m_elements.end();
|
||||
for (; iter != end; iter++) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
s.PutChar(',');
|
||||
JSONString key(iter->first);
|
||||
JSONValue::SP value(iter->second);
|
||||
key.Write(s);
|
||||
s.PutChar(':');
|
||||
value->Write(s);
|
||||
}
|
||||
s.PutChar('}');
|
||||
}
|
||||
|
||||
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) {
|
||||
auto iter = m_elements.find(key), end = m_elements.end();
|
||||
if (iter == end)
|
||||
return JSONValue::SP();
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
JSONArray::JSONArray() : JSONValue(JSONValue::Kind::Array) {}
|
||||
|
||||
void JSONArray::Write(Stream &s) {
|
||||
bool first = true;
|
||||
s.PutChar('[');
|
||||
auto iter = m_elements.begin(), end = m_elements.end();
|
||||
for (; iter != end; iter++) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
s.PutChar(',');
|
||||
(*iter)->Write(s);
|
||||
}
|
||||
s.PutChar(']');
|
||||
}
|
||||
|
||||
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(llvm::StringRef data) : StringExtractor(data) {}
|
||||
|
||||
JSONParser::Token JSONParser::GetToken(std::string &value) {
|
||||
StreamString 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 (true) {
|
||||
bool was_escaped = false;
|
||||
int escaped_ch = GetEscapedChar(was_escaped);
|
||||
if (escaped_ch == -1) {
|
||||
error.Printf(
|
||||
"error: an error occurred getting a character from offset %" PRIu64,
|
||||
start_index);
|
||||
value = std::move(error.GetString());
|
||||
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, static_cast<char>(escaped_ch));
|
||||
} else {
|
||||
error.Printf("error: wide character support is needed for unicode "
|
||||
"character 0x%4.4x at offset %" PRIu64,
|
||||
escaped_ch, start_index);
|
||||
value = std::move(error.GetString());
|
||||
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.Printf("error: extra decimal point found at offset %" PRIu64,
|
||||
start_index);
|
||||
value = std::move(error.GetString());
|
||||
return Token::Status;
|
||||
} else {
|
||||
got_decimal_point = true;
|
||||
++m_index; // Skip this character
|
||||
}
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
case 'E':
|
||||
if (exp_index != 0) {
|
||||
error.Printf(
|
||||
"error: extra exponent character found at offset %" PRIu64,
|
||||
start_index);
|
||||
value = std::move(error.GetString());
|
||||
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.Printf("error: unexpected %c character at offset %" PRIu64,
|
||||
next_ch, start_index);
|
||||
value = std::move(error.GetString());
|
||||
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.Printf("error: got exponent character but no exponent digits "
|
||||
"at offset in float value \"%s\"",
|
||||
value.c_str());
|
||||
value = std::move(error.GetString());
|
||||
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.Printf("error: no digits after decimal point \"%s\"",
|
||||
value.c_str());
|
||||
value = std::move(error.GetString());
|
||||
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.Printf("error: no digits negate sign \"%s\"", value.c_str());
|
||||
value = std::move(error.GetString());
|
||||
return Token::Status;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error.Printf("error: invalid number found at offset %" PRIu64,
|
||||
start_index);
|
||||
value = std::move(error.GetString());
|
||||
return Token::Status;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
error.Printf("error: failed to parse token at offset %" PRIu64
|
||||
" (around character '%c')",
|
||||
start_index, ch);
|
||||
value = std::move(error.GetString());
|
||||
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 (true) {
|
||||
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 (true) {
|
||||
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() == '-') {
|
||||
int64_t sval = 0;
|
||||
if (!llvm::StringRef(value).getAsInteger(0, sval))
|
||||
return JSONValue::SP(new JSONNumber(sval));
|
||||
} else {
|
||||
uint64_t uval = 0;
|
||||
if (!llvm::StringRef(value).getAsInteger(0, uval))
|
||||
return JSONValue::SP(new JSONNumber(uval));
|
||||
}
|
||||
} break;
|
||||
|
||||
case JSONParser::Token::Float: {
|
||||
double D;
|
||||
if (!llvm::StringRef(value).getAsDouble(D))
|
||||
return JSONValue::SP(new JSONNumber(D));
|
||||
} 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();
|
||||
}
|
|
@ -11,7 +11,6 @@ add_lldb_unittest(UtilityTests
|
|||
EventTest.cpp
|
||||
FileSpecTest.cpp
|
||||
FlagsTest.cpp
|
||||
JSONTest.cpp
|
||||
ListenerTest.cpp
|
||||
LogTest.cpp
|
||||
NameMatchesTest.cpp
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
#include "gtest/gtest.h"
|
||||
|
||||
#include "lldb/Utility/JSON.h"
|
||||
#include "lldb/Utility/StreamString.h"
|
||||
|
||||
using namespace lldb_private;
|
||||
|
||||
TEST(JSONTest, Dictionary) {
|
||||
JSONObject o;
|
||||
o.SetObject("key", std::make_shared<JSONString>("value"));
|
||||
|
||||
StreamString stream;
|
||||
o.Write(stream);
|
||||
|
||||
ASSERT_EQ(stream.GetString(), R"({"key":"value"})");
|
||||
}
|
||||
|
||||
TEST(JSONTest, Newlines) {
|
||||
JSONObject o;
|
||||
o.SetObject("key", std::make_shared<JSONString>("hello\nworld"));
|
||||
|
||||
StreamString stream;
|
||||
o.Write(stream);
|
||||
|
||||
ASSERT_EQ(stream.GetString(), R"({"key":"hello\nworld"})");
|
||||
}
|
Loading…
Reference in New Issue