1 //===-- MsgPackDocumentYAML.cpp - MsgPack Document YAML interface -------*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 /// This file implements YAMLIO on a msgpack::Document. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/BinaryFormat/MsgPackDocument.h" 14 #include "llvm/Support/YAMLTraits.h" 15 16 using namespace llvm; 17 using namespace msgpack; 18 19 namespace { 20 21 // Struct used to represent scalar node. (MapDocNode and ArrayDocNode already 22 // exist in MsgPackDocument.h.) 23 struct ScalarDocNode : DocNode { 24 ScalarDocNode(DocNode N) : DocNode(N) {} 25 26 /// Get the YAML tag for this ScalarDocNode. This normally returns ""; it only 27 /// returns something else if the result of toString would be ambiguous, e.g. 28 /// a string that parses as a number or boolean. 29 StringRef getYAMLTag() const; 30 }; 31 32 } // namespace 33 34 /// Convert this DocNode to a string, assuming it is scalar. 35 std::string DocNode::toString() const { 36 std::string S; 37 raw_string_ostream OS(S); 38 switch (getKind()) { 39 case msgpack::Type::String: 40 OS << Raw; 41 break; 42 case msgpack::Type::Nil: 43 break; 44 case msgpack::Type::Boolean: 45 OS << (Bool ? "true" : "false"); 46 break; 47 case msgpack::Type::Int: 48 OS << Int; 49 break; 50 case msgpack::Type::UInt: 51 if (getDocument()->getHexMode()) 52 OS << format("%#llx", (unsigned long long)UInt); 53 else 54 OS << UInt; 55 break; 56 case msgpack::Type::Float: 57 OS << Float; 58 break; 59 default: 60 llvm_unreachable("not scalar"); 61 break; 62 } 63 return OS.str(); 64 } 65 66 /// Convert the StringRef and use it to set this DocNode (assuming scalar). If 67 /// it is a string, copy the string into the Document's strings list so we do 68 /// not rely on S having a lifetime beyond this call. Tag is "" or a YAML tag. 69 StringRef DocNode::fromString(StringRef S, StringRef Tag) { 70 if (Tag == "tag:yaml.org,2002:str") 71 Tag = ""; 72 if (Tag == "!int" || Tag == "") { 73 // Try unsigned int then signed int. 74 *this = getDocument()->getNode(uint64_t(0)); 75 StringRef Err = yaml::ScalarTraits<uint64_t>::input(S, nullptr, getUInt()); 76 if (Err != "") { 77 *this = getDocument()->getNode(int64_t(0)); 78 Err = yaml::ScalarTraits<int64_t>::input(S, nullptr, getInt()); 79 } 80 if (Err == "" || Tag != "") 81 return Err; 82 } 83 if (Tag == "!nil") { 84 *this = getDocument()->getNode(); 85 return ""; 86 } 87 if (Tag == "!bool" || Tag == "") { 88 *this = getDocument()->getNode(false); 89 StringRef Err = yaml::ScalarTraits<bool>::input(S, nullptr, getBool()); 90 if (Err == "" || Tag != "") 91 return Err; 92 } 93 if (Tag == "!float" || Tag == "") { 94 *this = getDocument()->getNode(0.0); 95 StringRef Err = yaml::ScalarTraits<double>::input(S, nullptr, getFloat()); 96 if (Err == "" || Tag != "") 97 return Err; 98 } 99 assert((Tag == "!str" || Tag == "") && "unsupported tag"); 100 std::string V; 101 StringRef Err = yaml::ScalarTraits<std::string>::input(S, nullptr, V); 102 if (Err == "") 103 *this = getDocument()->getNode(V, /*Copy=*/true); 104 return Err; 105 } 106 107 /// Get the YAML tag for this ScalarDocNode. This normally returns ""; it only 108 /// returns something else if the result of toString would be ambiguous, e.g. 109 /// a string that parses as a number or boolean. 110 StringRef ScalarDocNode::getYAMLTag() const { 111 if (getKind() == msgpack::Type::Nil) 112 return "!nil"; 113 // Try converting both ways and see if we get the same kind. If not, we need 114 // a tag. 115 ScalarDocNode N = getDocument()->getNode(); 116 N.fromString(toString(), ""); 117 if (N.getKind() == getKind()) 118 return ""; 119 // Tolerate signedness of int changing, as tags do not differentiate between 120 // them anyway. 121 if (N.getKind() == msgpack::Type::UInt && getKind() == msgpack::Type::Int) 122 return ""; 123 if (N.getKind() == msgpack::Type::Int && getKind() == msgpack::Type::UInt) 124 return ""; 125 // We do need a tag. 126 switch (getKind()) { 127 case msgpack::Type::String: 128 return "!str"; 129 case msgpack::Type::Int: 130 return "!int"; 131 case msgpack::Type::UInt: 132 return "!int"; 133 case msgpack::Type::Boolean: 134 return "!bool"; 135 case msgpack::Type::Float: 136 return "!float"; 137 default: 138 llvm_unreachable("unrecognized kind"); 139 } 140 } 141 142 namespace llvm { 143 namespace yaml { 144 145 /// YAMLIO for DocNode 146 template <> struct PolymorphicTraits<DocNode> { 147 148 static NodeKind getKind(const DocNode &N) { 149 switch (N.getKind()) { 150 case msgpack::Type::Map: 151 return NodeKind::Map; 152 case msgpack::Type::Array: 153 return NodeKind::Sequence; 154 default: 155 return NodeKind::Scalar; 156 } 157 } 158 159 static MapDocNode &getAsMap(DocNode &N) { return N.getMap(/*Convert=*/true); } 160 161 static ArrayDocNode &getAsSequence(DocNode &N) { 162 N.getArray(/*Convert=*/true); 163 return *static_cast<ArrayDocNode *>(&N); 164 } 165 166 static ScalarDocNode &getAsScalar(DocNode &N) { 167 return *static_cast<ScalarDocNode *>(&N); 168 } 169 }; 170 171 /// YAMLIO for ScalarDocNode 172 template <> struct TaggedScalarTraits<ScalarDocNode> { 173 174 static void output(const ScalarDocNode &S, void *Ctxt, raw_ostream &OS, 175 raw_ostream &TagOS) { 176 TagOS << S.getYAMLTag(); 177 OS << S.toString(); 178 } 179 180 static StringRef input(StringRef Str, StringRef Tag, void *Ctxt, 181 ScalarDocNode &S) { 182 return S.fromString(Str, Tag); 183 } 184 185 static QuotingType mustQuote(const ScalarDocNode &S, StringRef ScalarStr) { 186 switch (S.getKind()) { 187 case Type::Int: 188 return ScalarTraits<int64_t>::mustQuote(ScalarStr); 189 case Type::UInt: 190 return ScalarTraits<uint64_t>::mustQuote(ScalarStr); 191 case Type::Nil: 192 return ScalarTraits<StringRef>::mustQuote(ScalarStr); 193 case Type::Boolean: 194 return ScalarTraits<bool>::mustQuote(ScalarStr); 195 case Type::Float: 196 return ScalarTraits<double>::mustQuote(ScalarStr); 197 case Type::Binary: 198 case Type::String: 199 return ScalarTraits<std::string>::mustQuote(ScalarStr); 200 default: 201 llvm_unreachable("unrecognized ScalarKind"); 202 } 203 } 204 }; 205 206 /// YAMLIO for MapDocNode 207 template <> struct CustomMappingTraits<MapDocNode> { 208 209 static void inputOne(IO &IO, StringRef Key, MapDocNode &M) { 210 ScalarDocNode KeyObj = M.getDocument()->getNode(); 211 KeyObj.fromString(Key, ""); 212 IO.mapRequired(Key.str().c_str(), M.getMap()[KeyObj]); 213 } 214 215 static void output(IO &IO, MapDocNode &M) { 216 for (auto I : M.getMap()) { 217 IO.mapRequired(I.first.toString().c_str(), I.second); 218 } 219 } 220 }; 221 222 /// YAMLIO for ArrayNode 223 template <> struct SequenceTraits<ArrayDocNode> { 224 225 static size_t size(IO &IO, ArrayDocNode &A) { return A.size(); } 226 227 static DocNode &element(IO &IO, ArrayDocNode &A, size_t Index) { 228 return A[Index]; 229 } 230 }; 231 232 } // namespace yaml 233 } // namespace llvm 234 235 /// Convert MsgPack Document to YAML text. 236 void msgpack::Document::toYAML(raw_ostream &OS) { 237 yaml::Output Yout(OS); 238 Yout << getRoot(); 239 } 240 241 /// Read YAML text into the MsgPack document. Returns false on failure. 242 bool msgpack::Document::fromYAML(StringRef S) { 243 clear(); 244 yaml::Input Yin(S); 245 Yin >> getRoot(); 246 return !Yin.error(); 247 } 248 249