//===--- JSONSerialization.h - JSON serialization support -------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 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 // //===----------------------------------------------------------------------===// // /// \file /// \brief 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/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Regex.h" #include "llvm/Support/raw_ostream.h" 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 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 { typedef void (*Signature_enumeration)(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 { typedef void (*Signature_bitset)(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 { typedef void (*Signature_output)(const T&, llvm::raw_ostream&); typedef bool (*Signature_mustQuote)(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 { typedef void (*Signature_mapping)(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 { typedef StringRef (*Signature_validate)(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 { typedef size_t (*Signature_size)(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 > { }; inline bool isNumber(StringRef S) { static const char DecChars[] = "0123456789"; if (S.find_first_not_of(DecChars) == 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(StringRef S) { if ((S.front() == '-' || S.front() == '+') && isNumber(S.drop_front())) return true; if (isNumber(S)) return true; return false; } inline bool isNull(StringRef S) { return S.equals("null"); } inline bool isBool(StringRef S) { return S.equals("true") || S.equals("false"); } template struct missingTraits : public std::integral_constant::value && !has_ScalarBitSetTraits::value && !has_ScalarTraits::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 { enum State { ArrayFirstValue, ArrayOtherValue, ObjectFirstKey, ObjectOtherKey }; llvm::raw_ostream &Stream; SmallVector StateStack; bool PrettyPrint; bool NeedBitValueComma; bool EnumerationMatchFound; public: Output(llvm::raw_ostream &os, bool PrettyPrint = true) : Stream(os), PrettyPrint(PrettyPrint), NeedBitValueComma(false), EnumerationMatchFound(false) {} virtual ~Output() = default; unsigned beginArray(); bool preflightElement(unsigned, void *&); void postflightElement(void*); void endArray(); bool canElideEmptyArray(); void beginObject(); void endObject(); bool preflightKey(const char*, 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(StringRef &, bool); 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(const char* Key, T& Val) { this->processKey(Key, Val, true); } template typename std::enable_if::value,void>::type mapOptional(const char* 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(const char* Key, Optional &Val) { processKeyWithDefault(Key, Val, Optional(), /*Required=*/false); } template typename std::enable_if::value,void>::type mapOptional(const char* Key, T& Val) { this->processKey(Key, Val, false); } template void mapOptional(const char* Key, T& Val, const T& Default) { this->processKeyWithDefault(Key, Val, Default, false); } private: template void processKeyWithDefault(const char *Key, Optional &Val, const Optional &DefaultValue, bool Required) { assert(!DefaultValue.hasValue() && "Optional shouldn't have a value!"); void *SaveInfo; bool UseDefault; const bool sameAsDefault = !Val.hasValue(); if (!Val.hasValue()) Val = T(); if (this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) { jsonize(*this, Val.getValue(), Required); this->postflightKey(SaveInfo); } else { if (UseDefault) Val = DefaultValue; } } template void processKeyWithDefault(const char *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(const char *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 ScalarTraits { static void output(const bool &, llvm::raw_ostream &); static bool mustQuote(StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const StringRef &, llvm::raw_ostream &); static bool mustQuote(StringRef S) { return true; } }; template<> struct ScalarTraits { static void output(const std::string &, llvm::raw_ostream &); static bool mustQuote(StringRef S) { return true; } }; template<> struct ScalarTraits { static void output(const uint8_t &, llvm::raw_ostream &); static bool mustQuote(StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const uint16_t &, llvm::raw_ostream &); static bool mustQuote(StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const uint32_t &, llvm::raw_ostream &); static bool mustQuote(StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const uint64_t &, llvm::raw_ostream &); static bool mustQuote(StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const int8_t &, llvm::raw_ostream &); static bool mustQuote(StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const int16_t &, llvm::raw_ostream &); static bool mustQuote(StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const int32_t &, llvm::raw_ostream &); static bool mustQuote(StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const int64_t &, llvm::raw_ostream &); static bool mustQuote(StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const float &, llvm::raw_ostream &); static bool mustQuote(StringRef) { return false; } }; template<> struct ScalarTraits { static void output(const double &, llvm::raw_ostream &); static bool mustQuote(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) { { std::string Storage; llvm::raw_string_ostream Buffer(Storage); ScalarTraits::output(Val, Buffer); StringRef Str = Buffer.str(); out.scalarString(Str, ScalarTraits::mustQuote(Str)); } } template typename std::enable_if::value, void>::type jsonize(Output &out, T &Val, bool) { out.beginObject(); { 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