blob: 9f958256ef80ece7f0eecd5e4871d8a40f691de9 [file] [log] [blame]
//===--- 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 <map>
#include <vector>
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<MyStruct> {
/// static void mapping(Output &out, MyStruct &s) {
/// out.mapRequired("name", s.name);
/// out.mapRequired("size", s.size);
/// out.mapOptional("age", s.age);
/// }
/// };
template<class T>
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<Colors> {
/// static void enumeration(Output &out, Colors &value) {
/// out.enumCase(value, "red", cRed);
/// out.enumCase(value, "blue", cBlue);
/// out.enumCase(value, "green", cGreen);
/// }
/// };
template<typename T>
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<MyFlags> {
/// static void bitset(Output &out, MyFlags &value) {
/// out.bitSetCase(value, "big", flagBig);
/// out.bitSetCase(value, "flat", flagFlat);
/// out.bitSetCase(value, "round", flagRound);
/// }
/// };
template<typename T>
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<MyType> {
/// 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<typename T>
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<MyType> {
/// static StringRef stringRef(const MyType &val) {
/// // Retrieve scalar value from memory
/// return value.stringValue;
/// }
/// static bool mustQuote(StringRef) { return true; }
/// };
template<typename T>
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<MyType *> > {
/// static bool isNull(MyType *&ptr) {
/// return !ptr;
/// }
/// static MyType &get(MyType *&ptr) {
/// return *ptr;
/// }
/// };
template<typename T>
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<MyType> > {
/// static size_t size(Output &out, std::vector<MyType> &seq) {
/// return seq.size();
/// }
/// static MyType& element(Output &, std::vector<MyType> &seq, size_t index) {
/// if (index >= seq.size())
/// seq.resize(index+1);
/// return seq[index];
/// }
/// };
template<typename T>
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 <typename T, T>
struct SameType;
// Only used for better diagnostics of missing traits
template <typename T>
struct MissingTrait;
// Test if ScalarEnumerationTraits<T> is defined on type T.
template <class T>
struct has_ScalarEnumerationTraits
{
using Signature_enumeration = void (*)(class Output &, T &);
template <typename U>
static char test(SameType<Signature_enumeration, &U::enumeration>*);
template <typename U>
static double test(...);
public:
static bool const value =
(sizeof(test<ScalarEnumerationTraits<T> >(nullptr)) == 1);
};
// Test if ScalarBitSetTraits<T> is defined on type T.
template <class T>
struct has_ScalarBitSetTraits
{
using Signature_bitset = void (*)(class Output &, T &);
template <typename U>
static char test(SameType<Signature_bitset, &U::bitset>*);
template <typename U>
static double test(...);
public:
static bool const value = (sizeof(test<ScalarBitSetTraits<T> >(nullptr)) == 1);
};
// Test if ScalarTraits<T> is defined on type T.
template <class T>
struct has_ScalarTraits
{
using Signature_output = void (*)(const T &, llvm::raw_ostream &);
using Signature_mustQuote = bool (*)(StringRef);
template <typename U>
static char test(SameType<Signature_output, &U::output> *,
SameType<Signature_mustQuote, &U::mustQuote> *);
template <typename U>
static double test(...);
public:
static bool const value =
(sizeof(test<ScalarTraits<T>>(nullptr, nullptr)) == 1);
};
// Test if ScalarReferenceTraits<T> is defined on type T.
template <class T>
struct has_ScalarReferenceTraits
{
using Signature_stringRef = StringRef (*)(const T &);
using Signature_mustQuote = bool (*)(StringRef);
template <typename U>
static char test(SameType<Signature_stringRef, &U::stringRef> *,
SameType<Signature_mustQuote, &U::mustQuote> *);
template <typename U>
static double test(...);
public:
static bool const value =
(sizeof(test<ScalarReferenceTraits<T>>(nullptr, nullptr)) == 1);
};
// Test if ObjectTraits<T> is defined on type T.
template <class T>
struct has_ObjectTraits
{
using Signature_mapping = void (*)(class Output &, T &);
template <typename U>
static char test(SameType<Signature_mapping, &U::mapping>*);
template <typename U>
static double test(...);
public:
static bool const value = (sizeof(test<ObjectTraits<T> >(nullptr)) == 1);
};
// Test if ObjectTraits<T>::validate() is defined on type T.
template <class T>
struct has_ObjectValidateTraits
{
using Signature_validate = StringRef (*)(class Output &, T &);
template <typename U>
static char test(SameType<Signature_validate, &U::validate>*);
template <typename U>
static double test(...);
public:
static bool const value = (sizeof(test<ObjectTraits<T> >(nullptr)) == 1);
};
// Test if ArrayTraits<T> is defined on type T.
template <class T>
struct has_ArrayMethodTraits
{
using Signature_size = size_t (*)(class Output &, T &);
template <typename U>
static char test(SameType<Signature_size, &U::size>*);
template <typename U>
static double test(...);
public:
static bool const value = (sizeof(test<ArrayTraits<T> >(nullptr)) == 1);
};
// Test if ArrayTraits<T> is defined on type T
template<typename T>
struct has_ArrayTraits : public std::integral_constant<bool,
has_ArrayMethodTraits<T>::value > { };
// Test if NullableTraits<T> is defined on type T.
template <class T>
struct has_NullableTraits
{
using Signature_isNull = bool (*)(T &);
template <typename U>
static char test(SameType<Signature_isNull, &U::isNull> *);
template <typename U>
static double test(...);
public:
static bool const value =
(sizeof(test<NullableTraits<T>>(nullptr)) == 1);
};
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<typename T>
struct missingTraits : public std::integral_constant<bool,
!has_ScalarEnumerationTraits<T>::value
&& !has_ScalarBitSetTraits<T>::value
&& !has_ScalarTraits<T>::value
&& !has_ScalarReferenceTraits<T>::value
&& !has_NullableTraits<T>::value
&& !has_ObjectTraits<T>::value
&& !has_ArrayTraits<T>::value> {};
template<typename T>
struct validatedObjectTraits : public std::integral_constant<bool,
has_ObjectTraits<T>::value
&& has_ObjectValidateTraits<T>::value> {};
template<typename T>
struct unvalidatedObjectTraits : public std::integral_constant<bool,
has_ObjectTraits<T>::value
&& !has_ObjectValidateTraits<T>::value> {};
class Output {
public:
using UserInfoMap = std::map<void *, void *>;
private:
enum State {
ArrayFirstValue,
ArrayOtherValue,
ObjectFirstKey,
ObjectOtherKey
};
llvm::raw_ostream &Stream;
SmallVector<State, 8> 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(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(StringRef &, bool);
void null();
template <typename T>
void enumCase(T &Val, const char* Str, const T ConstVal) {
if (matchEnumScalar(Str, Val == ConstVal)) {
Val = ConstVal;
}
}
template <typename T>
void bitSetCase(T &Val, const char* Str, const T ConstVal) {
if (bitSetMatch(Str, (Val & ConstVal) == ConstVal)) {
Val = Val | ConstVal;
}
}
template <typename T>
void maskedBitSetCase(T &Val, const char *Str, T ConstVal, T Mask) {
if (bitSetMatch(Str, (Val & Mask) == ConstVal))
Val = Val | ConstVal;
}
template <typename T>
void maskedBitSetCase(T &Val, const char *Str, uint32_t ConstVal,
uint32_t Mask) {
if (bitSetMatch(Str, (Val & Mask) == ConstVal))
Val = Val | ConstVal;
}
template <typename T>
void mapRequired(StringRef Key, T& Val) {
this->processKey(Key, Val, true);
}
template <typename T>
typename std::enable_if<has_ArrayTraits<T>::value,void>::type
mapOptional(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 <typename T>
void mapOptional(StringRef Key, Optional<T> &Val) {
processKeyWithDefault(Key, Val, Optional<T>(), /*Required=*/false);
}
template <typename T>
typename std::enable_if<!has_ArrayTraits<T>::value,void>::type
mapOptional(StringRef Key, T& Val) {
this->processKey(Key, Val, false);
}
template <typename T>
void mapOptional(StringRef Key, T& Val, const T& Default) {
this->processKeyWithDefault(Key, Val, Default, false);
}
private:
template <typename T>
void processKeyWithDefault(StringRef Key, Optional<T> &Val,
const Optional<T> &DefaultValue, bool Required) {
assert(!DefaultValue.hasValue() &&
"Optional<T> 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 <typename T>
void processKeyWithDefault(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 <typename T>
void processKey(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 <typename T> struct ArrayTraits<std::vector<T>> {
static size_t size(Output &out, std::vector<T> &seq) { return seq.size(); }
static T &element(Output &out, std::vector<T> &seq, size_t index) {
if (index >= seq.size())
seq.resize(index + 1);
return seq[index];
}
};
template<>
struct ScalarReferenceTraits<bool> {
static StringRef stringRef(const bool &);
static bool mustQuote(StringRef) { return false; }
};
template<>
struct ScalarReferenceTraits<StringRef> {
static StringRef stringRef(const StringRef &);
static bool mustQuote(StringRef S) { return true; }
};
template<>
struct ScalarReferenceTraits<std::string> {
static StringRef stringRef(const std::string &);
static bool mustQuote(StringRef S) { return true; }
};
template<>
struct ScalarTraits<uint8_t> {
static void output(const uint8_t &, llvm::raw_ostream &);
static bool mustQuote(StringRef) { return false; }
};
template<>
struct ScalarTraits<uint16_t> {
static void output(const uint16_t &, llvm::raw_ostream &);
static bool mustQuote(StringRef) { return false; }
};
template<>
struct ScalarTraits<uint32_t> {
static void output(const uint32_t &, llvm::raw_ostream &);
static bool mustQuote(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<unsigned long> {
static void output(const unsigned long &, llvm::raw_ostream &);
static bool mustQuote(StringRef) { return false; }
};
#endif
template<>
struct ScalarTraits<uint64_t> {
static void output(const uint64_t &, llvm::raw_ostream &);
static bool mustQuote(StringRef) { return false; }
};
template<>
struct ScalarTraits<int8_t> {
static void output(const int8_t &, llvm::raw_ostream &);
static bool mustQuote(StringRef) { return false; }
};
template<>
struct ScalarTraits<int16_t> {
static void output(const int16_t &, llvm::raw_ostream &);
static bool mustQuote(StringRef) { return false; }
};
template<>
struct ScalarTraits<int32_t> {
static void output(const int32_t &, llvm::raw_ostream &);
static bool mustQuote(StringRef) { return false; }
};
template<>
struct ScalarTraits<int64_t> {
static void output(const int64_t &, llvm::raw_ostream &);
static bool mustQuote(StringRef) { return false; }
};
template<>
struct ScalarTraits<float> {
static void output(const float &, llvm::raw_ostream &);
static bool mustQuote(StringRef) { return false; }
};
template<>
struct ScalarTraits<double> {
static void output(const double &, llvm::raw_ostream &);
static bool mustQuote(StringRef) { return false; }
};
template<typename T>
typename std::enable_if<has_ScalarEnumerationTraits<T>::value,void>::type
jsonize(Output &out, T &Val, bool) {
out.beginEnumScalar();
ScalarEnumerationTraits<T>::enumeration(out, Val);
out.endEnumScalar();
}
template<typename T>
typename std::enable_if<has_ScalarBitSetTraits<T>::value,void>::type
jsonize(Output &out, T &Val, bool) {
bool DoClear;
if (out.beginBitSetScalar(DoClear)) {
if (DoClear)
Val = static_cast<T>(0);
ScalarBitSetTraits<T>::bitset(out, Val);
out.endBitSetScalar();
}
}
template<typename T>
typename std::enable_if<has_ScalarTraits<T>::value,void>::type
jsonize(Output &out, T &Val, bool) {
{
SmallString<64> Storage;
llvm::raw_svector_ostream Buffer(Storage);
Buffer.SetUnbuffered();
ScalarTraits<T>::output(Val, Buffer);
StringRef Str = Buffer.str();
out.scalarString(Str, ScalarTraits<T>::mustQuote(Str));
}
}
template <typename T>
typename std::enable_if<has_ScalarReferenceTraits<T>::value, void>::type
jsonize(Output &out, T &Val, bool) {
StringRef Str = ScalarReferenceTraits<T>::stringRef(Val);
out.scalarString(Str, ScalarReferenceTraits<T>::mustQuote(Str));
}
template<typename T>
typename std::enable_if<has_NullableTraits<T>::value,void>::type
jsonize(Output &out, T &Obj, bool) {
if (NullableTraits<T>::isNull(Obj))
out.null();
else
jsonize(out, NullableTraits<T>::get(Obj), true);
}
template<typename T>
typename std::enable_if<validatedObjectTraits<T>::value, void>::type
jsonize(Output &out, T &Val, bool) {
out.beginObject();
{
StringRef Err = ObjectTraits<T>::validate(out, Val);
if (!Err.empty()) {
llvm::errs() << Err << "\n";
assert(Err.empty() && "invalid struct trying to be written as json");
}
}
ObjectTraits<T>::mapping(out, Val);
out.endObject();
}
template<typename T>
typename std::enable_if<unvalidatedObjectTraits<T>::value, void>::type
jsonize(Output &out, T &Val, bool) {
out.beginObject();
ObjectTraits<T>::mapping(out, Val);
out.endObject();
}
template<typename T>
typename std::enable_if<missingTraits<T>::value, void>::type
jsonize(Output &out, T &Val, bool) {
char missing_json_trait_for_type[sizeof(MissingTrait<T>)];
}
template<typename T>
typename std::enable_if<has_ArrayTraits<T>::value,void>::type
jsonize(Output &out, T &Seq, bool) {
{
out.beginArray();
unsigned count = ArrayTraits<T>::size(out, Seq);
for (unsigned i=0; i < count; ++i) {
void *SaveInfo;
if (out.preflightElement(i, SaveInfo)) {
jsonize(out, ArrayTraits<T>::element(out, Seq, i), true);
out.postflightElement(SaveInfo);
}
}
out.endArray();
}
}
// Define non-member operator<< so that Output can stream out a map.
template <typename T>
inline
typename
std::enable_if<swift::json::has_ObjectTraits<T>::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 <typename T>
inline
typename
std::enable_if<swift::json::has_ArrayTraits<T>::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