mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
305 lines
7.9 KiB
C++
305 lines
7.9 KiB
C++
//===--- JSONSerialization.cpp - JSON serialization support ---------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Basic/JSONSerialization.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/Format.h"
|
|
|
|
using namespace swift::json;
|
|
using namespace swift;
|
|
|
|
unsigned Output::beginArray() {
|
|
StateStack.push_back(ArrayFirstValue);
|
|
Stream << '[';
|
|
return 0;
|
|
}
|
|
|
|
bool Output::preflightElement(unsigned, void *&) {
|
|
if (StateStack.back() != ArrayFirstValue) {
|
|
assert(StateStack.back() == ArrayOtherValue && "We must be in a sequence!");
|
|
Stream << ',';
|
|
}
|
|
if (PrettyPrint) {
|
|
Stream << '\n';
|
|
indent();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Output::postflightElement(void*) {
|
|
if (StateStack.back() == ArrayFirstValue) {
|
|
StateStack.pop_back();
|
|
StateStack.push_back(ArrayOtherValue);
|
|
}
|
|
}
|
|
|
|
void Output::endArray() {
|
|
bool HadContent = StateStack.back() != ArrayFirstValue;
|
|
StateStack.pop_back();
|
|
if (PrettyPrint && HadContent) {
|
|
Stream << '\n';
|
|
indent();
|
|
}
|
|
Stream << ']';
|
|
}
|
|
|
|
bool Output::canElideEmptyArray() {
|
|
if (StateStack.size() < 2)
|
|
return true;
|
|
if (StateStack.back() != ObjectFirstKey)
|
|
return true;
|
|
State checkedState = StateStack[StateStack.size() - 2];
|
|
return (checkedState != ArrayFirstValue && checkedState != ArrayOtherValue);
|
|
}
|
|
|
|
void Output::beginObject() {
|
|
StateStack.push_back(ObjectFirstKey);
|
|
Stream << "{";
|
|
}
|
|
|
|
void Output::endObject() {
|
|
bool HadContent = StateStack.back() != ObjectFirstKey;
|
|
StateStack.pop_back();
|
|
if (PrettyPrint && HadContent) {
|
|
Stream << '\n';
|
|
indent();
|
|
}
|
|
Stream << "}";
|
|
}
|
|
|
|
bool Output::preflightKey(const char *Key, bool Required, bool SameAsDefault,
|
|
bool &UseDefault, void *&) {
|
|
UseDefault = false;
|
|
if (Required || !SameAsDefault) {
|
|
if (StateStack.back() != ObjectFirstKey) {
|
|
assert(StateStack.back() == ObjectOtherKey && "We must be in an object!");
|
|
Stream << ',';
|
|
}
|
|
if (PrettyPrint) {
|
|
Stream << '\n';
|
|
indent();
|
|
}
|
|
Stream << '"' << Key << "\":";
|
|
if (PrettyPrint)
|
|
Stream << ' ';
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Output::postflightKey(void*) {
|
|
if (StateStack.back() == ObjectFirstKey) {
|
|
StateStack.pop_back();
|
|
StateStack.push_back(ObjectOtherKey);
|
|
}
|
|
}
|
|
|
|
void Output::beginEnumScalar() {
|
|
EnumerationMatchFound = false;
|
|
}
|
|
|
|
bool Output::matchEnumScalar(const char *Str, bool Match) {
|
|
if (Match && !EnumerationMatchFound) {
|
|
StringRef StrRef(Str);
|
|
scalarString(StrRef, true);
|
|
EnumerationMatchFound = true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Output::endEnumScalar() {
|
|
if (!EnumerationMatchFound)
|
|
llvm_unreachable("bad runtime enum value");
|
|
}
|
|
|
|
bool Output::beginBitSetScalar(bool &DoClear) {
|
|
Stream << '[';
|
|
if (PrettyPrint)
|
|
Stream << ' ';
|
|
NeedBitValueComma = false;
|
|
DoClear = false;
|
|
return true;
|
|
}
|
|
|
|
bool Output::bitSetMatch(const char *Str, bool Matches) {
|
|
if (Matches) {
|
|
if (NeedBitValueComma) {
|
|
Stream << ',';
|
|
if (PrettyPrint)
|
|
Stream << ' ';
|
|
}
|
|
StringRef StrRef(Str);
|
|
scalarString(StrRef, true);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Output::endBitSetScalar() {
|
|
if (PrettyPrint)
|
|
Stream << ' ';
|
|
Stream << ']';
|
|
}
|
|
|
|
void Output::scalarString(StringRef &S, bool MustQuote) {
|
|
if (MustQuote) {
|
|
Stream << '"';
|
|
for (unsigned char c : S) {
|
|
// According to the JSON standard, the following characters must be
|
|
// escaped:
|
|
// - Quotation mark (U+0022)
|
|
// - Reverse solidus (U+005C)
|
|
// - Control characters (U+0000 to U+001F)
|
|
// We need to check for these and escape them if present.
|
|
//
|
|
// Since these are represented by a single byte in UTF8 (and will not be
|
|
// present in any multi-byte UTF8 representations), we can just switch on
|
|
// the value of the current byte.
|
|
//
|
|
// Any other bytes present in the string should therefore be emitted
|
|
// as-is, without any escaping.
|
|
switch (c) {
|
|
// First, check for characters for which JSON has custom escape sequences.
|
|
case '"':
|
|
Stream << '\\' << '"';
|
|
break;
|
|
case '\\':
|
|
Stream << '\\' << '\\';
|
|
break;
|
|
case '/':
|
|
Stream << '\\' << '/';
|
|
break;
|
|
case '\b':
|
|
Stream << '\\' << 'b';
|
|
break;
|
|
case '\f':
|
|
Stream << '\\' << 'f';
|
|
break;
|
|
case '\n':
|
|
Stream << '\\' << 'n';
|
|
break;
|
|
case '\r':
|
|
Stream << '\\' << 'r';
|
|
break;
|
|
case '\t':
|
|
Stream << '\\' << 't';
|
|
break;
|
|
default:
|
|
// Otherwise, check to see if the current byte is a control character.
|
|
if (c <= '\x1F') {
|
|
// Since we have a control character, we need to escape it using
|
|
// JSON's only valid escape sequence: \uxxxx (where x is a hex digit).
|
|
|
|
// The upper two digits for control characters are always 00.
|
|
Stream << "\\u00";
|
|
|
|
// Convert the current character into hexadecimal digits.
|
|
Stream << llvm::hexdigit((c >> 4) & 0xF);
|
|
Stream << llvm::hexdigit((c >> 0) & 0xF);
|
|
} else {
|
|
// This isn't a control character, so we don't need to escape it.
|
|
// As a result, emit it directly; if it's part of a multi-byte UTF8
|
|
// representation, all bytes will be emitted in this fashion.
|
|
Stream << c;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
Stream << '"';
|
|
}
|
|
else
|
|
Stream << S;
|
|
}
|
|
|
|
void Output::null() {
|
|
Stream << "null";
|
|
}
|
|
|
|
void Output::indent() {
|
|
Stream.indent(StateStack.size() * 2);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// traits for built-in types
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void ScalarTraits<bool>::output(const bool &Val, raw_ostream &Out) {
|
|
Out << (Val ? "true" : "false");
|
|
}
|
|
|
|
void ScalarTraits<StringRef>::output(const StringRef &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
void ScalarTraits<std::string>::output(const std::string &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
void ScalarTraits<uint8_t>::output(const uint8_t &Val,
|
|
raw_ostream &Out) {
|
|
// use temp uin32_t because ostream thinks uint8_t is a character
|
|
uint32_t Num = Val;
|
|
Out << Num;
|
|
}
|
|
|
|
void ScalarTraits<uint16_t>::output(const uint16_t &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
void ScalarTraits<uint32_t>::output(const uint32_t &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
#if defined(_MSC_VER)
|
|
void ScalarTraits<unsigned long>::output(const unsigned long &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
#endif
|
|
|
|
void ScalarTraits<uint64_t>::output(const uint64_t &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
void ScalarTraits<int8_t>::output(const int8_t &Val, raw_ostream &Out) {
|
|
// use temp in32_t because ostream thinks int8_t is a character
|
|
int32_t Num = Val;
|
|
Out << Num;
|
|
}
|
|
|
|
void ScalarTraits<int16_t>::output(const int16_t &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
void ScalarTraits<int32_t>::output(const int32_t &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
void ScalarTraits<int64_t>::output(const int64_t &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
void ScalarTraits<double>::output(const double &Val, raw_ostream &Out) {
|
|
Out << llvm::format("%g", Val);
|
|
}
|
|
|
|
void ScalarTraits<float>::output(const float &Val, raw_ostream &Out) {
|
|
Out << llvm::format("%g", Val);
|
|
}
|