//===--- JSONSerialization.h - JSON serialization support -------*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// // /// \file /// Provides an interface for serializing to JSON. /// \note This does not include support for deserializing JSON; since JSON is /// a subset of YAML, use LLVM's YAML parsing support instead. /// //===----------------------------------------------------------------------===// #ifndef SWIFT_BASIC_JSONSERIALIZATION_H #define SWIFT_BASIC_JSONSERIALIZATION_H /* #include "swift/Basic/LLVM.h" */ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Regex.h" #include "llvm/Support/raw_ostream.h" #include #include #include namespace swift { namespace json { /// This class should be specialized by any type that needs to be converted /// to/from a JSON object. For example: /// /// struct ObjectTraits { /// static void mapping(Output &out, MyStruct &s) { /// out.mapRequired("name", s.name); /// out.mapRequired("size", s.size); /// out.mapOptional("age", s.age); /// } /// }; template struct ObjectTraits { // Must provide: // static void mapping(Output &out, T &fields); // Optionally may provide: // static StringRef validate(Output &out, T &fields); }; /// This class should be specialized by any integral type that converts /// to/from a JSON scalar where there is a one-to-one mapping between /// in-memory values and a string in JSON. For example: /// /// struct ScalarEnumerationTraits { /// static void enumeration(Output &out, Colors &value) { /// out.enumCase(value, "red", cRed); /// out.enumCase(value, "blue", cBlue); /// out.enumCase(value, "green", cGreen); /// } /// }; template struct ScalarEnumerationTraits { // Must provide: // static void enumeration(Output &out, T &value); }; /// This class should be specialized by any integer type that is a union /// of bit values and the JSON representation is an array of /// strings. For example: /// /// struct ScalarBitSetTraits { /// static void bitset(Output &out, MyFlags &value) { /// out.bitSetCase(value, "big", flagBig); /// out.bitSetCase(value, "flat", flagFlat); /// out.bitSetCase(value, "round", flagRound); /// } /// }; template struct ScalarBitSetTraits { // Must provide: // static void bitset(Output &out, T &value); }; /// This class should be specialized by type that requires custom conversion /// to/from a json scalar. For example: /// /// template<> /// struct ScalarTraits { /// static void output(const MyType &val, llvm::raw_ostream &out) { /// // stream out custom formatting /// out << llvm::format("%x", val); /// } /// static bool mustQuote(StringRef) { return true; } /// }; template struct ScalarTraits { // Must provide: // // Function to write the value as a string: //static void output(const T &value, void *ctxt, llvm::raw_ostream &out); // // Function to determine if the value should be quoted. //static bool mustQuote(StringRef); }; /// This is an optimized form of ScalarTraits in case the scalar value is /// already present in a memory buffer. For example: /// /// template<> /// struct ScalarReferenceTraits { /// static StringRef stringRef(const MyType &val) { /// // Retrieve scalar value from memory /// return value.stringValue; /// } /// static bool mustQuote(StringRef) { return true; } /// }; template struct ScalarReferenceTraits { // Must provide: // // Function to return a string representation of the value. // static StringRef stringRef(const T &value); // // Function to determine if the value should be quoted. // static bool mustQuote(StringRef); }; /// This class should be specialized by any type that can be 'null' in JSON. /// For example: /// /// template<> /// struct NullableTraits > { /// static bool isNull(MyType *&ptr) { /// return !ptr; /// } /// static MyType &get(MyType *&ptr) { /// return *ptr; /// } /// }; template struct NullableTraits { // Must provide: // // Function to return true if the value is 'null'. // static bool isNull(const T &Val); // // Function to return a reference to the unwrapped value. // static T::value_type &get(const T &Val); }; /// This class should be specialized by any type that needs to be converted /// to/from a JSON array. For example: /// /// template<> /// struct ArrayTraits< std::vector > { /// static size_t size(Output &out, std::vector &seq) { /// return seq.size(); /// } /// static MyType& element(Output &, std::vector &seq, size_t index) { /// if (index >= seq.size()) /// seq.resize(index+1); /// return seq[index]; /// } /// }; template struct ArrayTraits { // Must provide: // static size_t size(Output &out, T &seq); // static T::value_type& element(Output &out, T &seq, size_t index); }; // Only used by compiler if both template types are the same template struct SameType; // Only used for better diagnostics of missing traits template struct MissingTrait; // Test if ScalarEnumerationTraits is defined on type T. template struct has_ScalarEnumerationTraits { using Signature_enumeration = void (*)(class Output &, T &); template static char test(SameType*); template static double test(...); public: static bool const value = (sizeof(test >(nullptr)) == 1); }; // Test if ScalarBitSetTraits is defined on type T. template struct has_ScalarBitSetTraits { using Signature_bitset = void (*)(class Output &, T &); template static char test(SameType*); template static double test(...); public: static bool const value = (sizeof(test >(nullptr)) == 1); }; // Test if ScalarTraits is defined on type T. template struct has_ScalarTraits { using Signature_output = void (*)(const T &, llvm::raw_ostream &); using Signature_mustQuote = bool (*)(llvm::StringRef); template static char test(SameType *, SameType *); template static double test(...); public: static bool const value = (sizeof(test>(nullptr, nullptr)) == 1); }; // Test if ScalarReferenceTraits is defined on type T. template struct has_ScalarReferenceTraits { using Signature_stringRef = llvm::StringRef (*)(const T &); using Signature_mustQuote = bool (*)(llvm::StringRef); template static char test(SameType *, SameType *); template static double test(...); public: static bool const value = (sizeof(test>(nullptr, nullptr)) == 1); }; // Test if ObjectTraits is defined on type T. template struct has_ObjectTraits { using Signature_mapping = void (*)(class Output &, T &); template static char test(SameType*); template static double test(...); public: static bool const value = (sizeof(test >(nullptr)) == 1); }; // Test if ObjectTraits::validate() is defined on type T. template struct has_ObjectValidateTraits { using Signature_validate = llvm::StringRef (*)(class Output &, T &); template static char test(SameType*); template static double test(...); public: static bool const value = (sizeof(test >(nullptr)) == 1); }; // Test if ArrayTraits is defined on type T. template struct has_ArrayMethodTraits { using Signature_size = size_t (*)(class Output &, T &); template static char test(SameType*); template static double test(...); public: static bool const value = (sizeof(test >(nullptr)) == 1); }; // Test if ArrayTraits is defined on type T template struct has_ArrayTraits : public std::integral_constant::value > { }; // Test if NullableTraits is defined on type T. template struct has_NullableTraits { using Signature_isNull = bool (*)(T &); template static char test(SameType *); template static double test(...); public: static bool const value = (sizeof(test>(nullptr)) == 1); }; inline bool isNumber(llvm::StringRef S) { static const char DecChars[] = "0123456789"; if (S.find_first_not_of(DecChars) == llvm::StringRef::npos) return true; llvm::Regex FloatMatcher( "^(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?$"); if (FloatMatcher.match(S)) return true; return false; } inline bool isNumeric(llvm::StringRef S) { if ((S.front() == '-' || S.front() == '+') && isNumber(S.drop_front())) return true; if (isNumber(S)) return true; return false; } inline bool isNull(llvm::StringRef S) { return S == "null"; } inline bool isBool(llvm::StringRef S) { return S == "true" || S == "false"; } template struct missingTraits : public std::integral_constant::value && !has_ScalarBitSetTraits::value && !has_ScalarTraits::value && !has_ScalarReferenceTraits::value && !has_NullableTraits::value && !has_ObjectTraits::value && !has_ArrayTraits::value> {}; template struct validatedObjectTraits : public std::integral_constant::value && has_ObjectValidateTraits::value> {}; template struct unvalidatedObjectTraits : public std::integral_constant::value && !has_ObjectValidateTraits::value> {}; class Output { public: using UserInfoMap = std::map; private: enum State { ArrayFirstValue, ArrayOtherValue, ObjectFirstKey, ObjectOtherKey }; llvm::raw_ostream &Stream; llvm::SmallVector StateStack; bool PrettyPrint; bool NeedBitValueComma; bool EnumerationMatchFound; UserInfoMap UserInfo; public: Output(llvm::raw_ostream &os, UserInfoMap UserInfo = {}, bool PrettyPrint = true) : Stream(os), PrettyPrint(PrettyPrint), NeedBitValueComma(false), EnumerationMatchFound(false), UserInfo(UserInfo) {} virtual ~Output() = default; UserInfoMap &getUserInfo() { return UserInfo; } unsigned beginArray(); bool preflightElement(unsigned, void *&); void postflightElement(void*); void endArray(); bool canElideEmptyArray(); void beginObject(); void endObject(); bool preflightKey(llvm::StringRef, bool, bool, bool &, void *&); void postflightKey(void*); void beginEnumScalar(); bool matchEnumScalar(const char*, bool); void endEnumScalar(); bool beginBitSetScalar(bool &); bool bitSetMatch(const char*, bool); void endBitSetScalar(); void scalarString(llvm::StringRef &, bool); void null(); template void enumCase(T &Val, const char* Str, const T ConstVal) { if (matchEnumScalar(Str, Val == ConstVal)) { Val = ConstVal; } } template void bitSetCase(T &Val, const char* Str, const T ConstVal) { if (bitSetMatch(Str, (Val & ConstVal) == ConstVal)) { Val = Val | ConstVal; } } template void maskedBitSetCase(T &Val, const char *Str, T ConstVal, T Mask) { if (bitSetMatch(Str, (Val & Mask) == ConstVal)) Val = Val | ConstVal; } template void maskedBitSetCase(T &Val, const char *Str, uint32_t ConstVal, uint32_t Mask) { if (bitSetMatch(Str, (Val & Mask) == ConstVal)) Val = Val | ConstVal; } template void mapRequired(llvm::StringRef Key, T &Val) { this->processKey(Key, Val, true); } template typename std::enable_if::value, void>::type mapOptional(llvm::StringRef Key, T &Val) { // omit key/value instead of outputting empty array if (this->canElideEmptyArray() && !(Val.begin() != Val.end())) return; this->processKey(Key, Val, false); } template void mapOptional(llvm::StringRef Key, std::optional &Val) { processKeyWithDefault(Key, Val, std::optional(), /*Required=*/false); } template typename std::enable_if::value, void>::type mapOptional(llvm::StringRef Key, T &Val) { this->processKey(Key, Val, false); } template void mapOptional(llvm::StringRef Key, T &Val, const T &Default) { this->processKeyWithDefault(Key, Val, Default, false); } private: template void processKeyWithDefault(llvm::StringRef Key, std::optional &Val, const std::optional &DefaultValue, bool Required) { assert(!DefaultValue.has_value() && "Optional shouldn't have a value!"); void *SaveInfo; bool UseDefault; const bool sameAsDefault = !Val.has_value(); if (!Val.has_value()) Val = T(); if (this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) { jsonize(*this, Val.value(), Required); this->postflightKey(SaveInfo); } else { if (UseDefault) Val = DefaultValue; } } template void processKeyWithDefault(llvm::StringRef Key, T &Val, const T &DefaultValue, bool Required) { void *SaveInfo; bool UseDefault; const bool sameAsDefault = Val == DefaultValue; if (this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) { jsonize(*this, Val, Required); this->postflightKey(SaveInfo); } else { if (UseDefault) Val = DefaultValue; } } template void processKey(llvm::StringRef Key, T &Val, bool Required) { void *SaveInfo; bool UseDefault; if (this->preflightKey(Key, Required, false, UseDefault, SaveInfo)) { jsonize(*this, Val, Required); this->postflightKey(SaveInfo); } } private: void indent(); }; template struct ArrayTraits> { static size_t size(Output &out, std::vector &seq) { return seq.size(); } static T &element(Output &out, std::vector &seq, size_t index) { if (index >= seq.size()) seq.resize(index + 1); return seq[index]; } }; template<> struct ScalarReferenceTraits { static llvm::StringRef stringRef(const bool &); static bool mustQuote(llvm::StringRef) { return false; } }; template <> struct ScalarReferenceTraits { static llvm::StringRef stringRef(const llvm::StringRef &); static bool mustQuote(llvm::StringRef S) { return true; } }; template<> struct ScalarReferenceTraits { static llvm::StringRef stringRef(const std::string &); static bool mustQuote(llvm::StringRef S) { return true; } }; template<> struct ScalarTraits { static void output(const uint8_t &, llvm::raw_ostream &); static bool mustQuote(llvm::StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const uint16_t &, llvm::raw_ostream &); static bool mustQuote(llvm::StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const uint32_t &, llvm::raw_ostream &); static bool mustQuote(llvm::StringRef) { return false; } }; #if defined(_MSC_VER) // In MSVC, 'unsigned long' is 32bit size and different from uint32_t, // and it is used to define swift::sys::ProcessId. template<> struct ScalarTraits { static void output(const unsigned long &, llvm::raw_ostream &); static bool mustQuote(llvm::StringRef) { return false; } }; #endif template<> struct ScalarTraits { static void output(const uint64_t &, llvm::raw_ostream &); static bool mustQuote(llvm::StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const int8_t &, llvm::raw_ostream &); static bool mustQuote(llvm::StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const int16_t &, llvm::raw_ostream &); static bool mustQuote(llvm::StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const int32_t &, llvm::raw_ostream &); static bool mustQuote(llvm::StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const int64_t &, llvm::raw_ostream &); static bool mustQuote(llvm::StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const float &, llvm::raw_ostream &); static bool mustQuote(llvm::StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const double &, llvm::raw_ostream &); static bool mustQuote(llvm::StringRef) { return false; } }; template typename std::enable_if::value,void>::type jsonize(Output &out, T &Val, bool) { out.beginEnumScalar(); ScalarEnumerationTraits::enumeration(out, Val); out.endEnumScalar(); } template typename std::enable_if::value,void>::type jsonize(Output &out, T &Val, bool) { bool DoClear; if (out.beginBitSetScalar(DoClear)) { if (DoClear) Val = static_cast(0); ScalarBitSetTraits::bitset(out, Val); out.endBitSetScalar(); } } template typename std::enable_if::value,void>::type jsonize(Output &out, T &Val, bool) { { llvm::SmallString<64> Storage; llvm::raw_svector_ostream Buffer(Storage); Buffer.SetUnbuffered(); ScalarTraits::output(Val, Buffer); llvm::StringRef Str = Buffer.str(); out.scalarString(Str, ScalarTraits::mustQuote(Str)); } } template typename std::enable_if::value, void>::type jsonize(Output &out, T &Val, bool) { llvm::StringRef Str = ScalarReferenceTraits::stringRef(Val); out.scalarString(Str, ScalarReferenceTraits::mustQuote(Str)); } template typename std::enable_if::value,void>::type jsonize(Output &out, T &Obj, bool) { if (NullableTraits::isNull(Obj)) out.null(); else jsonize(out, NullableTraits::get(Obj), true); } template typename std::enable_if::value, void>::type jsonize(Output &out, T &Val, bool) { out.beginObject(); { llvm::StringRef Err = ObjectTraits::validate(out, Val); if (!Err.empty()) { llvm::errs() << Err << "\n"; assert(Err.empty() && "invalid struct trying to be written as json"); } } ObjectTraits::mapping(out, Val); out.endObject(); } template typename std::enable_if::value, void>::type jsonize(Output &out, T &Val, bool) { out.beginObject(); ObjectTraits::mapping(out, Val); out.endObject(); } template typename std::enable_if::value, void>::type jsonize(Output &out, T &Val, bool) { char missing_json_trait_for_type[sizeof(MissingTrait)]; } template typename std::enable_if::value,void>::type jsonize(Output &out, T &Seq, bool) { { out.beginArray(); unsigned count = ArrayTraits::size(out, Seq); for (unsigned i=0; i < count; ++i) { void *SaveInfo; if (out.preflightElement(i, SaveInfo)) { jsonize(out, ArrayTraits::element(out, Seq, i), true); out.postflightElement(SaveInfo); } } out.endArray(); } } // Define non-member operator<< so that Output can stream out a map. template inline typename std::enable_if::value, Output &>::type operator<<(Output &yout, T &map) { jsonize(yout, map, true); return yout; } // Define non-member operator<< so that Output can stream out an array. template inline typename std::enable_if::value, Output &>::type operator<<(Output &yout, T &seq) { jsonize(yout, seq, true); return yout; } } // end namespace json } // end namespace swift #endif // SWIFT_BASIC_JSONSERIALIZATION_H