diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h index 4b8c4e958288..fa8caed1a9cc 100644 --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -1,1766 +1,1821 @@ //===- llvm/Support/YAMLTraits.h --------------------------------*- C++ -*-===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLVM_SUPPORT_YAMLTRAITS_H #define LLVM_SUPPORT_YAMLTRAITS_H #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/AlignOf.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Regex.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/YAMLParser.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include +#include #include #include #include #include #include #include #include namespace llvm { namespace yaml { struct EmptyContext {}; /// This class should be specialized by any type that needs to be converted /// to/from a YAML mapping. For example: /// /// struct MappingTraits { /// static void mapping(IO &io, MyStruct &s) { /// io.mapRequired("name", s.name); /// io.mapRequired("size", s.size); /// io.mapOptional("age", s.age); /// } /// }; template struct MappingTraits { // Must provide: // static void mapping(IO &io, T &fields); // Optionally may provide: // static StringRef validate(IO &io, T &fields); // // The optional flow flag will cause generated YAML to use a flow mapping // (e.g. { a: 0, b: 1 }): // static const bool flow = true; }; /// This class is similar to MappingTraits but allows you to pass in /// additional context for each map operation. For example: /// /// struct MappingContextTraits { /// static void mapping(IO &io, MyStruct &s, MyContext &c) { /// io.mapRequired("name", s.name); /// io.mapRequired("size", s.size); /// io.mapOptional("age", s.age); /// ++c.TimesMapped; /// } /// }; template struct MappingContextTraits { // Must provide: // static void mapping(IO &io, T &fields, Context &Ctx); // Optionally may provide: // static StringRef validate(IO &io, T &fields, Context &Ctx); // // The optional flow flag will cause generated YAML to use a flow mapping // (e.g. { a: 0, b: 1 }): // static const bool flow = true; }; /// This class should be specialized by any integral type that converts /// to/from a YAML scalar where there is a one-to-one mapping between /// in-memory values and a string in YAML. For example: /// /// struct ScalarEnumerationTraits { /// static void enumeration(IO &io, Colors &value) { /// io.enumCase(value, "red", cRed); /// io.enumCase(value, "blue", cBlue); /// io.enumCase(value, "green", cGreen); /// } /// }; template struct ScalarEnumerationTraits { // Must provide: // static void enumeration(IO &io, T &value); }; /// This class should be specialized by any integer type that is a union /// of bit values and the YAML representation is a flow sequence of /// strings. For example: /// /// struct ScalarBitSetTraits { /// static void bitset(IO &io, MyFlags &value) { /// io.bitSetCase(value, "big", flagBig); /// io.bitSetCase(value, "flat", flagFlat); /// io.bitSetCase(value, "round", flagRound); /// } /// }; template struct ScalarBitSetTraits { // Must provide: // static void bitset(IO &io, T &value); }; /// Describe which type of quotes should be used when quoting is necessary. /// Some non-printable characters need to be double-quoted, while some others /// are fine with simple-quoting, and some don't need any quoting. enum class QuotingType { None, Single, Double }; /// This class should be specialized by type that requires custom conversion /// to/from a yaml scalar. For example: /// /// template<> /// struct ScalarTraits { /// static void output(const MyType &val, void*, llvm::raw_ostream &out) { /// // stream out custom formatting /// out << llvm::format("%x", val); /// } /// static StringRef input(StringRef scalar, void*, MyType &value) { /// // parse scalar and set `value` /// // return empty string on success, or error string /// return StringRef(); /// } /// static QuotingType mustQuote(StringRef) { return QuotingType::Single; } /// }; 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 convert a string to a value. Returns the empty // StringRef on success or an error string if string is malformed: //static StringRef input(StringRef scalar, void *ctxt, T &value); // // Function to determine if the value should be quoted. //static QuotingType mustQuote(StringRef); }; /// This class should be specialized by type that requires custom conversion /// to/from a YAML literal block scalar. For example: /// /// template <> /// struct BlockScalarTraits { /// static void output(const MyType &Value, void*, llvm::raw_ostream &Out) /// { /// // stream out custom formatting /// Out << Val; /// } /// static StringRef input(StringRef Scalar, void*, MyType &Value) { /// // parse scalar and set `value` /// // return empty string on success, or error string /// return StringRef(); /// } /// }; template struct BlockScalarTraits { // Must provide: // // Function to write the value as a string: // static void output(const T &Value, void *ctx, llvm::raw_ostream &Out); // // Function to convert a string to a value. Returns the empty // StringRef on success or an error string if string is malformed: // static StringRef input(StringRef Scalar, void *ctxt, T &Value); }; /// This class should be specialized by any type that needs to be converted /// to/from a YAML sequence. For example: /// /// template<> /// struct SequenceTraits { /// static size_t size(IO &io, MyContainer &seq) { /// return seq.size(); /// } /// static MyType& element(IO &, MyContainer &seq, size_t index) { /// if ( index >= seq.size() ) /// seq.resize(index+1); /// return seq[index]; /// } /// }; template struct SequenceTraits { // Must provide: // static size_t size(IO &io, T &seq); // static T::value_type& element(IO &io, T &seq, size_t index); // // The following is option and will cause generated YAML to use // a flow sequence (e.g. [a,b,c]). // static const bool flow = true; }; /// This class should be specialized by any type for which vectors of that /// type need to be converted to/from a YAML sequence. template struct SequenceElementTraits { // Must provide: // static const bool flow; }; /// This class should be specialized by any type that needs to be converted /// to/from a list of YAML documents. template struct DocumentListTraits { // Must provide: // static size_t size(IO &io, T &seq); // static T::value_type& element(IO &io, T &seq, size_t index); }; /// This class should be specialized by any type that needs to be converted /// to/from a YAML mapping in the case where the names of the keys are not known /// in advance, e.g. a string map. template struct CustomMappingTraits { // static void inputOne(IO &io, StringRef key, T &elem); // static void output(IO &io, T &elem); }; // 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 IO&, 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 IO&, 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_input = StringRef (*)(StringRef, void*, T&); using Signature_output = void (*)(const T&, void*, raw_ostream&); using Signature_mustQuote = QuotingType (*)(StringRef); template static char test(SameType *, SameType *, SameType *); template static double test(...); public: static bool const value = (sizeof(test>(nullptr, nullptr, nullptr)) == 1); }; // Test if BlockScalarTraits is defined on type T. template struct has_BlockScalarTraits { using Signature_input = StringRef (*)(StringRef, void *, T &); using Signature_output = void (*)(const T &, void *, raw_ostream &); template static char test(SameType *, SameType *); template static double test(...); public: static bool const value = (sizeof(test>(nullptr, nullptr)) == 1); }; // Test if MappingContextTraits is defined on type T. template struct has_MappingTraits { using Signature_mapping = void (*)(class IO &, T &, Context &); template static char test(SameType*); template static double test(...); public: static bool const value = (sizeof(test>(nullptr)) == 1); }; // Test if MappingTraits is defined on type T. template struct has_MappingTraits { using Signature_mapping = void (*)(class IO &, T &); template static char test(SameType *); template static double test(...); public: static bool const value = (sizeof(test>(nullptr)) == 1); }; // Test if MappingContextTraits::validate() is defined on type T. template struct has_MappingValidateTraits { using Signature_validate = StringRef (*)(class IO &, T &, Context &); template static char test(SameType*); template static double test(...); public: static bool const value = (sizeof(test>(nullptr)) == 1); }; // Test if MappingTraits::validate() is defined on type T. template struct has_MappingValidateTraits { using Signature_validate = StringRef (*)(class IO &, T &); template static char test(SameType *); template static double test(...); public: static bool const value = (sizeof(test>(nullptr)) == 1); }; // Test if SequenceTraits is defined on type T. template struct has_SequenceMethodTraits { using Signature_size = size_t (*)(class IO&, T&); template static char test(SameType*); template static double test(...); public: static bool const value = (sizeof(test>(nullptr)) == 1); }; // Test if CustomMappingTraits is defined on type T. template struct has_CustomMappingTraits { using Signature_input = void (*)(IO &io, StringRef key, T &v); template static char test(SameType*); template static double test(...); public: static bool const value = (sizeof(test>(nullptr)) == 1); }; // has_FlowTraits will cause an error with some compilers because // it subclasses int. Using this wrapper only instantiates the // real has_FlowTraits only if the template type is a class. template ::value> class has_FlowTraits { public: static const bool value = false; }; // Some older gcc compilers don't support straight forward tests // for members, so test for ambiguity cause by the base and derived // classes both defining the member. template struct has_FlowTraits { struct Fallback { bool flow; }; struct Derived : T, Fallback { }; template static char (&f(SameType*))[1]; template static char (&f(...))[2]; public: static bool const value = sizeof(f(nullptr)) == 2; }; // Test if SequenceTraits is defined on type T template struct has_SequenceTraits : public std::integral_constant::value > { }; // Test if DocumentListTraits is defined on type T template struct has_DocumentListTraits { using Signature_size = size_t (*)(class IO &, T &); template static char test(SameType*); template static double test(...); public: static bool const value = (sizeof(test>(nullptr))==1); }; -inline bool isNumber(StringRef S) { - static const char OctalChars[] = "01234567"; - if (S.startswith("0") && - S.drop_front().find_first_not_of(OctalChars) == StringRef::npos) - return true; +inline bool isNumeric(StringRef S) { + const static auto skipDigits = [](StringRef Input) { + return Input.drop_front( + std::min(Input.find_first_not_of("0123456789"), Input.size())); + }; - if (S.startswith("0o") && - S.drop_front(2).find_first_not_of(OctalChars) == StringRef::npos) - return true; + // Make S.front() and S.drop_front().front() (if S.front() is [+-]) calls + // safe. + if (S.empty() || S.equals("+") || S.equals("-")) + return false; - static const char HexChars[] = "0123456789abcdefABCDEF"; - if (S.startswith("0x") && - S.drop_front(2).find_first_not_of(HexChars) == StringRef::npos) + if (S.equals(".nan") || S.equals(".NaN") || S.equals(".NAN")) return true; - static const char DecChars[] = "0123456789"; - if (S.find_first_not_of(DecChars) == StringRef::npos) - return true; + // Infinity and decimal numbers can be prefixed with sign. + StringRef Tail = (S.front() == '-' || S.front() == '+') ? S.drop_front() : S; - if (S.equals(".inf") || S.equals(".Inf") || S.equals(".INF")) + // Check for infinity first, because checking for hex and oct numbers is more + // expensive. + if (Tail.equals(".inf") || Tail.equals(".Inf") || Tail.equals(".INF")) return true; - Regex FloatMatcher("^(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?$"); - if (FloatMatcher.match(S)) - return true; + // Section 10.3.2 Tag Resolution + // YAML 1.2 Specification prohibits Base 8 and Base 16 numbers prefixed with + // [-+], so S should be used instead of Tail. + if (S.startswith("0o")) + return S.size() > 2 && + S.drop_front(2).find_first_not_of("01234567") == StringRef::npos; + + if (S.startswith("0x")) + return S.size() > 2 && S.drop_front(2).find_first_not_of( + "0123456789abcdefABCDEF") == StringRef::npos; + + // Parse float: [-+]? (\. [0-9]+ | [0-9]+ (\. [0-9]* )?) ([eE] [-+]? [0-9]+)? + S = Tail; + + // Handle cases when the number starts with '.' and hence needs at least one + // digit after dot (as opposed by number which has digits before the dot), but + // doesn't have one. + if (S.startswith(".") && + (S.equals(".") || + (S.size() > 1 && std::strchr("0123456789", S[1]) == nullptr))) + return false; + + if (S.startswith("E") || S.startswith("e")) + return false; + + enum ParseState { + Default, + FoundDot, + FoundExponent, + }; + ParseState State = Default; - return false; -} + S = skipDigits(S); -inline bool isNumeric(StringRef S) { - if ((S.front() == '-' || S.front() == '+') && isNumber(S.drop_front())) + // Accept decimal integer. + if (S.empty()) return true; - if (isNumber(S)) - return true; + if (S.front() == '.') { + State = FoundDot; + S = S.drop_front(); + } else if (S.front() == 'e' || S.front() == 'E') { + State = FoundExponent; + S = S.drop_front(); + } else { + return false; + } - if (S.equals(".nan") || S.equals(".NaN") || S.equals(".NAN")) - return true; + if (State == FoundDot) { + S = skipDigits(S); + if (S.empty()) + return true; + + if (S.front() == 'e' || S.front() == 'E') { + State = FoundExponent; + S = S.drop_front(); + } else { + return false; + } + } + + assert(FoundExponent && "Should have found exponent at this point."); + if (S.empty()) + return false; + + if (S.front() == '+' || S.front() == '-') { + S = S.drop_front(); + if (S.empty()) + return false; + } - return false; + return skipDigits(S).empty(); } inline bool isNull(StringRef S) { return S.equals("null") || S.equals("Null") || S.equals("NULL") || S.equals("~"); } inline bool isBool(StringRef S) { return S.equals("true") || S.equals("True") || S.equals("TRUE") || S.equals("false") || S.equals("False") || S.equals("FALSE"); } // 5.1. Character Set // The allowed character range explicitly excludes the C0 control block #x0-#x1F // (except for TAB #x9, LF #xA, and CR #xD which are allowed), DEL #x7F, the C1 // control block #x80-#x9F (except for NEL #x85 which is allowed), the surrogate // block #xD800-#xDFFF, #xFFFE, and #xFFFF. inline QuotingType needsQuotes(StringRef S) { if (S.empty()) return QuotingType::Single; if (isspace(S.front()) || isspace(S.back())) return QuotingType::Single; if (isNull(S)) return QuotingType::Single; if (isBool(S)) return QuotingType::Single; if (isNumeric(S)) return QuotingType::Single; // 7.3.3 Plain Style // Plain scalars must not begin with most indicators, as this would cause // ambiguity with other YAML constructs. static constexpr char Indicators[] = R"(-?:\,[]{}#&*!|>'"%@`)"; if (S.find_first_of(Indicators) == 0) return QuotingType::Single; QuotingType MaxQuotingNeeded = QuotingType::None; for (unsigned char C : S) { // Alphanum is safe. if (isAlnum(C)) continue; switch (C) { // Safe scalar characters. case '_': case '-': case '/': case '^': case '.': case ',': case ' ': // TAB (0x9) is allowed in unquoted strings. case 0x9: continue; // LF(0xA) and CR(0xD) may delimit values and so require at least single // quotes. case 0xA: case 0xD: MaxQuotingNeeded = QuotingType::Single; continue; // DEL (0x7F) are excluded from the allowed character range. case 0x7F: return QuotingType::Double; default: { // C0 control block (0x0 - 0x1F) is excluded from the allowed character // range. if (C <= 0x1F) return QuotingType::Double; // Always double quote UTF-8. if ((C & 0x80) != 0) return QuotingType::Double; // The character is not safe, at least simple quoting needed. MaxQuotingNeeded = QuotingType::Single; } } } return MaxQuotingNeeded; } template struct missingTraits : public std::integral_constant::value && !has_ScalarBitSetTraits::value && !has_ScalarTraits::value && !has_BlockScalarTraits::value && !has_MappingTraits::value && !has_SequenceTraits::value && !has_CustomMappingTraits::value && !has_DocumentListTraits::value> {}; template struct validatedMappingTraits : public std::integral_constant< bool, has_MappingTraits::value && has_MappingValidateTraits::value> {}; template struct unvalidatedMappingTraits : public std::integral_constant< bool, has_MappingTraits::value && !has_MappingValidateTraits::value> {}; // Base class for Input and Output. class IO { public: IO(void *Ctxt = nullptr); virtual ~IO(); virtual bool outputting() = 0; virtual unsigned beginSequence() = 0; virtual bool preflightElement(unsigned, void *&) = 0; virtual void postflightElement(void*) = 0; virtual void endSequence() = 0; virtual bool canElideEmptySequence() = 0; virtual unsigned beginFlowSequence() = 0; virtual bool preflightFlowElement(unsigned, void *&) = 0; virtual void postflightFlowElement(void*) = 0; virtual void endFlowSequence() = 0; virtual bool mapTag(StringRef Tag, bool Default=false) = 0; virtual void beginMapping() = 0; virtual void endMapping() = 0; virtual bool preflightKey(const char*, bool, bool, bool &, void *&) = 0; virtual void postflightKey(void*) = 0; virtual std::vector keys() = 0; virtual void beginFlowMapping() = 0; virtual void endFlowMapping() = 0; virtual void beginEnumScalar() = 0; virtual bool matchEnumScalar(const char*, bool) = 0; virtual bool matchEnumFallback() = 0; virtual void endEnumScalar() = 0; virtual bool beginBitSetScalar(bool &) = 0; virtual bool bitSetMatch(const char*, bool) = 0; virtual void endBitSetScalar() = 0; virtual void scalarString(StringRef &, QuotingType) = 0; virtual void blockScalarString(StringRef &) = 0; virtual void setError(const Twine &) = 0; template void enumCase(T &Val, const char* Str, const T ConstVal) { if ( matchEnumScalar(Str, outputting() && Val == ConstVal) ) { Val = ConstVal; } } // allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF template void enumCase(T &Val, const char* Str, const uint32_t ConstVal) { if ( matchEnumScalar(Str, outputting() && Val == static_cast(ConstVal)) ) { Val = ConstVal; } } template void enumFallback(T &Val) { if (matchEnumFallback()) { EmptyContext Context; // FIXME: Force integral conversion to allow strong typedefs to convert. FBT Res = static_cast(Val); yamlize(*this, Res, true, Context); Val = static_cast(static_cast(Res)); } } template void bitSetCase(T &Val, const char* Str, const T ConstVal) { if ( bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal) ) { Val = static_cast(Val | ConstVal); } } // allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF template void bitSetCase(T &Val, const char* Str, const uint32_t ConstVal) { if ( bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal) ) { Val = static_cast(Val | ConstVal); } } template void maskedBitSetCase(T &Val, const char *Str, T ConstVal, T Mask) { if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal)) Val = Val | ConstVal; } template void maskedBitSetCase(T &Val, const char *Str, uint32_t ConstVal, uint32_t Mask) { if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal)) Val = Val | ConstVal; } void *getContext(); void setContext(void *); template void mapRequired(const char *Key, T &Val) { EmptyContext Ctx; this->processKey(Key, Val, true, Ctx); } template void mapRequired(const char *Key, T &Val, Context &Ctx) { this->processKey(Key, Val, true, Ctx); } template void mapOptional(const char *Key, T &Val) { EmptyContext Ctx; mapOptionalWithContext(Key, Val, Ctx); } template void mapOptional(const char *Key, T &Val, const T &Default) { EmptyContext Ctx; mapOptionalWithContext(Key, Val, Default, Ctx); } template typename std::enable_if::value, void>::type mapOptionalWithContext(const char *Key, T &Val, Context &Ctx) { // omit key/value instead of outputting empty sequence if (this->canElideEmptySequence() && !(Val.begin() != Val.end())) return; this->processKey(Key, Val, false, Ctx); } template void mapOptionalWithContext(const char *Key, Optional &Val, Context &Ctx) { this->processKeyWithDefault(Key, Val, Optional(), /*Required=*/false, Ctx); } template typename std::enable_if::value, void>::type mapOptionalWithContext(const char *Key, T &Val, Context &Ctx) { this->processKey(Key, Val, false, Ctx); } template void mapOptionalWithContext(const char *Key, T &Val, const T &Default, Context &Ctx) { this->processKeyWithDefault(Key, Val, Default, false, Ctx); } private: template void processKeyWithDefault(const char *Key, Optional &Val, const Optional &DefaultValue, bool Required, Context &Ctx) { assert(DefaultValue.hasValue() == false && "Optional shouldn't have a value!"); void *SaveInfo; bool UseDefault = true; const bool sameAsDefault = outputting() && !Val.hasValue(); if (!outputting() && !Val.hasValue()) Val = T(); if (Val.hasValue() && this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) { yamlize(*this, Val.getValue(), Required, Ctx); this->postflightKey(SaveInfo); } else { if (UseDefault) Val = DefaultValue; } } template void processKeyWithDefault(const char *Key, T &Val, const T &DefaultValue, bool Required, Context &Ctx) { void *SaveInfo; bool UseDefault; const bool sameAsDefault = outputting() && Val == DefaultValue; if ( this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo) ) { yamlize(*this, Val, Required, Ctx); this->postflightKey(SaveInfo); } else { if ( UseDefault ) Val = DefaultValue; } } template void processKey(const char *Key, T &Val, bool Required, Context &Ctx) { void *SaveInfo; bool UseDefault; if ( this->preflightKey(Key, Required, false, UseDefault, SaveInfo) ) { yamlize(*this, Val, Required, Ctx); this->postflightKey(SaveInfo); } } private: void *Ctxt; }; namespace detail { template void doMapping(IO &io, T &Val, Context &Ctx) { MappingContextTraits::mapping(io, Val, Ctx); } template void doMapping(IO &io, T &Val, EmptyContext &Ctx) { MappingTraits::mapping(io, Val); } } // end namespace detail template typename std::enable_if::value, void>::type yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { io.beginEnumScalar(); ScalarEnumerationTraits::enumeration(io, Val); io.endEnumScalar(); } template typename std::enable_if::value, void>::type yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { bool DoClear; if ( io.beginBitSetScalar(DoClear) ) { if ( DoClear ) Val = static_cast(0); ScalarBitSetTraits::bitset(io, Val); io.endBitSetScalar(); } } template typename std::enable_if::value, void>::type yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { if ( io.outputting() ) { std::string Storage; raw_string_ostream Buffer(Storage); ScalarTraits::output(Val, io.getContext(), Buffer); StringRef Str = Buffer.str(); io.scalarString(Str, ScalarTraits::mustQuote(Str)); } else { StringRef Str; io.scalarString(Str, ScalarTraits::mustQuote(Str)); StringRef Result = ScalarTraits::input(Str, io.getContext(), Val); if ( !Result.empty() ) { io.setError(Twine(Result)); } } } template typename std::enable_if::value, void>::type yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) { if (YamlIO.outputting()) { std::string Storage; raw_string_ostream Buffer(Storage); BlockScalarTraits::output(Val, YamlIO.getContext(), Buffer); StringRef Str = Buffer.str(); YamlIO.blockScalarString(Str); } else { StringRef Str; YamlIO.blockScalarString(Str); StringRef Result = BlockScalarTraits::input(Str, YamlIO.getContext(), Val); if (!Result.empty()) YamlIO.setError(Twine(Result)); } } template typename std::enable_if::value, void>::type yamlize(IO &io, T &Val, bool, Context &Ctx) { if (has_FlowTraits>::value) io.beginFlowMapping(); else io.beginMapping(); if (io.outputting()) { StringRef Err = MappingTraits::validate(io, Val); if (!Err.empty()) { errs() << Err << "\n"; assert(Err.empty() && "invalid struct trying to be written as yaml"); } } detail::doMapping(io, Val, Ctx); if (!io.outputting()) { StringRef Err = MappingTraits::validate(io, Val); if (!Err.empty()) io.setError(Err); } if (has_FlowTraits>::value) io.endFlowMapping(); else io.endMapping(); } template typename std::enable_if::value, void>::type yamlize(IO &io, T &Val, bool, Context &Ctx) { if (has_FlowTraits>::value) { io.beginFlowMapping(); detail::doMapping(io, Val, Ctx); io.endFlowMapping(); } else { io.beginMapping(); detail::doMapping(io, Val, Ctx); io.endMapping(); } } template typename std::enable_if::value, void>::type yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { if ( io.outputting() ) { io.beginMapping(); CustomMappingTraits::output(io, Val); io.endMapping(); } else { io.beginMapping(); for (StringRef key : io.keys()) CustomMappingTraits::inputOne(io, key, Val); io.endMapping(); } } template typename std::enable_if::value, void>::type yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { char missing_yaml_trait_for_type[sizeof(MissingTrait)]; } template typename std::enable_if::value, void>::type yamlize(IO &io, T &Seq, bool, Context &Ctx) { if ( has_FlowTraits< SequenceTraits>::value ) { unsigned incnt = io.beginFlowSequence(); unsigned count = io.outputting() ? SequenceTraits::size(io, Seq) : incnt; for(unsigned i=0; i < count; ++i) { void *SaveInfo; if ( io.preflightFlowElement(i, SaveInfo) ) { yamlize(io, SequenceTraits::element(io, Seq, i), true, Ctx); io.postflightFlowElement(SaveInfo); } } io.endFlowSequence(); } else { unsigned incnt = io.beginSequence(); unsigned count = io.outputting() ? SequenceTraits::size(io, Seq) : incnt; for(unsigned i=0; i < count; ++i) { void *SaveInfo; if ( io.preflightElement(i, SaveInfo) ) { yamlize(io, SequenceTraits::element(io, Seq, i), true, Ctx); io.postflightElement(SaveInfo); } } io.endSequence(); } } template<> struct ScalarTraits { static void output(const bool &, void* , raw_ostream &); static StringRef input(StringRef, void *, bool &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits { static void output(const StringRef &, void *, raw_ostream &); static StringRef input(StringRef, void *, StringRef &); static QuotingType mustQuote(StringRef S) { return needsQuotes(S); } }; template<> struct ScalarTraits { static void output(const std::string &, void *, raw_ostream &); static StringRef input(StringRef, void *, std::string &); static QuotingType mustQuote(StringRef S) { return needsQuotes(S); } }; template<> struct ScalarTraits { static void output(const uint8_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, uint8_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits { static void output(const uint16_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, uint16_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits { static void output(const uint32_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, uint32_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits { static void output(const uint64_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, uint64_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits { static void output(const int8_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, int8_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits { static void output(const int16_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, int16_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits { static void output(const int32_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, int32_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits { static void output(const int64_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, int64_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits { static void output(const float &, void *, raw_ostream &); static StringRef input(StringRef, void *, float &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits { static void output(const double &, void *, raw_ostream &); static StringRef input(StringRef, void *, double &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; // For endian types, we just use the existing ScalarTraits for the underlying // type. This way endian aware types are supported whenever a ScalarTraits // is defined for the underlying type. template struct ScalarTraits> { using endian_type = support::detail::packed_endian_specific_integral; static void output(const endian_type &E, void *Ctx, raw_ostream &Stream) { ScalarTraits::output(static_cast(E), Ctx, Stream); } static StringRef input(StringRef Str, void *Ctx, endian_type &E) { value_type V; auto R = ScalarTraits::input(Str, Ctx, V); E = static_cast(V); return R; } static QuotingType mustQuote(StringRef Str) { return ScalarTraits::mustQuote(Str); } }; // Utility for use within MappingTraits<>::mapping() method // to [de]normalize an object for use with YAML conversion. template struct MappingNormalization { MappingNormalization(IO &i_o, TFinal &Obj) : io(i_o), BufPtr(nullptr), Result(Obj) { if ( io.outputting() ) { BufPtr = new (&Buffer) TNorm(io, Obj); } else { BufPtr = new (&Buffer) TNorm(io); } } ~MappingNormalization() { if ( ! io.outputting() ) { Result = BufPtr->denormalize(io); } BufPtr->~TNorm(); } TNorm* operator->() { return BufPtr; } private: using Storage = AlignedCharArrayUnion; Storage Buffer; IO &io; TNorm *BufPtr; TFinal &Result; }; // Utility for use within MappingTraits<>::mapping() method // to [de]normalize an object for use with YAML conversion. template struct MappingNormalizationHeap { MappingNormalizationHeap(IO &i_o, TFinal &Obj, BumpPtrAllocator *allocator) : io(i_o), Result(Obj) { if ( io.outputting() ) { BufPtr = new (&Buffer) TNorm(io, Obj); } else if (allocator) { BufPtr = allocator->Allocate(); new (BufPtr) TNorm(io); } else { BufPtr = new TNorm(io); } } ~MappingNormalizationHeap() { if ( io.outputting() ) { BufPtr->~TNorm(); } else { Result = BufPtr->denormalize(io); } } TNorm* operator->() { return BufPtr; } private: using Storage = AlignedCharArrayUnion; Storage Buffer; IO &io; TNorm *BufPtr = nullptr; TFinal &Result; }; /// /// The Input class is used to parse a yaml document into in-memory structs /// and vectors. /// /// It works by using YAMLParser to do a syntax parse of the entire yaml /// document, then the Input class builds a graph of HNodes which wraps /// each yaml Node. The extra layer is buffering. The low level yaml /// parser only lets you look at each node once. The buffering layer lets /// you search and interate multiple times. This is necessary because /// the mapRequired() method calls may not be in the same order /// as the keys in the document. /// class Input : public IO { public: // Construct a yaml Input object from a StringRef and optional // user-data. The DiagHandler can be specified to provide // alternative error reporting. Input(StringRef InputContent, void *Ctxt = nullptr, SourceMgr::DiagHandlerTy DiagHandler = nullptr, void *DiagHandlerCtxt = nullptr); Input(MemoryBufferRef Input, void *Ctxt = nullptr, SourceMgr::DiagHandlerTy DiagHandler = nullptr, void *DiagHandlerCtxt = nullptr); ~Input() override; // Check if there was an syntax or semantic error during parsing. std::error_code error(); private: bool outputting() override; bool mapTag(StringRef, bool) override; void beginMapping() override; void endMapping() override; bool preflightKey(const char *, bool, bool, bool &, void *&) override; void postflightKey(void *) override; std::vector keys() override; void beginFlowMapping() override; void endFlowMapping() override; unsigned beginSequence() override; void endSequence() override; bool preflightElement(unsigned index, void *&) override; void postflightElement(void *) override; unsigned beginFlowSequence() override; bool preflightFlowElement(unsigned , void *&) override; void postflightFlowElement(void *) override; void endFlowSequence() override; void beginEnumScalar() override; bool matchEnumScalar(const char*, bool) override; bool matchEnumFallback() override; void endEnumScalar() override; bool beginBitSetScalar(bool &) override; bool bitSetMatch(const char *, bool ) override; void endBitSetScalar() override; void scalarString(StringRef &, QuotingType) override; void blockScalarString(StringRef &) override; void setError(const Twine &message) override; bool canElideEmptySequence() override; class HNode { virtual void anchor(); public: HNode(Node *n) : _node(n) { } virtual ~HNode() = default; static bool classof(const HNode *) { return true; } Node *_node; }; class EmptyHNode : public HNode { void anchor() override; public: EmptyHNode(Node *n) : HNode(n) { } static bool classof(const HNode *n) { return NullNode::classof(n->_node); } static bool classof(const EmptyHNode *) { return true; } }; class ScalarHNode : public HNode { void anchor() override; public: ScalarHNode(Node *n, StringRef s) : HNode(n), _value(s) { } StringRef value() const { return _value; } static bool classof(const HNode *n) { return ScalarNode::classof(n->_node) || BlockScalarNode::classof(n->_node); } static bool classof(const ScalarHNode *) { return true; } protected: StringRef _value; }; class MapHNode : public HNode { void anchor() override; public: MapHNode(Node *n) : HNode(n) { } static bool classof(const HNode *n) { return MappingNode::classof(n->_node); } static bool classof(const MapHNode *) { return true; } using NameToNode = StringMap>; NameToNode Mapping; SmallVector ValidKeys; }; class SequenceHNode : public HNode { void anchor() override; public: SequenceHNode(Node *n) : HNode(n) { } static bool classof(const HNode *n) { return SequenceNode::classof(n->_node); } static bool classof(const SequenceHNode *) { return true; } std::vector> Entries; }; std::unique_ptr createHNodes(Node *node); void setError(HNode *hnode, const Twine &message); void setError(Node *node, const Twine &message); public: // These are only used by operator>>. They could be private // if those templated things could be made friends. bool setCurrentDocument(); bool nextDocument(); /// Returns the current node that's being parsed by the YAML Parser. const Node *getCurrentNode() const; private: SourceMgr SrcMgr; // must be before Strm std::unique_ptr Strm; std::unique_ptr TopNode; std::error_code EC; BumpPtrAllocator StringAllocator; document_iterator DocIterator; std::vector BitValuesUsed; HNode *CurrentNode = nullptr; bool ScalarMatchFound; }; /// /// The Output class is used to generate a yaml document from in-memory structs /// and vectors. /// class Output : public IO { public: Output(raw_ostream &, void *Ctxt = nullptr, int WrapColumn = 70); ~Output() override; /// Set whether or not to output optional values which are equal /// to the default value. By default, when outputting if you attempt /// to write a value that is equal to the default, the value gets ignored. /// Sometimes, it is useful to be able to see these in the resulting YAML /// anyway. void setWriteDefaultValues(bool Write) { WriteDefaultValues = Write; } bool outputting() override; bool mapTag(StringRef, bool) override; void beginMapping() override; void endMapping() override; bool preflightKey(const char *key, bool, bool, bool &, void *&) override; void postflightKey(void *) override; std::vector keys() override; void beginFlowMapping() override; void endFlowMapping() override; unsigned beginSequence() override; void endSequence() override; bool preflightElement(unsigned, void *&) override; void postflightElement(void *) override; unsigned beginFlowSequence() override; bool preflightFlowElement(unsigned, void *&) override; void postflightFlowElement(void *) override; void endFlowSequence() override; void beginEnumScalar() override; bool matchEnumScalar(const char*, bool) override; bool matchEnumFallback() override; void endEnumScalar() override; bool beginBitSetScalar(bool &) override; bool bitSetMatch(const char *, bool ) override; void endBitSetScalar() override; void scalarString(StringRef &, QuotingType) override; void blockScalarString(StringRef &) override; void setError(const Twine &message) override; bool canElideEmptySequence() override; // These are only used by operator<<. They could be private // if that templated operator could be made a friend. void beginDocuments(); bool preflightDocument(unsigned); void postflightDocument(); void endDocuments(); private: void output(StringRef s); void outputUpToEndOfLine(StringRef s); void newLineCheck(); void outputNewLine(); void paddedKey(StringRef key); void flowKey(StringRef Key); enum InState { inSeq, inFlowSeq, inMapFirstKey, inMapOtherKey, inFlowMapFirstKey, inFlowMapOtherKey }; raw_ostream &Out; int WrapColumn; SmallVector StateStack; int Column = 0; int ColumnAtFlowStart = 0; int ColumnAtMapFlowStart = 0; bool NeedBitValueComma = false; bool NeedFlowSequenceComma = false; bool EnumerationMatchFound = false; bool NeedsNewLine = false; bool WriteDefaultValues = false; }; /// YAML I/O does conversion based on types. But often native data types /// are just a typedef of built in intergral types (e.g. int). But the C++ /// type matching system sees through the typedef and all the typedefed types /// look like a built in type. This will cause the generic YAML I/O conversion /// to be used. To provide better control over the YAML conversion, you can /// use this macro instead of typedef. It will create a class with one field /// and automatic conversion operators to and from the base type. /// Based on BOOST_STRONG_TYPEDEF #define LLVM_YAML_STRONG_TYPEDEF(_base, _type) \ struct _type { \ _type() = default; \ _type(const _base v) : value(v) {} \ _type(const _type &v) = default; \ _type &operator=(const _type &rhs) = default; \ _type &operator=(const _base &rhs) { value = rhs; return *this; } \ operator const _base & () const { return value; } \ bool operator==(const _type &rhs) const { return value == rhs.value; } \ bool operator==(const _base &rhs) const { return value == rhs; } \ bool operator<(const _type &rhs) const { return value < rhs.value; } \ _base value; \ using BaseType = _base; \ }; /// /// Use these types instead of uintXX_t in any mapping to have /// its yaml output formatted as hexadecimal. /// LLVM_YAML_STRONG_TYPEDEF(uint8_t, Hex8) LLVM_YAML_STRONG_TYPEDEF(uint16_t, Hex16) LLVM_YAML_STRONG_TYPEDEF(uint32_t, Hex32) LLVM_YAML_STRONG_TYPEDEF(uint64_t, Hex64) template<> struct ScalarTraits { static void output(const Hex8 &, void *, raw_ostream &); static StringRef input(StringRef, void *, Hex8 &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits { static void output(const Hex16 &, void *, raw_ostream &); static StringRef input(StringRef, void *, Hex16 &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits { static void output(const Hex32 &, void *, raw_ostream &); static StringRef input(StringRef, void *, Hex32 &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits { static void output(const Hex64 &, void *, raw_ostream &); static StringRef input(StringRef, void *, Hex64 &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; // Define non-member operator>> so that Input can stream in a document list. template inline typename std::enable_if::value, Input &>::type operator>>(Input &yin, T &docList) { int i = 0; EmptyContext Ctx; while ( yin.setCurrentDocument() ) { yamlize(yin, DocumentListTraits::element(yin, docList, i), true, Ctx); if ( yin.error() ) return yin; yin.nextDocument(); ++i; } return yin; } // Define non-member operator>> so that Input can stream in a map as a document. template inline typename std::enable_if::value, Input &>::type operator>>(Input &yin, T &docMap) { EmptyContext Ctx; yin.setCurrentDocument(); yamlize(yin, docMap, true, Ctx); return yin; } // Define non-member operator>> so that Input can stream in a sequence as // a document. template inline typename std::enable_if::value, Input &>::type operator>>(Input &yin, T &docSeq) { EmptyContext Ctx; if (yin.setCurrentDocument()) yamlize(yin, docSeq, true, Ctx); return yin; } // Define non-member operator>> so that Input can stream in a block scalar. template inline typename std::enable_if::value, Input &>::type operator>>(Input &In, T &Val) { EmptyContext Ctx; if (In.setCurrentDocument()) yamlize(In, Val, true, Ctx); return In; } // Define non-member operator>> so that Input can stream in a string map. template inline typename std::enable_if::value, Input &>::type operator>>(Input &In, T &Val) { EmptyContext Ctx; if (In.setCurrentDocument()) yamlize(In, Val, true, Ctx); return In; } // Provide better error message about types missing a trait specialization template inline typename std::enable_if::value, Input &>::type operator>>(Input &yin, T &docSeq) { char missing_yaml_trait_for_type[sizeof(MissingTrait)]; return yin; } // Define non-member operator<< so that Output can stream out document list. template inline typename std::enable_if::value, Output &>::type operator<<(Output &yout, T &docList) { EmptyContext Ctx; yout.beginDocuments(); const size_t count = DocumentListTraits::size(yout, docList); for(size_t i=0; i < count; ++i) { if ( yout.preflightDocument(i) ) { yamlize(yout, DocumentListTraits::element(yout, docList, i), true, Ctx); yout.postflightDocument(); } } yout.endDocuments(); return yout; } // 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) { EmptyContext Ctx; yout.beginDocuments(); if ( yout.preflightDocument(0) ) { yamlize(yout, map, true, Ctx); yout.postflightDocument(); } yout.endDocuments(); return yout; } // Define non-member operator<< so that Output can stream out a sequence. template inline typename std::enable_if::value, Output &>::type operator<<(Output &yout, T &seq) { EmptyContext Ctx; yout.beginDocuments(); if ( yout.preflightDocument(0) ) { yamlize(yout, seq, true, Ctx); yout.postflightDocument(); } yout.endDocuments(); return yout; } // Define non-member operator<< so that Output can stream out a block scalar. template inline typename std::enable_if::value, Output &>::type operator<<(Output &Out, T &Val) { EmptyContext Ctx; Out.beginDocuments(); if (Out.preflightDocument(0)) { yamlize(Out, Val, true, Ctx); Out.postflightDocument(); } Out.endDocuments(); return Out; } // Define non-member operator<< so that Output can stream out a string map. template inline typename std::enable_if::value, Output &>::type operator<<(Output &Out, T &Val) { EmptyContext Ctx; Out.beginDocuments(); if (Out.preflightDocument(0)) { yamlize(Out, Val, true, Ctx); Out.postflightDocument(); } Out.endDocuments(); return Out; } // Provide better error message about types missing a trait specialization template inline typename std::enable_if::value, Output &>::type operator<<(Output &yout, T &seq) { char missing_yaml_trait_for_type[sizeof(MissingTrait)]; return yout; } template struct IsFlowSequenceBase {}; template <> struct IsFlowSequenceBase { static const bool flow = true; }; template struct SequenceTraitsImpl : IsFlowSequenceBase { private: using type = typename T::value_type; public: static size_t size(IO &io, T &seq) { return seq.size(); } static type &element(IO &io, T &seq, size_t index) { if (index >= seq.size()) seq.resize(index + 1); return seq[index]; } }; // Simple helper to check an expression can be used as a bool-valued template // argument. template struct CheckIsBool { static const bool value = true; }; // If T has SequenceElementTraits, then vector and SmallVector have // SequenceTraits that do the obvious thing. template struct SequenceTraits, typename std::enable_if::flow>::value>::type> : SequenceTraitsImpl, SequenceElementTraits::flow> {}; template struct SequenceTraits, typename std::enable_if::flow>::value>::type> : SequenceTraitsImpl, SequenceElementTraits::flow> {}; // Sequences of fundamental types use flow formatting. template struct SequenceElementTraits< T, typename std::enable_if::value>::type> { static const bool flow = true; }; // Sequences of strings use block formatting. template<> struct SequenceElementTraits { static const bool flow = false; }; template<> struct SequenceElementTraits { static const bool flow = false; }; template<> struct SequenceElementTraits> { static const bool flow = false; }; /// Implementation of CustomMappingTraits for std::map. template struct StdMapStringCustomMappingTraitsImpl { using map_type = std::map; static void inputOne(IO &io, StringRef key, map_type &v) { io.mapRequired(key.str().c_str(), v[key]); } static void output(IO &io, map_type &v) { for (auto &p : v) io.mapRequired(p.first.c_str(), p.second); } }; } // end namespace yaml } // end namespace llvm #define LLVM_YAML_IS_SEQUENCE_VECTOR_IMPL(TYPE, FLOW) \ namespace llvm { \ namespace yaml { \ static_assert( \ !std::is_fundamental::value && \ !std::is_same::value && \ !std::is_same::value, \ "only use LLVM_YAML_IS_SEQUENCE_VECTOR for types you control"); \ template <> struct SequenceElementTraits { \ static const bool flow = FLOW; \ }; \ } \ } /// Utility for declaring that a std::vector of a particular type /// should be considered a YAML sequence. #define LLVM_YAML_IS_SEQUENCE_VECTOR(type) \ LLVM_YAML_IS_SEQUENCE_VECTOR_IMPL(type, false) /// Utility for declaring that a std::vector of a particular type /// should be considered a YAML flow sequence. #define LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(type) \ LLVM_YAML_IS_SEQUENCE_VECTOR_IMPL(type, true) #define LLVM_YAML_DECLARE_MAPPING_TRAITS(Type) \ namespace llvm { \ namespace yaml { \ template <> struct MappingTraits { \ static void mapping(IO &IO, Type &Obj); \ }; \ } \ } #define LLVM_YAML_DECLARE_ENUM_TRAITS(Type) \ namespace llvm { \ namespace yaml { \ template <> struct ScalarEnumerationTraits { \ static void enumeration(IO &io, Type &Value); \ }; \ } \ } #define LLVM_YAML_DECLARE_BITSET_TRAITS(Type) \ namespace llvm { \ namespace yaml { \ template <> struct ScalarBitSetTraits { \ static void bitset(IO &IO, Type &Options); \ }; \ } \ } #define LLVM_YAML_DECLARE_SCALAR_TRAITS(Type, MustQuote) \ namespace llvm { \ namespace yaml { \ template <> struct ScalarTraits { \ static void output(const Type &Value, void *ctx, raw_ostream &Out); \ static StringRef input(StringRef Scalar, void *ctxt, Type &Value); \ static QuotingType mustQuote(StringRef) { return MustQuote; } \ }; \ } \ } /// Utility for declaring that a std::vector of a particular type /// should be considered a YAML document list. #define LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(_type) \ namespace llvm { \ namespace yaml { \ template \ struct DocumentListTraits> \ : public SequenceTraitsImpl, false> {}; \ template <> \ struct DocumentListTraits> \ : public SequenceTraitsImpl, false> {}; \ } \ } /// Utility for declaring that std::map should be considered /// a YAML map. #define LLVM_YAML_IS_STRING_MAP(_type) \ namespace llvm { \ namespace yaml { \ template <> \ struct CustomMappingTraits> \ : public StdMapStringCustomMappingTraitsImpl<_type> {}; \ } \ } #endif // LLVM_SUPPORT_YAMLTRAITS_H diff --git a/llvm/tools/llvm-yaml-numeric-parser-fuzzer/CMakeLists.txt b/llvm/tools/llvm-yaml-numeric-parser-fuzzer/CMakeLists.txt new file mode 100644 index 000000000000..34027431697f --- /dev/null +++ b/llvm/tools/llvm-yaml-numeric-parser-fuzzer/CMakeLists.txt @@ -0,0 +1,9 @@ +set(LLVM_LINK_COMPONENTS + Support + FuzzMutate +) + +add_llvm_fuzzer(llvm-yaml-numeric-parser-fuzzer + yaml-numeric-parser-fuzzer.cpp + DUMMY_MAIN DummyYAMLNumericParserFuzzer.cpp + ) diff --git a/llvm/tools/llvm-yaml-numeric-parser-fuzzer/DummyYAMLNumericParserFuzzer.cpp b/llvm/tools/llvm-yaml-numeric-parser-fuzzer/DummyYAMLNumericParserFuzzer.cpp new file mode 100644 index 000000000000..3396168acec0 --- /dev/null +++ b/llvm/tools/llvm-yaml-numeric-parser-fuzzer/DummyYAMLNumericParserFuzzer.cpp @@ -0,0 +1,19 @@ +//===--- DummyYAMLNumericParserFuzzer.cpp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of main so we can build and test without linking libFuzzer. +// +//===----------------------------------------------------------------------===// + +#include "llvm/FuzzMutate/FuzzerCLI.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +int main(int argc, char *argv[]) { + return llvm::runFuzzerOnInputs(argc, argv, LLVMFuzzerTestOneInput); +} diff --git a/llvm/tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp b/llvm/tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp new file mode 100644 index 000000000000..1510aeb9a37e --- /dev/null +++ b/llvm/tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp @@ -0,0 +1,47 @@ +//===--- special-case-list-fuzzer.cpp - Fuzzer for special case lists -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/YAMLTraits.h" +#include +#include + +llvm::Regex Infinity("^[-+]?(\\.inf|\\.Inf|\\.INF)$"); +llvm::Regex Base8("^0o[0-7]+$"); +llvm::Regex Base16("^0x[0-9a-fA-F]+$"); +llvm::Regex Float("^[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?$"); + +inline bool isNumericRegex(llvm::StringRef S) { + + if (S.equals(".nan") || S.equals(".NaN") || S.equals(".NAN")) + return true; + + if (Infinity.match(S)) + return true; + + if (Base8.match(S)) + return true; + + if (Base16.match(S)) + return true; + + if (Float.match(S)) + return true; + + return false; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + std::string Input(reinterpret_cast(Data), Size); + Input.erase(std::remove(Input.begin(), Input.end(), 0), Input.end()); + if (!Input.empty() && llvm::yaml::isNumeric(Input) != isNumericRegex(Input)) + __builtin_trap(); + return 0; +} diff --git a/llvm/unittests/Support/YAMLIOTest.cpp b/llvm/unittests/Support/YAMLIOTest.cpp index 133648065240..4530482ec809 100644 --- a/llvm/unittests/Support/YAMLIOTest.cpp +++ b/llvm/unittests/Support/YAMLIOTest.cpp @@ -1,2571 +1,2642 @@ //===- unittest/Support/YAMLIOTest.cpp ------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Format.h" #include "llvm/Support/YAMLTraits.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +using llvm::yaml::Hex16; +using llvm::yaml::Hex32; +using llvm::yaml::Hex64; +using llvm::yaml::Hex8; using llvm::yaml::Input; -using llvm::yaml::Output; using llvm::yaml::IO; -using llvm::yaml::MappingTraits; +using llvm::yaml::isNumeric; using llvm::yaml::MappingNormalization; +using llvm::yaml::MappingTraits; +using llvm::yaml::Output; using llvm::yaml::ScalarTraits; -using llvm::yaml::Hex8; -using llvm::yaml::Hex16; -using llvm::yaml::Hex32; -using llvm::yaml::Hex64; using ::testing::StartsWith; static void suppressErrorMessages(const llvm::SMDiagnostic &, void *) { } //===----------------------------------------------------------------------===// // Test MappingTraits //===----------------------------------------------------------------------===// struct FooBar { int foo; int bar; }; typedef std::vector FooBarSequence; LLVM_YAML_IS_SEQUENCE_VECTOR(FooBar) struct FooBarContainer { FooBarSequence fbs; }; namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, FooBar& fb) { io.mapRequired("foo", fb.foo); io.mapRequired("bar", fb.bar); } }; template <> struct MappingTraits { static void mapping(IO &io, FooBarContainer &fb) { io.mapRequired("fbs", fb.fbs); } }; } } // // Test the reading of a yaml mapping // TEST(YAMLIO, TestMapRead) { FooBar doc; { Input yin("---\nfoo: 3\nbar: 5\n...\n"); yin >> doc; EXPECT_FALSE(yin.error()); EXPECT_EQ(doc.foo, 3); EXPECT_EQ(doc.bar, 5); } { Input yin("{foo: 3, bar: 5}"); yin >> doc; EXPECT_FALSE(yin.error()); EXPECT_EQ(doc.foo, 3); EXPECT_EQ(doc.bar, 5); } } TEST(YAMLIO, TestMalformedMapRead) { FooBar doc; Input yin("{foo: 3; bar: 5}", nullptr, suppressErrorMessages); yin >> doc; EXPECT_TRUE(!!yin.error()); } // // Test the reading of a yaml sequence of mappings // TEST(YAMLIO, TestSequenceMapRead) { FooBarSequence seq; Input yin("---\n - foo: 3\n bar: 5\n - foo: 7\n bar: 9\n...\n"); yin >> seq; EXPECT_FALSE(yin.error()); EXPECT_EQ(seq.size(), 2UL); FooBar& map1 = seq[0]; FooBar& map2 = seq[1]; EXPECT_EQ(map1.foo, 3); EXPECT_EQ(map1.bar, 5); EXPECT_EQ(map2.foo, 7); EXPECT_EQ(map2.bar, 9); } // // Test the reading of a map containing a yaml sequence of mappings // TEST(YAMLIO, TestContainerSequenceMapRead) { { FooBarContainer cont; Input yin2("---\nfbs:\n - foo: 3\n bar: 5\n - foo: 7\n bar: 9\n...\n"); yin2 >> cont; EXPECT_FALSE(yin2.error()); EXPECT_EQ(cont.fbs.size(), 2UL); EXPECT_EQ(cont.fbs[0].foo, 3); EXPECT_EQ(cont.fbs[0].bar, 5); EXPECT_EQ(cont.fbs[1].foo, 7); EXPECT_EQ(cont.fbs[1].bar, 9); } { FooBarContainer cont; Input yin("---\nfbs:\n...\n"); yin >> cont; // Okay: Empty node represents an empty array. EXPECT_FALSE(yin.error()); EXPECT_EQ(cont.fbs.size(), 0UL); } { FooBarContainer cont; Input yin("---\nfbs: !!null null\n...\n"); yin >> cont; // Okay: null represents an empty array. EXPECT_FALSE(yin.error()); EXPECT_EQ(cont.fbs.size(), 0UL); } { FooBarContainer cont; Input yin("---\nfbs: ~\n...\n"); yin >> cont; // Okay: null represents an empty array. EXPECT_FALSE(yin.error()); EXPECT_EQ(cont.fbs.size(), 0UL); } { FooBarContainer cont; Input yin("---\nfbs: null\n...\n"); yin >> cont; // Okay: null represents an empty array. EXPECT_FALSE(yin.error()); EXPECT_EQ(cont.fbs.size(), 0UL); } } // // Test the reading of a map containing a malformed yaml sequence // TEST(YAMLIO, TestMalformedContainerSequenceMapRead) { { FooBarContainer cont; Input yin("---\nfbs:\n foo: 3\n bar: 5\n...\n", nullptr, suppressErrorMessages); yin >> cont; // Error: fbs is not a sequence. EXPECT_TRUE(!!yin.error()); EXPECT_EQ(cont.fbs.size(), 0UL); } { FooBarContainer cont; Input yin("---\nfbs: 'scalar'\n...\n", nullptr, suppressErrorMessages); yin >> cont; // This should be an error. EXPECT_TRUE(!!yin.error()); EXPECT_EQ(cont.fbs.size(), 0UL); } } // // Test writing then reading back a sequence of mappings // TEST(YAMLIO, TestSequenceMapWriteAndRead) { std::string intermediate; { FooBar entry1; entry1.foo = 10; entry1.bar = -3; FooBar entry2; entry2.foo = 257; entry2.bar = 0; FooBarSequence seq; seq.push_back(entry1); seq.push_back(entry2); llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << seq; } { Input yin(intermediate); FooBarSequence seq2; yin >> seq2; EXPECT_FALSE(yin.error()); EXPECT_EQ(seq2.size(), 2UL); FooBar& map1 = seq2[0]; FooBar& map2 = seq2[1]; EXPECT_EQ(map1.foo, 10); EXPECT_EQ(map1.bar, -3); EXPECT_EQ(map2.foo, 257); EXPECT_EQ(map2.bar, 0); } } // // Test YAML filename handling. // static void testErrorFilename(const llvm::SMDiagnostic &Error, void *) { EXPECT_EQ(Error.getFilename(), "foo.yaml"); } TEST(YAMLIO, TestGivenFilename) { auto Buffer = llvm::MemoryBuffer::getMemBuffer("{ x: 42 }", "foo.yaml"); Input yin(*Buffer, nullptr, testErrorFilename); FooBar Value; yin >> Value; EXPECT_TRUE(!!yin.error()); } struct WithStringField { std::string str1; std::string str2; std::string str3; }; namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, WithStringField &fb) { io.mapRequired("str1", fb.str1); io.mapRequired("str2", fb.str2); io.mapRequired("str3", fb.str3); } }; } // namespace yaml } // namespace llvm TEST(YAMLIO, MultilineStrings) { WithStringField Original; Original.str1 = "a multiline string\nfoobarbaz"; Original.str2 = "another one\rfoobarbaz"; Original.str3 = "a one-line string"; std::string Serialized; { llvm::raw_string_ostream OS(Serialized); Output YOut(OS); YOut << Original; } auto Expected = "---\n" "str1: 'a multiline string\n" "foobarbaz'\n" "str2: 'another one\r" "foobarbaz'\n" "str3: a one-line string\n" "...\n"; ASSERT_EQ(Serialized, Expected); // Also check it parses back without the errors. WithStringField Deserialized; { Input YIn(Serialized); YIn >> Deserialized; ASSERT_FALSE(YIn.error()) << "Parsing error occurred during deserialization. Serialized string:\n" << Serialized; } EXPECT_EQ(Original.str1, Deserialized.str1); EXPECT_EQ(Original.str2, Deserialized.str2); EXPECT_EQ(Original.str3, Deserialized.str3); } TEST(YAMLIO, NoQuotesForTab) { WithStringField WithTab; WithTab.str1 = "aba\tcaba"; std::string Serialized; { llvm::raw_string_ostream OS(Serialized); Output YOut(OS); YOut << WithTab; } auto ExpectedPrefix = "---\n" "str1: aba\tcaba\n"; EXPECT_THAT(Serialized, StartsWith(ExpectedPrefix)); } //===----------------------------------------------------------------------===// // Test built-in types //===----------------------------------------------------------------------===// struct BuiltInTypes { llvm::StringRef str; std::string stdstr; uint64_t u64; uint32_t u32; uint16_t u16; uint8_t u8; bool b; int64_t s64; int32_t s32; int16_t s16; int8_t s8; float f; double d; Hex8 h8; Hex16 h16; Hex32 h32; Hex64 h64; }; namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, BuiltInTypes& bt) { io.mapRequired("str", bt.str); io.mapRequired("stdstr", bt.stdstr); io.mapRequired("u64", bt.u64); io.mapRequired("u32", bt.u32); io.mapRequired("u16", bt.u16); io.mapRequired("u8", bt.u8); io.mapRequired("b", bt.b); io.mapRequired("s64", bt.s64); io.mapRequired("s32", bt.s32); io.mapRequired("s16", bt.s16); io.mapRequired("s8", bt.s8); io.mapRequired("f", bt.f); io.mapRequired("d", bt.d); io.mapRequired("h8", bt.h8); io.mapRequired("h16", bt.h16); io.mapRequired("h32", bt.h32); io.mapRequired("h64", bt.h64); } }; } } // // Test the reading of all built-in scalar conversions // TEST(YAMLIO, TestReadBuiltInTypes) { BuiltInTypes map; Input yin("---\n" "str: hello there\n" "stdstr: hello where?\n" "u64: 5000000000\n" "u32: 4000000000\n" "u16: 65000\n" "u8: 255\n" "b: false\n" "s64: -5000000000\n" "s32: -2000000000\n" "s16: -32000\n" "s8: -127\n" "f: 137.125\n" "d: -2.8625\n" "h8: 0xFF\n" "h16: 0x8765\n" "h32: 0xFEDCBA98\n" "h64: 0xFEDCBA9876543210\n" "...\n"); yin >> map; EXPECT_FALSE(yin.error()); EXPECT_TRUE(map.str.equals("hello there")); EXPECT_TRUE(map.stdstr == "hello where?"); EXPECT_EQ(map.u64, 5000000000ULL); EXPECT_EQ(map.u32, 4000000000U); EXPECT_EQ(map.u16, 65000); EXPECT_EQ(map.u8, 255); EXPECT_EQ(map.b, false); EXPECT_EQ(map.s64, -5000000000LL); EXPECT_EQ(map.s32, -2000000000L); EXPECT_EQ(map.s16, -32000); EXPECT_EQ(map.s8, -127); EXPECT_EQ(map.f, 137.125); EXPECT_EQ(map.d, -2.8625); EXPECT_EQ(map.h8, Hex8(255)); EXPECT_EQ(map.h16, Hex16(0x8765)); EXPECT_EQ(map.h32, Hex32(0xFEDCBA98)); EXPECT_EQ(map.h64, Hex64(0xFEDCBA9876543210LL)); } // // Test writing then reading back all built-in scalar types // TEST(YAMLIO, TestReadWriteBuiltInTypes) { std::string intermediate; { BuiltInTypes map; map.str = "one two"; map.stdstr = "three four"; map.u64 = 6000000000ULL; map.u32 = 3000000000U; map.u16 = 50000; map.u8 = 254; map.b = true; map.s64 = -6000000000LL; map.s32 = -2000000000; map.s16 = -32000; map.s8 = -128; map.f = 3.25; map.d = -2.8625; map.h8 = 254; map.h16 = 50000; map.h32 = 3000000000U; map.h64 = 6000000000LL; llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << map; } { Input yin(intermediate); BuiltInTypes map; yin >> map; EXPECT_FALSE(yin.error()); EXPECT_TRUE(map.str.equals("one two")); EXPECT_TRUE(map.stdstr == "three four"); EXPECT_EQ(map.u64, 6000000000ULL); EXPECT_EQ(map.u32, 3000000000U); EXPECT_EQ(map.u16, 50000); EXPECT_EQ(map.u8, 254); EXPECT_EQ(map.b, true); EXPECT_EQ(map.s64, -6000000000LL); EXPECT_EQ(map.s32, -2000000000L); EXPECT_EQ(map.s16, -32000); EXPECT_EQ(map.s8, -128); EXPECT_EQ(map.f, 3.25); EXPECT_EQ(map.d, -2.8625); EXPECT_EQ(map.h8, Hex8(254)); EXPECT_EQ(map.h16, Hex16(50000)); EXPECT_EQ(map.h32, Hex32(3000000000U)); EXPECT_EQ(map.h64, Hex64(6000000000LL)); } } //===----------------------------------------------------------------------===// // Test endian-aware types //===----------------------------------------------------------------------===// struct EndianTypes { typedef llvm::support::detail::packed_endian_specific_integral< float, llvm::support::little, llvm::support::unaligned> ulittle_float; typedef llvm::support::detail::packed_endian_specific_integral< double, llvm::support::little, llvm::support::unaligned> ulittle_double; llvm::support::ulittle64_t u64; llvm::support::ulittle32_t u32; llvm::support::ulittle16_t u16; llvm::support::little64_t s64; llvm::support::little32_t s32; llvm::support::little16_t s16; ulittle_float f; ulittle_double d; }; namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, EndianTypes &et) { io.mapRequired("u64", et.u64); io.mapRequired("u32", et.u32); io.mapRequired("u16", et.u16); io.mapRequired("s64", et.s64); io.mapRequired("s32", et.s32); io.mapRequired("s16", et.s16); io.mapRequired("f", et.f); io.mapRequired("d", et.d); } }; } } // // Test the reading of all endian scalar conversions // TEST(YAMLIO, TestReadEndianTypes) { EndianTypes map; Input yin("---\n" "u64: 5000000000\n" "u32: 4000000000\n" "u16: 65000\n" "s64: -5000000000\n" "s32: -2000000000\n" "s16: -32000\n" "f: 3.25\n" "d: -2.8625\n" "...\n"); yin >> map; EXPECT_FALSE(yin.error()); EXPECT_EQ(map.u64, 5000000000ULL); EXPECT_EQ(map.u32, 4000000000U); EXPECT_EQ(map.u16, 65000); EXPECT_EQ(map.s64, -5000000000LL); EXPECT_EQ(map.s32, -2000000000L); EXPECT_EQ(map.s16, -32000); EXPECT_EQ(map.f, 3.25f); EXPECT_EQ(map.d, -2.8625); } // // Test writing then reading back all endian-aware scalar types // TEST(YAMLIO, TestReadWriteEndianTypes) { std::string intermediate; { EndianTypes map; map.u64 = 6000000000ULL; map.u32 = 3000000000U; map.u16 = 50000; map.s64 = -6000000000LL; map.s32 = -2000000000; map.s16 = -32000; map.f = 3.25f; map.d = -2.8625; llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << map; } { Input yin(intermediate); EndianTypes map; yin >> map; EXPECT_FALSE(yin.error()); EXPECT_EQ(map.u64, 6000000000ULL); EXPECT_EQ(map.u32, 3000000000U); EXPECT_EQ(map.u16, 50000); EXPECT_EQ(map.s64, -6000000000LL); EXPECT_EQ(map.s32, -2000000000L); EXPECT_EQ(map.s16, -32000); EXPECT_EQ(map.f, 3.25f); EXPECT_EQ(map.d, -2.8625); } } struct StringTypes { llvm::StringRef str1; llvm::StringRef str2; llvm::StringRef str3; llvm::StringRef str4; llvm::StringRef str5; llvm::StringRef str6; llvm::StringRef str7; llvm::StringRef str8; llvm::StringRef str9; llvm::StringRef str10; llvm::StringRef str11; std::string stdstr1; std::string stdstr2; std::string stdstr3; std::string stdstr4; std::string stdstr5; std::string stdstr6; std::string stdstr7; std::string stdstr8; std::string stdstr9; std::string stdstr10; std::string stdstr11; std::string stdstr12; }; namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, StringTypes& st) { io.mapRequired("str1", st.str1); io.mapRequired("str2", st.str2); io.mapRequired("str3", st.str3); io.mapRequired("str4", st.str4); io.mapRequired("str5", st.str5); io.mapRequired("str6", st.str6); io.mapRequired("str7", st.str7); io.mapRequired("str8", st.str8); io.mapRequired("str9", st.str9); io.mapRequired("str10", st.str10); io.mapRequired("str11", st.str11); io.mapRequired("stdstr1", st.stdstr1); io.mapRequired("stdstr2", st.stdstr2); io.mapRequired("stdstr3", st.stdstr3); io.mapRequired("stdstr4", st.stdstr4); io.mapRequired("stdstr5", st.stdstr5); io.mapRequired("stdstr6", st.stdstr6); io.mapRequired("stdstr7", st.stdstr7); io.mapRequired("stdstr8", st.stdstr8); io.mapRequired("stdstr9", st.stdstr9); io.mapRequired("stdstr10", st.stdstr10); io.mapRequired("stdstr11", st.stdstr11); io.mapRequired("stdstr12", st.stdstr12); } }; } } TEST(YAMLIO, TestReadWriteStringTypes) { std::string intermediate; { StringTypes map; map.str1 = "'aaa"; map.str2 = "\"bbb"; map.str3 = "`ccc"; map.str4 = "@ddd"; map.str5 = ""; map.str6 = "0000000004000000"; map.str7 = "true"; map.str8 = "FALSE"; map.str9 = "~"; map.str10 = "0.2e20"; map.str11 = "0x30"; map.stdstr1 = "'eee"; map.stdstr2 = "\"fff"; map.stdstr3 = "`ggg"; map.stdstr4 = "@hhh"; map.stdstr5 = ""; map.stdstr6 = "0000000004000000"; map.stdstr7 = "true"; map.stdstr8 = "FALSE"; map.stdstr9 = "~"; map.stdstr10 = "0.2e20"; map.stdstr11 = "0x30"; map.stdstr12 = "- match"; llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << map; } llvm::StringRef flowOut(intermediate); EXPECT_NE(llvm::StringRef::npos, flowOut.find("'''aaa")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("'\"bbb'")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("'`ccc'")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("'@ddd'")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("''\n")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("'0000000004000000'\n")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("'true'\n")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("'FALSE'\n")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("'~'\n")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("'0.2e20'\n")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("'0x30'\n")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("'- match'\n")); EXPECT_NE(std::string::npos, flowOut.find("'''eee")); EXPECT_NE(std::string::npos, flowOut.find("'\"fff'")); EXPECT_NE(std::string::npos, flowOut.find("'`ggg'")); EXPECT_NE(std::string::npos, flowOut.find("'@hhh'")); EXPECT_NE(std::string::npos, flowOut.find("''\n")); EXPECT_NE(std::string::npos, flowOut.find("'0000000004000000'\n")); { Input yin(intermediate); StringTypes map; yin >> map; EXPECT_FALSE(yin.error()); EXPECT_TRUE(map.str1.equals("'aaa")); EXPECT_TRUE(map.str2.equals("\"bbb")); EXPECT_TRUE(map.str3.equals("`ccc")); EXPECT_TRUE(map.str4.equals("@ddd")); EXPECT_TRUE(map.str5.equals("")); EXPECT_TRUE(map.str6.equals("0000000004000000")); EXPECT_TRUE(map.stdstr1 == "'eee"); EXPECT_TRUE(map.stdstr2 == "\"fff"); EXPECT_TRUE(map.stdstr3 == "`ggg"); EXPECT_TRUE(map.stdstr4 == "@hhh"); EXPECT_TRUE(map.stdstr5 == ""); EXPECT_TRUE(map.stdstr6 == "0000000004000000"); } } //===----------------------------------------------------------------------===// // Test ScalarEnumerationTraits //===----------------------------------------------------------------------===// enum Colors { cRed, cBlue, cGreen, cYellow }; struct ColorMap { Colors c1; Colors c2; Colors c3; Colors c4; Colors c5; Colors c6; }; namespace llvm { namespace yaml { template <> struct ScalarEnumerationTraits { static void enumeration(IO &io, Colors &value) { io.enumCase(value, "red", cRed); io.enumCase(value, "blue", cBlue); io.enumCase(value, "green", cGreen); io.enumCase(value, "yellow",cYellow); } }; template <> struct MappingTraits { static void mapping(IO &io, ColorMap& c) { io.mapRequired("c1", c.c1); io.mapRequired("c2", c.c2); io.mapRequired("c3", c.c3); io.mapOptional("c4", c.c4, cBlue); // supplies default io.mapOptional("c5", c.c5, cYellow); // supplies default io.mapOptional("c6", c.c6, cRed); // supplies default } }; } } // // Test reading enumerated scalars // TEST(YAMLIO, TestEnumRead) { ColorMap map; Input yin("---\n" "c1: blue\n" "c2: red\n" "c3: green\n" "c5: yellow\n" "...\n"); yin >> map; EXPECT_FALSE(yin.error()); EXPECT_EQ(cBlue, map.c1); EXPECT_EQ(cRed, map.c2); EXPECT_EQ(cGreen, map.c3); EXPECT_EQ(cBlue, map.c4); // tests default EXPECT_EQ(cYellow,map.c5); // tests overridden EXPECT_EQ(cRed, map.c6); // tests default } //===----------------------------------------------------------------------===// // Test ScalarBitSetTraits //===----------------------------------------------------------------------===// enum MyFlags { flagNone = 0, flagBig = 1 << 0, flagFlat = 1 << 1, flagRound = 1 << 2, flagPointy = 1 << 3 }; inline MyFlags operator|(MyFlags a, MyFlags b) { return static_cast( static_cast(a) | static_cast(b)); } struct FlagsMap { MyFlags f1; MyFlags f2; MyFlags f3; MyFlags f4; }; namespace llvm { namespace yaml { template <> struct ScalarBitSetTraits { static void bitset(IO &io, MyFlags &value) { io.bitSetCase(value, "big", flagBig); io.bitSetCase(value, "flat", flagFlat); io.bitSetCase(value, "round", flagRound); io.bitSetCase(value, "pointy",flagPointy); } }; template <> struct MappingTraits { static void mapping(IO &io, FlagsMap& c) { io.mapRequired("f1", c.f1); io.mapRequired("f2", c.f2); io.mapRequired("f3", c.f3); io.mapOptional("f4", c.f4, MyFlags(flagRound)); } }; } } // // Test reading flow sequence representing bit-mask values // TEST(YAMLIO, TestFlagsRead) { FlagsMap map; Input yin("---\n" "f1: [ big ]\n" "f2: [ round, flat ]\n" "f3: []\n" "...\n"); yin >> map; EXPECT_FALSE(yin.error()); EXPECT_EQ(flagBig, map.f1); EXPECT_EQ(flagRound|flagFlat, map.f2); EXPECT_EQ(flagNone, map.f3); // check empty set EXPECT_EQ(flagRound, map.f4); // check optional key } // // Test writing then reading back bit-mask values // TEST(YAMLIO, TestReadWriteFlags) { std::string intermediate; { FlagsMap map; map.f1 = flagBig; map.f2 = flagRound | flagFlat; map.f3 = flagNone; map.f4 = flagNone; llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << map; } { Input yin(intermediate); FlagsMap map2; yin >> map2; EXPECT_FALSE(yin.error()); EXPECT_EQ(flagBig, map2.f1); EXPECT_EQ(flagRound|flagFlat, map2.f2); EXPECT_EQ(flagNone, map2.f3); //EXPECT_EQ(flagRound, map2.f4); // check optional key } } //===----------------------------------------------------------------------===// // Test ScalarTraits //===----------------------------------------------------------------------===// struct MyCustomType { int length; int width; }; struct MyCustomTypeMap { MyCustomType f1; MyCustomType f2; int f3; }; namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, MyCustomTypeMap& s) { io.mapRequired("f1", s.f1); io.mapRequired("f2", s.f2); io.mapRequired("f3", s.f3); } }; // MyCustomType is formatted as a yaml scalar. A value of // {length=3, width=4} would be represented in yaml as "3 by 4". template<> struct ScalarTraits { static void output(const MyCustomType &value, void* ctxt, llvm::raw_ostream &out) { out << llvm::format("%d by %d", value.length, value.width); } static StringRef input(StringRef scalar, void* ctxt, MyCustomType &value) { size_t byStart = scalar.find("by"); if ( byStart != StringRef::npos ) { StringRef lenStr = scalar.slice(0, byStart); lenStr = lenStr.rtrim(); if ( lenStr.getAsInteger(0, value.length) ) { return "malformed length"; } StringRef widthStr = scalar.drop_front(byStart+2); widthStr = widthStr.ltrim(); if ( widthStr.getAsInteger(0, value.width) ) { return "malformed width"; } return StringRef(); } else { return "malformed by"; } } static QuotingType mustQuote(StringRef) { return QuotingType::Single; } }; } } // // Test writing then reading back custom values // TEST(YAMLIO, TestReadWriteMyCustomType) { std::string intermediate; { MyCustomTypeMap map; map.f1.length = 1; map.f1.width = 4; map.f2.length = 100; map.f2.width = 400; map.f3 = 10; llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << map; } { Input yin(intermediate); MyCustomTypeMap map2; yin >> map2; EXPECT_FALSE(yin.error()); EXPECT_EQ(1, map2.f1.length); EXPECT_EQ(4, map2.f1.width); EXPECT_EQ(100, map2.f2.length); EXPECT_EQ(400, map2.f2.width); EXPECT_EQ(10, map2.f3); } } //===----------------------------------------------------------------------===// // Test BlockScalarTraits //===----------------------------------------------------------------------===// struct MultilineStringType { std::string str; }; struct MultilineStringTypeMap { MultilineStringType name; MultilineStringType description; MultilineStringType ingredients; MultilineStringType recipes; MultilineStringType warningLabels; MultilineStringType documentation; int price; }; namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, MultilineStringTypeMap& s) { io.mapRequired("name", s.name); io.mapRequired("description", s.description); io.mapRequired("ingredients", s.ingredients); io.mapRequired("recipes", s.recipes); io.mapRequired("warningLabels", s.warningLabels); io.mapRequired("documentation", s.documentation); io.mapRequired("price", s.price); } }; // MultilineStringType is formatted as a yaml block literal scalar. A value of // "Hello\nWorld" would be represented in yaml as // | // Hello // World template <> struct BlockScalarTraits { static void output(const MultilineStringType &value, void *ctxt, llvm::raw_ostream &out) { out << value.str; } static StringRef input(StringRef scalar, void *ctxt, MultilineStringType &value) { value.str = scalar.str(); return StringRef(); } }; } } LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(MultilineStringType) // // Test writing then reading back custom values // TEST(YAMLIO, TestReadWriteMultilineStringType) { std::string intermediate; { MultilineStringTypeMap map; map.name.str = "An Item"; map.description.str = "Hello\nWorld"; map.ingredients.str = "SubItem 1\nSub Item 2\n\nSub Item 3\n"; map.recipes.str = "\n\nTest 1\n\n\n"; map.warningLabels.str = ""; map.documentation.str = "\n\n"; map.price = 350; llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << map; } { Input yin(intermediate); MultilineStringTypeMap map2; yin >> map2; EXPECT_FALSE(yin.error()); EXPECT_EQ(map2.name.str, "An Item\n"); EXPECT_EQ(map2.description.str, "Hello\nWorld\n"); EXPECT_EQ(map2.ingredients.str, "SubItem 1\nSub Item 2\n\nSub Item 3\n"); EXPECT_EQ(map2.recipes.str, "\n\nTest 1\n"); EXPECT_TRUE(map2.warningLabels.str.empty()); EXPECT_TRUE(map2.documentation.str.empty()); EXPECT_EQ(map2.price, 350); } } // // Test writing then reading back custom values // TEST(YAMLIO, TestReadWriteBlockScalarDocuments) { std::string intermediate; { std::vector documents; MultilineStringType doc; doc.str = "Hello\nWorld"; documents.push_back(doc); llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << documents; // Verify that the block scalar header was written out on the same line // as the document marker. EXPECT_NE(llvm::StringRef::npos, llvm::StringRef(ostr.str()).find("--- |")); } { Input yin(intermediate); std::vector documents2; yin >> documents2; EXPECT_FALSE(yin.error()); EXPECT_EQ(documents2.size(), size_t(1)); EXPECT_EQ(documents2[0].str, "Hello\nWorld\n"); } } TEST(YAMLIO, TestReadWriteBlockScalarValue) { std::string intermediate; { MultilineStringType doc; doc.str = "Just a block\nscalar doc"; llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << doc; } { Input yin(intermediate); MultilineStringType doc; yin >> doc; EXPECT_FALSE(yin.error()); EXPECT_EQ(doc.str, "Just a block\nscalar doc\n"); } } //===----------------------------------------------------------------------===// // Test flow sequences //===----------------------------------------------------------------------===// LLVM_YAML_STRONG_TYPEDEF(int, MyNumber) LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(MyNumber) LLVM_YAML_STRONG_TYPEDEF(llvm::StringRef, MyString) LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(MyString) namespace llvm { namespace yaml { template<> struct ScalarTraits { static void output(const MyNumber &value, void *, llvm::raw_ostream &out) { out << value; } static StringRef input(StringRef scalar, void *, MyNumber &value) { long long n; if ( getAsSignedInteger(scalar, 0, n) ) return "invalid number"; value = n; return StringRef(); } static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template <> struct ScalarTraits { using Impl = ScalarTraits; static void output(const MyString &V, void *Ctx, raw_ostream &OS) { Impl::output(V, Ctx, OS); } static StringRef input(StringRef S, void *Ctx, MyString &V) { return Impl::input(S, Ctx, V.value); } static QuotingType mustQuote(StringRef S) { return Impl::mustQuote(S); } }; } } struct NameAndNumbers { llvm::StringRef name; std::vector strings; std::vector single; std::vector numbers; }; namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, NameAndNumbers& nn) { io.mapRequired("name", nn.name); io.mapRequired("strings", nn.strings); io.mapRequired("single", nn.single); io.mapRequired("numbers", nn.numbers); } }; } } typedef std::vector MyNumberFlowSequence; LLVM_YAML_IS_SEQUENCE_VECTOR(MyNumberFlowSequence) struct NameAndNumbersFlow { llvm::StringRef name; std::vector sequenceOfNumbers; }; namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, NameAndNumbersFlow& nn) { io.mapRequired("name", nn.name); io.mapRequired("sequenceOfNumbers", nn.sequenceOfNumbers); } }; } } // // Test writing then reading back custom values // TEST(YAMLIO, TestReadWriteMyFlowSequence) { std::string intermediate; { NameAndNumbers map; map.name = "hello"; map.strings.push_back(llvm::StringRef("one")); map.strings.push_back(llvm::StringRef("two")); map.single.push_back(1); map.numbers.push_back(10); map.numbers.push_back(-30); map.numbers.push_back(1024); llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << map; // Verify sequences were written in flow style ostr.flush(); llvm::StringRef flowOut(intermediate); EXPECT_NE(llvm::StringRef::npos, flowOut.find("one, two")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("10, -30, 1024")); } { Input yin(intermediate); NameAndNumbers map2; yin >> map2; EXPECT_FALSE(yin.error()); EXPECT_TRUE(map2.name.equals("hello")); EXPECT_EQ(map2.strings.size(), 2UL); EXPECT_TRUE(map2.strings[0].value.equals("one")); EXPECT_TRUE(map2.strings[1].value.equals("two")); EXPECT_EQ(map2.single.size(), 1UL); EXPECT_EQ(1, map2.single[0]); EXPECT_EQ(map2.numbers.size(), 3UL); EXPECT_EQ(10, map2.numbers[0]); EXPECT_EQ(-30, map2.numbers[1]); EXPECT_EQ(1024, map2.numbers[2]); } } // // Test writing then reading back a sequence of flow sequences. // TEST(YAMLIO, TestReadWriteSequenceOfMyFlowSequence) { std::string intermediate; { NameAndNumbersFlow map; map.name = "hello"; MyNumberFlowSequence single = { 0 }; MyNumberFlowSequence numbers = { 12, 1, -512 }; map.sequenceOfNumbers.push_back(single); map.sequenceOfNumbers.push_back(numbers); map.sequenceOfNumbers.push_back(MyNumberFlowSequence()); llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << map; // Verify sequences were written in flow style // and that the parent sequence used '-'. ostr.flush(); llvm::StringRef flowOut(intermediate); EXPECT_NE(llvm::StringRef::npos, flowOut.find("- [ 0 ]")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("- [ 12, 1, -512 ]")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("- [ ]")); } { Input yin(intermediate); NameAndNumbersFlow map2; yin >> map2; EXPECT_FALSE(yin.error()); EXPECT_TRUE(map2.name.equals("hello")); EXPECT_EQ(map2.sequenceOfNumbers.size(), 3UL); EXPECT_EQ(map2.sequenceOfNumbers[0].size(), 1UL); EXPECT_EQ(0, map2.sequenceOfNumbers[0][0]); EXPECT_EQ(map2.sequenceOfNumbers[1].size(), 3UL); EXPECT_EQ(12, map2.sequenceOfNumbers[1][0]); EXPECT_EQ(1, map2.sequenceOfNumbers[1][1]); EXPECT_EQ(-512, map2.sequenceOfNumbers[1][2]); EXPECT_TRUE(map2.sequenceOfNumbers[2].empty()); } } //===----------------------------------------------------------------------===// // Test normalizing/denormalizing //===----------------------------------------------------------------------===// LLVM_YAML_STRONG_TYPEDEF(uint32_t, TotalSeconds) typedef std::vector SecondsSequence; LLVM_YAML_IS_SEQUENCE_VECTOR(TotalSeconds) namespace llvm { namespace yaml { template <> struct MappingTraits { class NormalizedSeconds { public: NormalizedSeconds(IO &io) : hours(0), minutes(0), seconds(0) { } NormalizedSeconds(IO &, TotalSeconds &secs) : hours(secs/3600), minutes((secs - (hours*3600))/60), seconds(secs % 60) { } TotalSeconds denormalize(IO &) { return TotalSeconds(hours*3600 + minutes*60 + seconds); } uint32_t hours; uint8_t minutes; uint8_t seconds; }; static void mapping(IO &io, TotalSeconds &secs) { MappingNormalization keys(io, secs); io.mapOptional("hours", keys->hours, (uint32_t)0); io.mapOptional("minutes", keys->minutes, (uint8_t)0); io.mapRequired("seconds", keys->seconds); } }; } } // // Test the reading of a yaml sequence of mappings // TEST(YAMLIO, TestReadMySecondsSequence) { SecondsSequence seq; Input yin("---\n - hours: 1\n seconds: 5\n - seconds: 59\n...\n"); yin >> seq; EXPECT_FALSE(yin.error()); EXPECT_EQ(seq.size(), 2UL); EXPECT_EQ(seq[0], 3605U); EXPECT_EQ(seq[1], 59U); } // // Test writing then reading back custom values // TEST(YAMLIO, TestReadWriteMySecondsSequence) { std::string intermediate; { SecondsSequence seq; seq.push_back(4000); seq.push_back(500); seq.push_back(59); llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << seq; } { Input yin(intermediate); SecondsSequence seq2; yin >> seq2; EXPECT_FALSE(yin.error()); EXPECT_EQ(seq2.size(), 3UL); EXPECT_EQ(seq2[0], 4000U); EXPECT_EQ(seq2[1], 500U); EXPECT_EQ(seq2[2], 59U); } } //===----------------------------------------------------------------------===// // Test dynamic typing //===----------------------------------------------------------------------===// enum AFlags { a1, a2, a3 }; enum BFlags { b1, b2, b3 }; enum Kind { kindA, kindB }; struct KindAndFlags { KindAndFlags() : kind(kindA), flags(0) { } KindAndFlags(Kind k, uint32_t f) : kind(k), flags(f) { } Kind kind; uint32_t flags; }; typedef std::vector KindAndFlagsSequence; LLVM_YAML_IS_SEQUENCE_VECTOR(KindAndFlags) namespace llvm { namespace yaml { template <> struct ScalarEnumerationTraits { static void enumeration(IO &io, AFlags &value) { io.enumCase(value, "a1", a1); io.enumCase(value, "a2", a2); io.enumCase(value, "a3", a3); } }; template <> struct ScalarEnumerationTraits { static void enumeration(IO &io, BFlags &value) { io.enumCase(value, "b1", b1); io.enumCase(value, "b2", b2); io.enumCase(value, "b3", b3); } }; template <> struct ScalarEnumerationTraits { static void enumeration(IO &io, Kind &value) { io.enumCase(value, "A", kindA); io.enumCase(value, "B", kindB); } }; template <> struct MappingTraits { static void mapping(IO &io, KindAndFlags& kf) { io.mapRequired("kind", kf.kind); // Type of "flags" field varies depending on "kind" field. // Use memcpy here to avoid breaking strict aliasing rules. if (kf.kind == kindA) { AFlags aflags = static_cast(kf.flags); io.mapRequired("flags", aflags); kf.flags = aflags; } else { BFlags bflags = static_cast(kf.flags); io.mapRequired("flags", bflags); kf.flags = bflags; } } }; } } // // Test the reading of a yaml sequence dynamic types // TEST(YAMLIO, TestReadKindAndFlagsSequence) { KindAndFlagsSequence seq; Input yin("---\n - kind: A\n flags: a2\n - kind: B\n flags: b1\n...\n"); yin >> seq; EXPECT_FALSE(yin.error()); EXPECT_EQ(seq.size(), 2UL); EXPECT_EQ(seq[0].kind, kindA); EXPECT_EQ(seq[0].flags, (uint32_t)a2); EXPECT_EQ(seq[1].kind, kindB); EXPECT_EQ(seq[1].flags, (uint32_t)b1); } // // Test writing then reading back dynamic types // TEST(YAMLIO, TestReadWriteKindAndFlagsSequence) { std::string intermediate; { KindAndFlagsSequence seq; seq.push_back(KindAndFlags(kindA,a1)); seq.push_back(KindAndFlags(kindB,b1)); seq.push_back(KindAndFlags(kindA,a2)); seq.push_back(KindAndFlags(kindB,b2)); seq.push_back(KindAndFlags(kindA,a3)); llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << seq; } { Input yin(intermediate); KindAndFlagsSequence seq2; yin >> seq2; EXPECT_FALSE(yin.error()); EXPECT_EQ(seq2.size(), 5UL); EXPECT_EQ(seq2[0].kind, kindA); EXPECT_EQ(seq2[0].flags, (uint32_t)a1); EXPECT_EQ(seq2[1].kind, kindB); EXPECT_EQ(seq2[1].flags, (uint32_t)b1); EXPECT_EQ(seq2[2].kind, kindA); EXPECT_EQ(seq2[2].flags, (uint32_t)a2); EXPECT_EQ(seq2[3].kind, kindB); EXPECT_EQ(seq2[3].flags, (uint32_t)b2); EXPECT_EQ(seq2[4].kind, kindA); EXPECT_EQ(seq2[4].flags, (uint32_t)a3); } } //===----------------------------------------------------------------------===// // Test document list //===----------------------------------------------------------------------===// struct FooBarMap { int foo; int bar; }; typedef std::vector FooBarMapDocumentList; LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(FooBarMap) namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, FooBarMap& fb) { io.mapRequired("foo", fb.foo); io.mapRequired("bar", fb.bar); } }; } } // // Test the reading of a yaml mapping // TEST(YAMLIO, TestDocRead) { FooBarMap doc; Input yin("---\nfoo: 3\nbar: 5\n...\n"); yin >> doc; EXPECT_FALSE(yin.error()); EXPECT_EQ(doc.foo, 3); EXPECT_EQ(doc.bar,5); } // // Test writing then reading back a sequence of mappings // TEST(YAMLIO, TestSequenceDocListWriteAndRead) { std::string intermediate; { FooBarMap doc1; doc1.foo = 10; doc1.bar = -3; FooBarMap doc2; doc2.foo = 257; doc2.bar = 0; std::vector docList; docList.push_back(doc1); docList.push_back(doc2); llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << docList; } { Input yin(intermediate); std::vector docList2; yin >> docList2; EXPECT_FALSE(yin.error()); EXPECT_EQ(docList2.size(), 2UL); FooBarMap& map1 = docList2[0]; FooBarMap& map2 = docList2[1]; EXPECT_EQ(map1.foo, 10); EXPECT_EQ(map1.bar, -3); EXPECT_EQ(map2.foo, 257); EXPECT_EQ(map2.bar, 0); } } //===----------------------------------------------------------------------===// // Test document tags //===----------------------------------------------------------------------===// struct MyDouble { MyDouble() : value(0.0) { } MyDouble(double x) : value(x) { } double value; }; LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(MyDouble) namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, MyDouble &d) { if (io.mapTag("!decimal", true)) { mappingDecimal(io, d); } else if (io.mapTag("!fraction")) { mappingFraction(io, d); } } static void mappingDecimal(IO &io, MyDouble &d) { io.mapRequired("value", d.value); } static void mappingFraction(IO &io, MyDouble &d) { double num, denom; io.mapRequired("numerator", num); io.mapRequired("denominator", denom); // convert fraction to double d.value = num/denom; } }; } } // // Test the reading of two different tagged yaml documents. // TEST(YAMLIO, TestTaggedDocuments) { std::vector docList; Input yin("--- !decimal\nvalue: 3.0\n" "--- !fraction\nnumerator: 9.0\ndenominator: 2\n...\n"); yin >> docList; EXPECT_FALSE(yin.error()); EXPECT_EQ(docList.size(), 2UL); EXPECT_EQ(docList[0].value, 3.0); EXPECT_EQ(docList[1].value, 4.5); } // // Test writing then reading back tagged documents // TEST(YAMLIO, TestTaggedDocumentsWriteAndRead) { std::string intermediate; { MyDouble a(10.25); MyDouble b(-3.75); std::vector docList; docList.push_back(a); docList.push_back(b); llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << docList; } { Input yin(intermediate); std::vector docList2; yin >> docList2; EXPECT_FALSE(yin.error()); EXPECT_EQ(docList2.size(), 2UL); EXPECT_EQ(docList2[0].value, 10.25); EXPECT_EQ(docList2[1].value, -3.75); } } //===----------------------------------------------------------------------===// // Test mapping validation //===----------------------------------------------------------------------===// struct MyValidation { double value; }; LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(MyValidation) namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, MyValidation &d) { io.mapRequired("value", d.value); } static StringRef validate(IO &io, MyValidation &d) { if (d.value < 0) return "negative value"; return StringRef(); } }; } } // // Test that validate() is called and complains about the negative value. // TEST(YAMLIO, TestValidatingInput) { std::vector docList; Input yin("--- \nvalue: 3.0\n" "--- \nvalue: -1.0\n...\n", nullptr, suppressErrorMessages); yin >> docList; EXPECT_TRUE(!!yin.error()); } //===----------------------------------------------------------------------===// // Test flow mapping //===----------------------------------------------------------------------===// struct FlowFooBar { int foo; int bar; FlowFooBar() : foo(0), bar(0) {} FlowFooBar(int foo, int bar) : foo(foo), bar(bar) {} }; typedef std::vector FlowFooBarSequence; LLVM_YAML_IS_SEQUENCE_VECTOR(FlowFooBar) struct FlowFooBarDoc { FlowFooBar attribute; FlowFooBarSequence seq; }; namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, FlowFooBar &fb) { io.mapRequired("foo", fb.foo); io.mapRequired("bar", fb.bar); } static const bool flow = true; }; template <> struct MappingTraits { static void mapping(IO &io, FlowFooBarDoc &fb) { io.mapRequired("attribute", fb.attribute); io.mapRequired("seq", fb.seq); } }; } } // // Test writing then reading back custom mappings // TEST(YAMLIO, TestReadWriteMyFlowMapping) { std::string intermediate; { FlowFooBarDoc doc; doc.attribute = FlowFooBar(42, 907); doc.seq.push_back(FlowFooBar(1, 2)); doc.seq.push_back(FlowFooBar(0, 0)); doc.seq.push_back(FlowFooBar(-1, 1024)); llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << doc; // Verify that mappings were written in flow style ostr.flush(); llvm::StringRef flowOut(intermediate); EXPECT_NE(llvm::StringRef::npos, flowOut.find("{ foo: 42, bar: 907 }")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("- { foo: 1, bar: 2 }")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("- { foo: 0, bar: 0 }")); EXPECT_NE(llvm::StringRef::npos, flowOut.find("- { foo: -1, bar: 1024 }")); } { Input yin(intermediate); FlowFooBarDoc doc2; yin >> doc2; EXPECT_FALSE(yin.error()); EXPECT_EQ(doc2.attribute.foo, 42); EXPECT_EQ(doc2.attribute.bar, 907); EXPECT_EQ(doc2.seq.size(), 3UL); EXPECT_EQ(doc2.seq[0].foo, 1); EXPECT_EQ(doc2.seq[0].bar, 2); EXPECT_EQ(doc2.seq[1].foo, 0); EXPECT_EQ(doc2.seq[1].bar, 0); EXPECT_EQ(doc2.seq[2].foo, -1); EXPECT_EQ(doc2.seq[2].bar, 1024); } } //===----------------------------------------------------------------------===// // Test error handling //===----------------------------------------------------------------------===// // // Test error handling of unknown enumerated scalar // TEST(YAMLIO, TestColorsReadError) { ColorMap map; Input yin("---\n" "c1: blue\n" "c2: purple\n" "c3: green\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> map; EXPECT_TRUE(!!yin.error()); } // // Test error handling of flow sequence with unknown value // TEST(YAMLIO, TestFlagsReadError) { FlagsMap map; Input yin("---\n" "f1: [ big ]\n" "f2: [ round, hollow ]\n" "f3: []\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> map; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in uint8_t type // TEST(YAMLIO, TestReadBuiltInTypesUint8Error) { std::vector seq; Input yin("---\n" "- 255\n" "- 0\n" "- 257\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in uint16_t type // TEST(YAMLIO, TestReadBuiltInTypesUint16Error) { std::vector seq; Input yin("---\n" "- 65535\n" "- 0\n" "- 66000\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in uint32_t type // TEST(YAMLIO, TestReadBuiltInTypesUint32Error) { std::vector seq; Input yin("---\n" "- 4000000000\n" "- 0\n" "- 5000000000\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in uint64_t type // TEST(YAMLIO, TestReadBuiltInTypesUint64Error) { std::vector seq; Input yin("---\n" "- 18446744073709551615\n" "- 0\n" "- 19446744073709551615\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in int8_t type // TEST(YAMLIO, TestReadBuiltInTypesint8OverError) { std::vector seq; Input yin("---\n" "- -128\n" "- 0\n" "- 127\n" "- 128\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in int8_t type // TEST(YAMLIO, TestReadBuiltInTypesint8UnderError) { std::vector seq; Input yin("---\n" "- -128\n" "- 0\n" "- 127\n" "- -129\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in int16_t type // TEST(YAMLIO, TestReadBuiltInTypesint16UnderError) { std::vector seq; Input yin("---\n" "- 32767\n" "- 0\n" "- -32768\n" "- -32769\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in int16_t type // TEST(YAMLIO, TestReadBuiltInTypesint16OverError) { std::vector seq; Input yin("---\n" "- 32767\n" "- 0\n" "- -32768\n" "- 32768\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in int32_t type // TEST(YAMLIO, TestReadBuiltInTypesint32UnderError) { std::vector seq; Input yin("---\n" "- 2147483647\n" "- 0\n" "- -2147483648\n" "- -2147483649\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in int32_t type // TEST(YAMLIO, TestReadBuiltInTypesint32OverError) { std::vector seq; Input yin("---\n" "- 2147483647\n" "- 0\n" "- -2147483648\n" "- 2147483649\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in int64_t type // TEST(YAMLIO, TestReadBuiltInTypesint64UnderError) { std::vector seq; Input yin("---\n" "- -9223372036854775808\n" "- 0\n" "- 9223372036854775807\n" "- -9223372036854775809\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in int64_t type // TEST(YAMLIO, TestReadBuiltInTypesint64OverError) { std::vector seq; Input yin("---\n" "- -9223372036854775808\n" "- 0\n" "- 9223372036854775807\n" "- 9223372036854775809\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in float type // TEST(YAMLIO, TestReadBuiltInTypesFloatError) { std::vector seq; Input yin("---\n" "- 0.0\n" "- 1000.1\n" "- -123.456\n" "- 1.2.3\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in float type // TEST(YAMLIO, TestReadBuiltInTypesDoubleError) { std::vector seq; Input yin("---\n" "- 0.0\n" "- 1000.1\n" "- -123.456\n" "- 1.2.3\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in Hex8 type // LLVM_YAML_IS_SEQUENCE_VECTOR(Hex8) TEST(YAMLIO, TestReadBuiltInTypesHex8Error) { std::vector seq; Input yin("---\n" "- 0x12\n" "- 0xFE\n" "- 0x123\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in Hex16 type // LLVM_YAML_IS_SEQUENCE_VECTOR(Hex16) TEST(YAMLIO, TestReadBuiltInTypesHex16Error) { std::vector seq; Input yin("---\n" "- 0x0012\n" "- 0xFEFF\n" "- 0x12345\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in Hex32 type // LLVM_YAML_IS_SEQUENCE_VECTOR(Hex32) TEST(YAMLIO, TestReadBuiltInTypesHex32Error) { std::vector seq; Input yin("---\n" "- 0x0012\n" "- 0xFEFF0000\n" "- 0x1234556789\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } // // Test error handling reading built-in Hex64 type // LLVM_YAML_IS_SEQUENCE_VECTOR(Hex64) TEST(YAMLIO, TestReadBuiltInTypesHex64Error) { std::vector seq; Input yin("---\n" "- 0x0012\n" "- 0xFFEEDDCCBBAA9988\n" "- 0x12345567890ABCDEF0\n" "...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_TRUE(!!yin.error()); } TEST(YAMLIO, TestMalformedMapFailsGracefully) { FooBar doc; { // We pass the suppressErrorMessages handler to handle the error // message generated in the constructor of Input. Input yin("{foo:3, bar: 5}", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> doc; EXPECT_TRUE(!!yin.error()); } { Input yin("---\nfoo:3\nbar: 5\n...\n", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> doc; EXPECT_TRUE(!!yin.error()); } } struct OptionalTest { std::vector Numbers; }; struct OptionalTestSeq { std::vector Tests; }; LLVM_YAML_IS_SEQUENCE_VECTOR(OptionalTest) namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO& IO, OptionalTest &OT) { IO.mapOptional("Numbers", OT.Numbers); } }; template <> struct MappingTraits { static void mapping(IO &IO, OptionalTestSeq &OTS) { IO.mapOptional("Tests", OTS.Tests); } }; } } TEST(YAMLIO, SequenceElideTest) { // Test that writing out a purely optional structure with its fields set to // default followed by other data is properly read back in. OptionalTestSeq Seq; OptionalTest One, Two, Three, Four; int N[] = {1, 2, 3}; Three.Numbers.assign(N, N + 3); Seq.Tests.push_back(One); Seq.Tests.push_back(Two); Seq.Tests.push_back(Three); Seq.Tests.push_back(Four); std::string intermediate; { llvm::raw_string_ostream ostr(intermediate); Output yout(ostr); yout << Seq; } Input yin(intermediate); OptionalTestSeq Seq2; yin >> Seq2; EXPECT_FALSE(yin.error()); EXPECT_EQ(4UL, Seq2.Tests.size()); EXPECT_TRUE(Seq2.Tests[0].Numbers.empty()); EXPECT_TRUE(Seq2.Tests[1].Numbers.empty()); EXPECT_EQ(1, Seq2.Tests[2].Numbers[0]); EXPECT_EQ(2, Seq2.Tests[2].Numbers[1]); EXPECT_EQ(3, Seq2.Tests[2].Numbers[2]); EXPECT_TRUE(Seq2.Tests[3].Numbers.empty()); } TEST(YAMLIO, TestEmptyStringFailsForMapWithRequiredFields) { FooBar doc; Input yin(""); yin >> doc; EXPECT_TRUE(!!yin.error()); } TEST(YAMLIO, TestEmptyStringSucceedsForMapWithOptionalFields) { OptionalTest doc; Input yin(""); yin >> doc; EXPECT_FALSE(yin.error()); } TEST(YAMLIO, TestEmptyStringSucceedsForSequence) { std::vector seq; Input yin("", /*Ctxt=*/nullptr, suppressErrorMessages); yin >> seq; EXPECT_FALSE(yin.error()); EXPECT_TRUE(seq.empty()); } struct FlowMap { llvm::StringRef str1, str2, str3; FlowMap(llvm::StringRef str1, llvm::StringRef str2, llvm::StringRef str3) : str1(str1), str2(str2), str3(str3) {} }; struct FlowSeq { llvm::StringRef str; FlowSeq(llvm::StringRef S) : str(S) {} FlowSeq() = default; }; namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, FlowMap &fm) { io.mapRequired("str1", fm.str1); io.mapRequired("str2", fm.str2); io.mapRequired("str3", fm.str3); } static const bool flow = true; }; template <> struct ScalarTraits { static void output(const FlowSeq &value, void*, llvm::raw_ostream &out) { out << value.str; } static StringRef input(StringRef scalar, void*, FlowSeq &value) { value.str = scalar; return ""; } static QuotingType mustQuote(StringRef S) { return QuotingType::None; } }; } } LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FlowSeq) TEST(YAMLIO, TestWrapFlow) { std::string out; llvm::raw_string_ostream ostr(out); FlowMap Map("This is str1", "This is str2", "This is str3"); std::vector Seq; Seq.emplace_back("This is str1"); Seq.emplace_back("This is str2"); Seq.emplace_back("This is str3"); { // 20 is just bellow the total length of the first mapping field. // We should wreap at every element. Output yout(ostr, nullptr, 15); yout << Map; ostr.flush(); EXPECT_EQ(out, "---\n" "{ str1: This is str1, \n" " str2: This is str2, \n" " str3: This is str3 }\n" "...\n"); out.clear(); yout << Seq; ostr.flush(); EXPECT_EQ(out, "---\n" "[ This is str1, \n" " This is str2, \n" " This is str3 ]\n" "...\n"); out.clear(); } { // 25 will allow the second field to be output on the first line. Output yout(ostr, nullptr, 25); yout << Map; ostr.flush(); EXPECT_EQ(out, "---\n" "{ str1: This is str1, str2: This is str2, \n" " str3: This is str3 }\n" "...\n"); out.clear(); yout << Seq; ostr.flush(); EXPECT_EQ(out, "---\n" "[ This is str1, This is str2, \n" " This is str3 ]\n" "...\n"); out.clear(); } { // 0 means no wrapping. Output yout(ostr, nullptr, 0); yout << Map; ostr.flush(); EXPECT_EQ(out, "---\n" "{ str1: This is str1, str2: This is str2, str3: This is str3 }\n" "...\n"); out.clear(); yout << Seq; ostr.flush(); EXPECT_EQ(out, "---\n" "[ This is str1, This is str2, This is str3 ]\n" "...\n"); out.clear(); } } struct MappingContext { int A = 0; }; struct SimpleMap { int B = 0; int C = 0; }; struct NestedMap { NestedMap(MappingContext &Context) : Context(Context) {} SimpleMap Simple; MappingContext &Context; }; namespace llvm { namespace yaml { template <> struct MappingContextTraits { static void mapping(IO &io, SimpleMap &sm, MappingContext &Context) { io.mapRequired("B", sm.B); io.mapRequired("C", sm.C); ++Context.A; io.mapRequired("Context", Context.A); } }; template <> struct MappingTraits { static void mapping(IO &io, NestedMap &nm) { io.mapRequired("Simple", nm.Simple, nm.Context); } }; } } TEST(YAMLIO, TestMapWithContext) { MappingContext Context; NestedMap Nested(Context); std::string out; llvm::raw_string_ostream ostr(out); Output yout(ostr, nullptr, 15); yout << Nested; ostr.flush(); EXPECT_EQ(1, Context.A); EXPECT_EQ("---\n" "Simple: \n" " B: 0\n" " C: 0\n" " Context: 1\n" "...\n", out); out.clear(); Nested.Simple.B = 2; Nested.Simple.C = 3; yout << Nested; ostr.flush(); EXPECT_EQ(2, Context.A); EXPECT_EQ("---\n" "Simple: \n" " B: 2\n" " C: 3\n" " Context: 2\n" "...\n", out); out.clear(); } LLVM_YAML_IS_STRING_MAP(int) TEST(YAMLIO, TestCustomMapping) { std::map x; x["foo"] = 1; x["bar"] = 2; std::string out; llvm::raw_string_ostream ostr(out); Output xout(ostr, nullptr, 0); xout << x; ostr.flush(); EXPECT_EQ("---\n" "bar: 2\n" "foo: 1\n" "...\n", out); Input yin(out); std::map y; yin >> y; EXPECT_EQ(2ul, y.size()); EXPECT_EQ(1, y["foo"]); EXPECT_EQ(2, y["bar"]); } LLVM_YAML_IS_STRING_MAP(FooBar) TEST(YAMLIO, TestCustomMappingStruct) { std::map x; x["foo"].foo = 1; x["foo"].bar = 2; x["bar"].foo = 3; x["bar"].bar = 4; std::string out; llvm::raw_string_ostream ostr(out); Output xout(ostr, nullptr, 0); xout << x; ostr.flush(); EXPECT_EQ("---\n" "bar: \n" " foo: 3\n" " bar: 4\n" "foo: \n" " foo: 1\n" " bar: 2\n" "...\n", out); Input yin(out); std::map y; yin >> y; EXPECT_EQ(2ul, y.size()); EXPECT_EQ(1, y["foo"].foo); EXPECT_EQ(2, y["foo"].bar); EXPECT_EQ(3, y["bar"].foo); EXPECT_EQ(4, y["bar"].bar); } static void TestEscaped(llvm::StringRef Input, llvm::StringRef Expected) { std::string out; llvm::raw_string_ostream ostr(out); Output xout(ostr, nullptr, 0); llvm::yaml::EmptyContext Ctx; yamlize(xout, Input, true, Ctx); ostr.flush(); // Make a separate StringRef so we get nice byte-by-byte output. llvm::StringRef Got(out); EXPECT_EQ(Expected, Got); } TEST(YAMLIO, TestEscaped) { // Single quote TestEscaped("@abc@", "'@abc@'"); // No quote TestEscaped("abc/", "abc/"); // Double quote non-printable TestEscaped("\01@abc@", "\"\\x01@abc@\""); // Double quote inside single quote TestEscaped("abc\"fdf", "'abc\"fdf'"); // Double quote inside double quote TestEscaped("\01bc\"fdf", "\"\\x01bc\\\"fdf\""); // Single quote inside single quote TestEscaped("abc'fdf", "'abc''fdf'"); // UTF8 TestEscaped("/*параметр*/", "\"/*параметр*/\""); // UTF8 with single quote inside double quote TestEscaped("parameter 'параметр' is unused", "\"parameter 'параметр' is unused\""); // String with embedded non-printable multibyte UTF-8 sequence (U+200B // zero-width space). The thing to test here is that we emit a // unicode-scalar level escape like \uNNNN (at the YAML level), and don't // just pass the UTF-8 byte sequence through as with quoted printables. { const unsigned char foobar[10] = {'f', 'o', 'o', 0xE2, 0x80, 0x8B, // UTF-8 of U+200B 'b', 'a', 'r', 0x0}; TestEscaped((char const *)foobar, "\"foo\\u200Bbar\""); } } + +TEST(YAMLIO, Numeric) { + EXPECT_TRUE(isNumeric(".inf")); + EXPECT_TRUE(isNumeric(".INF")); + EXPECT_TRUE(isNumeric(".Inf")); + EXPECT_TRUE(isNumeric("-.inf")); + EXPECT_TRUE(isNumeric("+.inf")); + + EXPECT_TRUE(isNumeric(".nan")); + EXPECT_TRUE(isNumeric(".NaN")); + EXPECT_TRUE(isNumeric(".NAN")); + + EXPECT_TRUE(isNumeric("0")); + EXPECT_TRUE(isNumeric("0.")); + EXPECT_TRUE(isNumeric("0.0")); + EXPECT_TRUE(isNumeric("-0.0")); + EXPECT_TRUE(isNumeric("+0.0")); + + EXPECT_TRUE(isNumeric("12345")); + EXPECT_TRUE(isNumeric("012345")); + EXPECT_TRUE(isNumeric("+12.0")); + EXPECT_TRUE(isNumeric(".5")); + EXPECT_TRUE(isNumeric("+.5")); + EXPECT_TRUE(isNumeric("-1.0")); + + EXPECT_TRUE(isNumeric("2.3e4")); + EXPECT_TRUE(isNumeric("-2E+05")); + EXPECT_TRUE(isNumeric("+12e03")); + EXPECT_TRUE(isNumeric("6.8523015e+5")); + + EXPECT_TRUE(isNumeric("1.e+1")); + EXPECT_TRUE(isNumeric(".0e+1")); + + EXPECT_TRUE(isNumeric("0x2aF3")); + EXPECT_TRUE(isNumeric("0o01234567")); + + EXPECT_FALSE(isNumeric("not a number")); + EXPECT_FALSE(isNumeric(".")); + EXPECT_FALSE(isNumeric(".e+1")); + EXPECT_FALSE(isNumeric(".1e")); + EXPECT_FALSE(isNumeric(".1e+")); + EXPECT_FALSE(isNumeric(".1e++1")); + + EXPECT_FALSE(isNumeric("ABCD")); + EXPECT_FALSE(isNumeric("+0x2AF3")); + EXPECT_FALSE(isNumeric("-0x2AF3")); + EXPECT_FALSE(isNumeric("0x2AF3Z")); + EXPECT_FALSE(isNumeric("0o012345678")); + EXPECT_FALSE(isNumeric("0xZ")); + EXPECT_FALSE(isNumeric("-0o012345678")); + EXPECT_FALSE(isNumeric("000003A8229434B839616A25C16B0291F77A438B")); + + EXPECT_FALSE(isNumeric("")); + EXPECT_FALSE(isNumeric(".")); + EXPECT_FALSE(isNumeric(".e+1")); + EXPECT_FALSE(isNumeric(".e+")); + EXPECT_FALSE(isNumeric(".e")); + EXPECT_FALSE(isNumeric("e1")); + + // Deprecated formats: as for YAML 1.2 specification, the following are not + // valid numbers anymore: + // + // * Sexagecimal numbers + // * Decimal numbers with comma s the delimiter + // * "inf", "nan" without '.' prefix + EXPECT_FALSE(isNumeric("3:25:45")); + EXPECT_FALSE(isNumeric("+12,345")); + EXPECT_FALSE(isNumeric("-inf")); + EXPECT_FALSE(isNumeric("1,230.15")); +}