Index: docs/YamlIO.rst =================================================================== --- docs/YamlIO.rst +++ docs/YamlIO.rst @@ -466,7 +466,7 @@ return StringRef(); } // Determine if this scalar needs quotes. - static bool mustQuote(StringRef) { return true; } + static QuotingType mustQuote(StringRef) { return QuotingType::Simple; } }; Block Scalars Index: include/llvm/CodeGen/MIRYamlMapping.h =================================================================== --- include/llvm/CodeGen/MIRYamlMapping.h +++ include/llvm/CodeGen/MIRYamlMapping.h @@ -56,7 +56,7 @@ return ""; } - static bool mustQuote(StringRef Scalar) { return needsQuotes(Scalar); } + static QuotingType mustQuote(StringRef S) { return needsQuotes(S); } }; struct FlowStringValue : StringValue { @@ -73,7 +73,7 @@ return ScalarTraits::input(Scalar, Ctx, S); } - static bool mustQuote(StringRef Scalar) { return needsQuotes(Scalar); } + static QuotingType mustQuote(StringRef S) { return needsQuotes(S); } }; struct BlockStringValue { @@ -120,7 +120,7 @@ return ScalarTraits::input(Scalar, Ctx, Value.Value); } - static bool mustQuote(StringRef Scalar) { + static QuotingType mustQuote(StringRef Scalar) { return ScalarTraits::mustQuote(Scalar); } }; Index: include/llvm/ObjectYAML/CodeViewYAMLTypeHashing.h =================================================================== --- include/llvm/ObjectYAML/CodeViewYAMLTypeHashing.h +++ include/llvm/ObjectYAML/CodeViewYAMLTypeHashing.h @@ -56,7 +56,7 @@ } // end namespace llvm LLVM_YAML_DECLARE_MAPPING_TRAITS(CodeViewYAML::DebugHSection) -LLVM_YAML_DECLARE_SCALAR_TRAITS(CodeViewYAML::GlobalHash, false) +LLVM_YAML_DECLARE_SCALAR_TRAITS(CodeViewYAML::GlobalHash, QuotingType::None) LLVM_YAML_IS_SEQUENCE_VECTOR(CodeViewYAML::GlobalHash) #endif // LLVM_OBJECTYAML_CODEVIEWYAMLTYPES_H Index: include/llvm/ObjectYAML/CodeViewYAMLTypes.h =================================================================== --- include/llvm/ObjectYAML/CodeViewYAMLTypes.h +++ include/llvm/ObjectYAML/CodeViewYAMLTypes.h @@ -58,7 +58,7 @@ } // end namespace llvm -LLVM_YAML_DECLARE_SCALAR_TRAITS(codeview::GUID, true) +LLVM_YAML_DECLARE_SCALAR_TRAITS(codeview::GUID, QuotingType::Single) LLVM_YAML_DECLARE_MAPPING_TRAITS(CodeViewYAML::LeafRecord) LLVM_YAML_DECLARE_MAPPING_TRAITS(CodeViewYAML::MemberRecord) Index: include/llvm/ObjectYAML/MachOYAML.h =================================================================== --- include/llvm/ObjectYAML/MachOYAML.h +++ include/llvm/ObjectYAML/MachOYAML.h @@ -261,7 +261,7 @@ template <> struct ScalarTraits { static void output(const char_16 &Val, void *, raw_ostream &Out); static StringRef input(StringRef Scalar, void *, char_16 &Val); - static bool mustQuote(StringRef S); + static QuotingType mustQuote(StringRef S); }; // This trait is used for UUIDs. It reads and writes them matching otool's @@ -271,7 +271,7 @@ template <> struct ScalarTraits { static void output(const uuid_t &Val, void *, raw_ostream &Out); static StringRef input(StringRef Scalar, void *, uuid_t &Val); - static bool mustQuote(StringRef S); + static QuotingType mustQuote(StringRef S); }; // Load Command struct mapping traits Index: include/llvm/ObjectYAML/YAML.h =================================================================== --- include/llvm/ObjectYAML/YAML.h +++ include/llvm/ObjectYAML/YAML.h @@ -107,7 +107,7 @@ template <> struct ScalarTraits { static void output(const BinaryRef &, void *, raw_ostream &); static StringRef input(StringRef, void *, BinaryRef &); - static bool mustQuote(StringRef S) { return needsQuotes(S); } + static QuotingType mustQuote(StringRef S) { return needsQuotes(S); } }; } // end namespace yaml Index: include/llvm/Support/YAMLTraits.h =================================================================== --- include/llvm/Support/YAMLTraits.h +++ include/llvm/Support/YAMLTraits.h @@ -117,6 +117,11 @@ // 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: /// @@ -131,7 +136,7 @@ /// // return empty string on success, or error string /// return StringRef(); /// } -/// static bool mustQuote(StringRef) { return true; } +/// static QuotingType mustQuote(StringRef) { return QuotingType::Single; } /// }; template struct ScalarTraits { @@ -145,7 +150,7 @@ //static StringRef input(StringRef scalar, void *ctxt, T &value); // // Function to determine if the value should be quoted. - //static bool mustQuote(StringRef); + //static QuotingType mustQuote(StringRef); }; /// This class should be specialized by type that requires custom conversion @@ -270,7 +275,7 @@ { using Signature_input = StringRef (*)(StringRef, void*, T&); using Signature_output = void (*)(const T&, void*, raw_ostream&); - using Signature_mustQuote = bool (*)(StringRef); + using Signature_mustQuote = QuotingType (*)(StringRef); template static char test(SameType *, @@ -495,7 +500,7 @@ S.equals("false") || S.equals("False") || S.equals("FALSE"); } -inline bool needsQuotes(StringRef S) { +inline bool needsSingleQuotes(StringRef S) { if (S.empty()) return true; if (isspace(S.front()) || isspace(S.back())) @@ -519,6 +524,46 @@ return 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 bool needsDoubleQuotes(StringRef S) { + for (unsigned char C : S) { + switch (C) { + // TAB (0x9), LF (0xA), CR (0xD) and NEL (0x85) are allowed. + case 0x9: + case 0xA: + case 0xD: + case 0x85: + continue; + // DEL (0x7F) are excluded from the allowed character range. + case 0x7F: + return true; + default: { + // C0 control block (0x0 - 0x1F) is excluded from the allowed character + // range. + if (C <= 0x1F) + return true; + // C1 control block (0x80 - 0x9F) is excluded from the allowed character + // range. + if (C >= 0x80 && C <= 0x9F) + return true; + } + } + } + return false; +} + +inline QuotingType needsQuotes(StringRef S) { + if (needsDoubleQuotes(S)) + return QuotingType::Double; + if (needsSingleQuotes(S)) + return QuotingType::Single; + return QuotingType::None; +} + template struct missingTraits : public std::integral_constant { static void output(const bool &, void* , raw_ostream &); static StringRef input(StringRef, void *, bool &); - static bool mustQuote(StringRef) { return false; } + 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 bool mustQuote(StringRef S) { return needsQuotes(S); } + 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 bool mustQuote(StringRef S) { return needsQuotes(S); } + 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 bool mustQuote(StringRef) { return false; } + 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 bool mustQuote(StringRef) { return false; } + 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 bool mustQuote(StringRef) { return false; } + 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 bool mustQuote(StringRef) { return false; } + 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 bool mustQuote(StringRef) { return false; } + 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 bool mustQuote(StringRef) { return false; } + 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 bool mustQuote(StringRef) { return false; } + 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 bool mustQuote(StringRef) { return false; } + 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 bool mustQuote(StringRef) { return false; } + 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 bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; // For endian types, we just use the existing ScalarTraits for the underlying @@ -1019,7 +1064,7 @@ return R; } - static bool mustQuote(StringRef Str) { + static QuotingType mustQuote(StringRef Str) { return ScalarTraits::mustQuote(Str); } }; @@ -1148,7 +1193,7 @@ bool beginBitSetScalar(bool &) override; bool bitSetMatch(const char *, bool ) override; void endBitSetScalar() override; - void scalarString(StringRef &, bool) override; + void scalarString(StringRef &, QuotingType) override; void blockScalarString(StringRef &) override; void setError(const Twine &message) override; bool canElideEmptySequence() override; @@ -1293,7 +1338,7 @@ bool beginBitSetScalar(bool &) override; bool bitSetMatch(const char *, bool ) override; void endBitSetScalar() override; - void scalarString(StringRef &, bool) override; + void scalarString(StringRef &, QuotingType) override; void blockScalarString(StringRef &) override; void setError(const Twine &message) override; bool canElideEmptySequence() override; @@ -1371,28 +1416,28 @@ struct ScalarTraits { static void output(const Hex8 &, void *, raw_ostream &); static StringRef input(StringRef, void *, Hex8 &); - static bool mustQuote(StringRef) { return false; } + 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 bool mustQuote(StringRef) { return false; } + 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 bool mustQuote(StringRef) { return false; } + 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 bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; // Define non-member operator>> so that Input can stream in a document list. @@ -1681,7 +1726,7 @@ template <> struct ScalarTraits { \ static void output(const Type &Value, void *ctx, raw_ostream &Out); \ static StringRef input(StringRef Scalar, void *ctxt, Type &Value); \ - static bool mustQuote(StringRef) { return MustQuote; } \ + static QuotingType mustQuote(StringRef) { return MustQuote; } \ }; \ } \ } Index: lib/ObjectYAML/CodeViewYAMLDebugSections.cpp =================================================================== --- lib/ObjectYAML/CodeViewYAMLDebugSections.cpp +++ lib/ObjectYAML/CodeViewYAMLDebugSections.cpp @@ -66,7 +66,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(YAMLCrossModuleImport) LLVM_YAML_IS_SEQUENCE_VECTOR(YAMLFrameData) -LLVM_YAML_DECLARE_SCALAR_TRAITS(HexFormattedString, false) +LLVM_YAML_DECLARE_SCALAR_TRAITS(HexFormattedString, QuotingType::None) LLVM_YAML_DECLARE_ENUM_TRAITS(DebugSubsectionKind) LLVM_YAML_DECLARE_ENUM_TRAITS(FileChecksumKind) LLVM_YAML_DECLARE_BITSET_TRAITS(LineFlags) Index: lib/ObjectYAML/CodeViewYAMLSymbols.cpp =================================================================== --- lib/ObjectYAML/CodeViewYAMLSymbols.cpp +++ lib/ObjectYAML/CodeViewYAMLSymbols.cpp @@ -42,8 +42,8 @@ LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(TypeIndex) // We only need to declare these, the definitions are in CodeViewYAMLTypes.cpp -LLVM_YAML_DECLARE_SCALAR_TRAITS(APSInt, false) -LLVM_YAML_DECLARE_SCALAR_TRAITS(TypeIndex, false) +LLVM_YAML_DECLARE_SCALAR_TRAITS(APSInt, QuotingType::None) +LLVM_YAML_DECLARE_SCALAR_TRAITS(TypeIndex, QuotingType::None) LLVM_YAML_DECLARE_ENUM_TRAITS(SymbolKind) LLVM_YAML_DECLARE_ENUM_TRAITS(FrameCookieKind) @@ -62,7 +62,7 @@ LLVM_YAML_STRONG_TYPEDEF(StringRef, TypeName) -LLVM_YAML_DECLARE_SCALAR_TRAITS(TypeName, true) +LLVM_YAML_DECLARE_SCALAR_TRAITS(TypeName, QuotingType::Single) StringRef ScalarTraits::input(StringRef S, void *V, TypeName &T) { return ScalarTraits::input(S, V, T.value); Index: lib/ObjectYAML/CodeViewYAMLTypes.cpp =================================================================== --- lib/ObjectYAML/CodeViewYAMLTypes.cpp +++ lib/ObjectYAML/CodeViewYAMLTypes.cpp @@ -48,8 +48,8 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(VFTableSlotKind) LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(TypeIndex) -LLVM_YAML_DECLARE_SCALAR_TRAITS(TypeIndex, false) -LLVM_YAML_DECLARE_SCALAR_TRAITS(APSInt, false) +LLVM_YAML_DECLARE_SCALAR_TRAITS(TypeIndex, QuotingType::None) +LLVM_YAML_DECLARE_SCALAR_TRAITS(APSInt, QuotingType::None) LLVM_YAML_DECLARE_ENUM_TRAITS(TypeLeafKind) LLVM_YAML_DECLARE_ENUM_TRAITS(PointerToMemberRepresentation) Index: lib/ObjectYAML/MachOYAML.cpp =================================================================== --- lib/ObjectYAML/MachOYAML.cpp +++ lib/ObjectYAML/MachOYAML.cpp @@ -52,7 +52,9 @@ return StringRef(); } -bool ScalarTraits::mustQuote(StringRef S) { return needsQuotes(S); } +QuotingType ScalarTraits::mustQuote(StringRef S) { + return needsQuotes(S); +} void ScalarTraits::output(const uuid_t &Val, void *, raw_ostream &Out) { Out.write_uuid(Val); @@ -75,7 +77,9 @@ return StringRef(); } -bool ScalarTraits::mustQuote(StringRef S) { return needsQuotes(S); } +QuotingType ScalarTraits::mustQuote(StringRef S) { + return needsQuotes(S); +} void MappingTraits::mapping( IO &IO, MachOYAML::FileHeader &FileHdr) { Index: lib/Support/Statistic.cpp =================================================================== --- lib/Support/Statistic.cpp +++ lib/Support/Statistic.cpp @@ -168,9 +168,10 @@ const char *delim = ""; for (const Statistic *Stat : Stats.Stats) { OS << delim; - assert(!yaml::needsQuotes(Stat->getDebugType()) && + assert(yaml::needsQuotes(Stat->getDebugType()) == yaml::QuotingType::None && "Statistic group/type name is simple."); - assert(!yaml::needsQuotes(Stat->getName()) && "Statistic name is simple"); + assert(yaml::needsQuotes(Stat->getName()) == yaml::QuotingType::None && + "Statistic name is simple"); OS << "\t\"" << Stat->getDebugType() << '.' << Stat->getName() << "\": " << Stat->getValue(); delim = ",\n"; Index: lib/Support/Timer.cpp =================================================================== --- lib/Support/Timer.cpp +++ lib/Support/Timer.cpp @@ -362,8 +362,10 @@ void TimerGroup::printJSONValue(raw_ostream &OS, const PrintRecord &R, const char *suffix, double Value) { - assert(!yaml::needsQuotes(Name) && "TimerGroup name needs no quotes"); - assert(!yaml::needsQuotes(R.Name) && "Timer name needs no quotes"); + assert(yaml::needsQuotes(Name) == yaml::QuotingType::None && + "TimerGroup name needs no quotes"); + assert(yaml::needsQuotes(R.Name) == yaml::QuotingType::None && + "Timer name needs no quotes"); OS << "\t\"time." << Name << '.' << R.Name << suffix << "\": " << Value; } Index: lib/Support/YAMLTraits.cpp =================================================================== --- lib/Support/YAMLTraits.cpp +++ lib/Support/YAMLTraits.cpp @@ -330,7 +330,7 @@ } } -void Input::scalarString(StringRef &S, bool) { +void Input::scalarString(StringRef &S, QuotingType) { if (ScalarHNode *SN = dyn_cast(CurrentNode)) { S = SN->value(); } else { @@ -338,7 +338,7 @@ } } -void Input::blockScalarString(StringRef &S) { scalarString(S, false); } +void Input::blockScalarString(StringRef &S) { scalarString(S, QuotingType::None); } void Input::setError(HNode *hnode, const Twine &message) { assert(hnode && "HNode must not be NULL"); @@ -617,7 +617,7 @@ this->outputUpToEndOfLine(" ]"); } -void Output::scalarString(StringRef &S, bool MustQuote) { +void Output::scalarString(StringRef &S, QuotingType MustQuote) { this->newLineCheck(); if (S.empty()) { // Print '' for the empty string because leaving the field empty is not @@ -625,27 +625,31 @@ this->outputUpToEndOfLine("''"); return; } - if (!MustQuote) { + if (MustQuote == QuotingType::None) { // Only quote if we must. this->outputUpToEndOfLine(S); return; } + + const char *const Quote = MustQuote == QuotingType::Single ? "'" : "\""; + const char QuoteChar = MustQuote == QuotingType::Single ? '\'' : '"'; + unsigned i = 0; unsigned j = 0; unsigned End = S.size(); - output("'"); // Starting single quote. + output(Quote); // Starting quote. const char *Base = S.data(); while (j < End) { // Escape a single quote by doubling it. - if (S[j] == '\'') { + if (S[j] == QuoteChar) { output(StringRef(&Base[i], j - i + 1)); - output("'"); + output(Quote); i = j + 1; } ++j; } output(StringRef(&Base[i], j - i)); - this->outputUpToEndOfLine("'"); // Ending single quote. + this->outputUpToEndOfLine(Quote); // Ending quote. } void Output::blockScalarString(StringRef &S) { Index: tools/dsymutil/DebugMap.h =================================================================== --- tools/dsymutil/DebugMap.h +++ tools/dsymutil/DebugMap.h @@ -232,7 +232,7 @@ template <> struct ScalarTraits { static void output(const Triple &val, void *, raw_ostream &out); static StringRef input(StringRef scalar, void *, Triple &value); - static bool mustQuote(StringRef) { return true; } + static QuotingType mustQuote(StringRef) { return QuotingType::Single; } }; template <> Index: unittests/Support/YAMLIOTest.cpp =================================================================== --- unittests/Support/YAMLIOTest.cpp +++ unittests/Support/YAMLIOTest.cpp @@ -860,7 +860,7 @@ return "malformed by"; } } - static bool mustQuote(StringRef) { return true; } + static QuotingType mustQuote(StringRef) { return QuotingType::Single; } }; } } @@ -1064,7 +1064,7 @@ return StringRef(); } - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template <> struct ScalarTraits { @@ -1075,7 +1075,9 @@ static StringRef input(StringRef S, void *Ctx, MyString &V) { return Impl::input(S, Ctx, V.value); } - static bool mustQuote(StringRef S) { return Impl::mustQuote(S); } + static QuotingType mustQuote(StringRef S) { + return Impl::mustQuote(S); + } }; } } @@ -2232,7 +2234,7 @@ return ""; } - static bool mustQuote(StringRef S) { return false; } + static QuotingType mustQuote(StringRef S) { return QuotingType::None; } }; } } @@ -2455,3 +2457,45 @@ yin >> Data; EXPECT_TRUE((bool)yin.error()); } + +TEST(YAMLIO, TestEscapedDoubleQuote) { + std::string Id = "\01@abc@"; + + std::string out; + llvm::raw_string_ostream ostr(out); + Output xout(ostr, nullptr, 0); + + llvm::yaml::EmptyContext Ctx; + yamlize(xout, Id, true, Ctx); + + ostr.flush(); + EXPECT_EQ("\"\01@abc@\"", out); +} + +TEST(YAMLIO, TestEscapedSingleQuote) { + std::string Id = "@abc@"; + + std::string out; + llvm::raw_string_ostream ostr(out); + Output xout(ostr, nullptr, 0); + + llvm::yaml::EmptyContext Ctx; + yamlize(xout, Id, true, Ctx); + + ostr.flush(); + EXPECT_EQ("'@abc@'", out); +} + +TEST(YAMLIO, TestEscapedNoQuote) { + std::string Id = "abc/"; + + std::string out; + llvm::raw_string_ostream ostr(out); + Output xout(ostr, nullptr, 0); + + llvm::yaml::EmptyContext Ctx; + yamlize(xout, Id, true, Ctx); + + ostr.flush(); + EXPECT_EQ("abc/", out); +}