//===--- 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::output(const bool &Val, raw_ostream &Out) { Out << (Val ? "true" : "false"); } void ScalarTraits::output(const StringRef &Val, raw_ostream &Out) { Out << Val; } void ScalarTraits::output(const std::string &Val, raw_ostream &Out) { Out << Val; } void ScalarTraits::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::output(const uint16_t &Val, raw_ostream &Out) { Out << Val; } void ScalarTraits::output(const uint32_t &Val, raw_ostream &Out) { Out << Val; } void ScalarTraits::output(const uint64_t &Val, raw_ostream &Out) { Out << Val; } void ScalarTraits::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::output(const int16_t &Val, raw_ostream &Out) { Out << Val; } void ScalarTraits::output(const int32_t &Val, raw_ostream &Out) { Out << Val; } void ScalarTraits::output(const int64_t &Val, raw_ostream &Out) { Out << Val; } void ScalarTraits::output(const double &Val, raw_ostream &Out) { Out << llvm::format("%g", Val); } void ScalarTraits::output(const float &Val, raw_ostream &Out) { Out << llvm::format("%g", Val); }