mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Instead of using llvm::raw_ostream::write_escaped (which does not produce valid JSON strings), implemented custom escaping logic based on the JSON standard, which only requires that the following characters be escaped: - Quotation mark (U+0022) - Reverse solidus (U+005C) - Control characters (U+0000 to U+001F) Since these characters all fit within a single UTF8 byte, and will not be present in a multi-byte UTF8 representation, simply check whether the current byte needs to be escaped according to those requirements. If the current byte needs to be escaped, then print out the escaped version of the byte; otherwise, pass the current byte to the stream directly. This fixes <rdar://problem/18266570>. Swift SVN r21892
298 lines
7.7 KiB
C++
298 lines
7.7 KiB
C++
//===--- JSONSerialization.cpp - JSON serialization support ---------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://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 << '[';
|
|
if (PrettyPrint) {
|
|
Stream << '\n';
|
|
}
|
|
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';
|
|
}
|
|
if (PrettyPrint)
|
|
indent();
|
|
return true;
|
|
}
|
|
|
|
void Output::postflightElement(void*) {
|
|
if (StateStack.back() == ArrayFirstValue) {
|
|
StateStack.pop_back();
|
|
StateStack.push_back(ArrayOtherValue);
|
|
}
|
|
}
|
|
|
|
void Output::endArray() {
|
|
StateStack.pop_back();
|
|
if (PrettyPrint) {
|
|
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 << "{";
|
|
if (PrettyPrint)
|
|
Stream << '\n';
|
|
}
|
|
|
|
void Output::endObject() {
|
|
StateStack.pop_back();
|
|
if (PrettyPrint) {
|
|
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';
|
|
}
|
|
if (PrettyPrint)
|
|
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 >= '\x00' && 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::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;
|
|
}
|
|
|
|
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);
|
|
}
|