| 1 | //===-------------------------- cxa_demangle.cpp --------------------------===// | 
| 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 | // FIXME: (possibly) incomplete list of features that clang mangles that this | 
| 10 | // file does not yet support: | 
| 11 | //   - C++ modules TS | 
| 12 |  | 
| 13 | #include "demangle/ItaniumDemangle.h" | 
| 14 | #include "__cxxabi_config.h" | 
| 15 | #include <cassert> | 
| 16 | #include <cctype> | 
| 17 | #include <cstdio> | 
| 18 | #include <cstdlib> | 
| 19 | #include <cstring> | 
| 20 | #include <functional> | 
| 21 | #include <numeric> | 
| 22 | #include <utility> | 
| 23 |  | 
| 24 | using namespace itanium_demangle; | 
| 25 |  | 
| 26 | constexpr const char *itanium_demangle::FloatData<float>::spec; | 
| 27 | constexpr const char *itanium_demangle::FloatData<double>::spec; | 
| 28 | constexpr const char *itanium_demangle::FloatData<long double>::spec; | 
| 29 |  | 
| 30 | // <discriminator> := _ <non-negative number>      # when number < 10 | 
| 31 | //                 := __ <non-negative number> _   # when number >= 10 | 
| 32 | //  extension      := decimal-digit+               # at the end of string | 
| 33 | const char *itanium_demangle::parse_discriminator(const char *first, | 
| 34 |                                                   const char *last) { | 
| 35 |   // parse but ignore discriminator | 
| 36 |   if (first != last) { | 
| 37 |     if (*first == '_') { | 
| 38 |       const char *t1 = first + 1; | 
| 39 |       if (t1 != last) { | 
| 40 |         if (std::isdigit(*t1)) | 
| 41 |           first = t1 + 1; | 
| 42 |         else if (*t1 == '_') { | 
| 43 |           for (++t1; t1 != last && std::isdigit(*t1); ++t1) | 
| 44 |             ; | 
| 45 |           if (t1 != last && *t1 == '_') | 
| 46 |             first = t1 + 1; | 
| 47 |         } | 
| 48 |       } | 
| 49 |     } else if (std::isdigit(*first)) { | 
| 50 |       const char *t1 = first + 1; | 
| 51 |       for (; t1 != last && std::isdigit(*t1); ++t1) | 
| 52 |         ; | 
| 53 |       if (t1 == last) | 
| 54 |         first = last; | 
| 55 |     } | 
| 56 |   } | 
| 57 |   return first; | 
| 58 | } | 
| 59 |  | 
| 60 | #ifndef NDEBUG | 
| 61 | namespace { | 
| 62 | struct DumpVisitor { | 
| 63 |   unsigned Depth = 0; | 
| 64 |   bool PendingNewline = false; | 
| 65 |  | 
| 66 |   template<typename NodeT> static constexpr bool wantsNewline(const NodeT *) { | 
| 67 |     return true; | 
| 68 |   } | 
| 69 |   static bool wantsNewline(NodeArray A) { return !A.empty(); } | 
| 70 |   static constexpr bool wantsNewline(...) { return false; } | 
| 71 |  | 
| 72 |   template<typename ...Ts> static bool anyWantNewline(Ts ...Vs) { | 
| 73 |     for (bool B : {wantsNewline(Vs)...}) | 
| 74 |       if (B) | 
| 75 |         return true; | 
| 76 |     return false; | 
| 77 |   } | 
| 78 |  | 
| 79 |   void printStr(const char *S) { fprintf(stderr, "%s" , S); } | 
| 80 |   void print(StringView SV) { | 
| 81 |     fprintf(stderr, "\"%.*s\"" , (int)SV.size(), SV.begin()); | 
| 82 |   } | 
| 83 |   void print(const Node *N) { | 
| 84 |     if (N) | 
| 85 |       N->visit(std::ref(*this)); | 
| 86 |     else | 
| 87 |       printStr("<null>" ); | 
| 88 |   } | 
| 89 |   void print(NodeOrString NS) { | 
| 90 |     if (NS.isNode()) | 
| 91 |       print(NS.asNode()); | 
| 92 |     else if (NS.isString()) | 
| 93 |       print(NS.asString()); | 
| 94 |     else | 
| 95 |       printStr("NodeOrString()" ); | 
| 96 |   } | 
| 97 |   void print(NodeArray A) { | 
| 98 |     ++Depth; | 
| 99 |     printStr("{" ); | 
| 100 |     bool First = true; | 
| 101 |     for (const Node *N : A) { | 
| 102 |       if (First) | 
| 103 |         print(N); | 
| 104 |       else | 
| 105 |         printWithComma(N); | 
| 106 |       First = false; | 
| 107 |     } | 
| 108 |     printStr("}" ); | 
| 109 |     --Depth; | 
| 110 |   } | 
| 111 |  | 
| 112 |   // Overload used when T is exactly 'bool', not merely convertible to 'bool'. | 
| 113 |   void print(bool B) { printStr(B ? "true"  : "false" ); } | 
| 114 |  | 
| 115 |   template <class T> | 
| 116 |   typename std::enable_if<std::is_unsigned<T>::value>::type print(T N) { | 
| 117 |     fprintf(stderr, "%llu" , (unsigned long long)N); | 
| 118 |   } | 
| 119 |  | 
| 120 |   template <class T> | 
| 121 |   typename std::enable_if<std::is_signed<T>::value>::type print(T N) { | 
| 122 |     fprintf(stderr, "%lld" , (long long)N); | 
| 123 |   } | 
| 124 |  | 
| 125 |   void print(ReferenceKind RK) { | 
| 126 |     switch (RK) { | 
| 127 |     case ReferenceKind::LValue: | 
| 128 |       return printStr("ReferenceKind::LValue" ); | 
| 129 |     case ReferenceKind::RValue: | 
| 130 |       return printStr("ReferenceKind::RValue" ); | 
| 131 |     } | 
| 132 |   } | 
| 133 |   void print(FunctionRefQual RQ) { | 
| 134 |     switch (RQ) { | 
| 135 |     case FunctionRefQual::FrefQualNone: | 
| 136 |       return printStr("FunctionRefQual::FrefQualNone" ); | 
| 137 |     case FunctionRefQual::FrefQualLValue: | 
| 138 |       return printStr("FunctionRefQual::FrefQualLValue" ); | 
| 139 |     case FunctionRefQual::FrefQualRValue: | 
| 140 |       return printStr("FunctionRefQual::FrefQualRValue" ); | 
| 141 |     } | 
| 142 |   } | 
| 143 |   void print(Qualifiers Qs) { | 
| 144 |     if (!Qs) return printStr("QualNone" ); | 
| 145 |     struct QualName { Qualifiers Q; const char *Name; } Names[] = { | 
| 146 |       {QualConst, "QualConst" }, | 
| 147 |       {QualVolatile, "QualVolatile" }, | 
| 148 |       {QualRestrict, "QualRestrict" }, | 
| 149 |     }; | 
| 150 |     for (QualName Name : Names) { | 
| 151 |       if (Qs & Name.Q) { | 
| 152 |         printStr(Name.Name); | 
| 153 |         Qs = Qualifiers(Qs & ~Name.Q); | 
| 154 |         if (Qs) printStr(" | " ); | 
| 155 |       } | 
| 156 |     } | 
| 157 |   } | 
| 158 |   void print(SpecialSubKind SSK) { | 
| 159 |     switch (SSK) { | 
| 160 |     case SpecialSubKind::allocator: | 
| 161 |       return printStr("SpecialSubKind::allocator" ); | 
| 162 |     case SpecialSubKind::basic_string: | 
| 163 |       return printStr("SpecialSubKind::basic_string" ); | 
| 164 |     case SpecialSubKind::string: | 
| 165 |       return printStr("SpecialSubKind::string" ); | 
| 166 |     case SpecialSubKind::istream: | 
| 167 |       return printStr("SpecialSubKind::istream" ); | 
| 168 |     case SpecialSubKind::ostream: | 
| 169 |       return printStr("SpecialSubKind::ostream" ); | 
| 170 |     case SpecialSubKind::iostream: | 
| 171 |       return printStr("SpecialSubKind::iostream" ); | 
| 172 |     } | 
| 173 |   } | 
| 174 |  | 
| 175 |   void newLine() { | 
| 176 |     printStr("\n" ); | 
| 177 |     for (unsigned I = 0; I != Depth; ++I) | 
| 178 |       printStr(" " ); | 
| 179 |     PendingNewline = false; | 
| 180 |   } | 
| 181 |  | 
| 182 |   template<typename T> void printWithPendingNewline(T V) { | 
| 183 |     print(V); | 
| 184 |     if (wantsNewline(V)) | 
| 185 |       PendingNewline = true; | 
| 186 |   } | 
| 187 |  | 
| 188 |   template<typename T> void printWithComma(T V) { | 
| 189 |     if (PendingNewline || wantsNewline(V)) { | 
| 190 |       printStr("," ); | 
| 191 |       newLine(); | 
| 192 |     } else { | 
| 193 |       printStr(", " ); | 
| 194 |     } | 
| 195 |  | 
| 196 |     printWithPendingNewline(V); | 
| 197 |   } | 
| 198 |  | 
| 199 |   struct CtorArgPrinter { | 
| 200 |     DumpVisitor &Visitor; | 
| 201 |  | 
| 202 |     template<typename T, typename ...Rest> void operator()(T V, Rest ...Vs) { | 
| 203 |       if (Visitor.anyWantNewline(V, Vs...)) | 
| 204 |         Visitor.newLine(); | 
| 205 |       Visitor.printWithPendingNewline(V); | 
| 206 |       int PrintInOrder[] = { (Visitor.printWithComma(Vs), 0)..., 0 }; | 
| 207 |       (void)PrintInOrder; | 
| 208 |     } | 
| 209 |   }; | 
| 210 |  | 
| 211 |   template<typename NodeT> void operator()(const NodeT *Node) { | 
| 212 |     Depth += 2; | 
| 213 |     fprintf(stderr, "%s(" , itanium_demangle::NodeKind<NodeT>::name()); | 
| 214 |     Node->match(CtorArgPrinter{*this}); | 
| 215 |     fprintf(stderr, ")" ); | 
| 216 |     Depth -= 2; | 
| 217 |   } | 
| 218 |  | 
| 219 |   void operator()(const ForwardTemplateReference *Node) { | 
| 220 |     Depth += 2; | 
| 221 |     fprintf(stderr, "ForwardTemplateReference(" ); | 
| 222 |     if (Node->Ref && !Node->Printing) { | 
| 223 |       Node->Printing = true; | 
| 224 |       CtorArgPrinter{*this}(Node->Ref); | 
| 225 |       Node->Printing = false; | 
| 226 |     } else { | 
| 227 |       CtorArgPrinter{*this}(Node->Index); | 
| 228 |     } | 
| 229 |     fprintf(stderr, ")" ); | 
| 230 |     Depth -= 2; | 
| 231 |   } | 
| 232 | }; | 
| 233 | } | 
| 234 |  | 
| 235 | void itanium_demangle::Node::dump() const { | 
| 236 |   DumpVisitor V; | 
| 237 |   visit(std::ref(V)); | 
| 238 |   V.newLine(); | 
| 239 | } | 
| 240 | #endif | 
| 241 |  | 
| 242 | namespace { | 
| 243 | class BumpPointerAllocator { | 
| 244 |   struct BlockMeta { | 
| 245 |     BlockMeta* Next; | 
| 246 |     size_t Current; | 
| 247 |   }; | 
| 248 |  | 
| 249 |   static constexpr size_t AllocSize = 4096; | 
| 250 |   static constexpr size_t UsableAllocSize = AllocSize - sizeof(BlockMeta); | 
| 251 |  | 
| 252 |   alignas(long double) char InitialBuffer[AllocSize]; | 
| 253 |   BlockMeta* BlockList = nullptr; | 
| 254 |  | 
| 255 |   void grow() { | 
| 256 |     char* NewMeta = static_cast<char *>(std::malloc(AllocSize)); | 
| 257 |     if (NewMeta == nullptr) | 
| 258 |       std::terminate(); | 
| 259 |     BlockList = new (NewMeta) BlockMeta{BlockList, 0}; | 
| 260 |   } | 
| 261 |  | 
| 262 |   void* allocateMassive(size_t NBytes) { | 
| 263 |     NBytes += sizeof(BlockMeta); | 
| 264 |     BlockMeta* NewMeta = reinterpret_cast<BlockMeta*>(std::malloc(NBytes)); | 
| 265 |     if (NewMeta == nullptr) | 
| 266 |       std::terminate(); | 
| 267 |     BlockList->Next = new (NewMeta) BlockMeta{BlockList->Next, 0}; | 
| 268 |     return static_cast<void*>(NewMeta + 1); | 
| 269 |   } | 
| 270 |  | 
| 271 | public: | 
| 272 |   BumpPointerAllocator() | 
| 273 |       : BlockList(new (InitialBuffer) BlockMeta{nullptr, 0}) {} | 
| 274 |  | 
| 275 |   void* allocate(size_t N) { | 
| 276 |     N = (N + 15u) & ~15u; | 
| 277 |     if (N + BlockList->Current >= UsableAllocSize) { | 
| 278 |       if (N > UsableAllocSize) | 
| 279 |         return allocateMassive(N); | 
| 280 |       grow(); | 
| 281 |     } | 
| 282 |     BlockList->Current += N; | 
| 283 |     return static_cast<void*>(reinterpret_cast<char*>(BlockList + 1) + | 
| 284 |                               BlockList->Current - N); | 
| 285 |   } | 
| 286 |  | 
| 287 |   void reset() { | 
| 288 |     while (BlockList) { | 
| 289 |       BlockMeta* Tmp = BlockList; | 
| 290 |       BlockList = BlockList->Next; | 
| 291 |       if (reinterpret_cast<char*>(Tmp) != InitialBuffer) | 
| 292 |         std::free(Tmp); | 
| 293 |     } | 
| 294 |     BlockList = new (InitialBuffer) BlockMeta{nullptr, 0}; | 
| 295 |   } | 
| 296 |  | 
| 297 |   ~BumpPointerAllocator() { reset(); } | 
| 298 | }; | 
| 299 |  | 
| 300 | class DefaultAllocator { | 
| 301 |   BumpPointerAllocator Alloc; | 
| 302 |  | 
| 303 | public: | 
| 304 |   void reset() { Alloc.reset(); } | 
| 305 |  | 
| 306 |   template<typename T, typename ...Args> T *makeNode(Args &&...args) { | 
| 307 |     return new (Alloc.allocate(sizeof(T))) | 
| 308 |         T(std::forward<Args>(args)...); | 
| 309 |   } | 
| 310 |  | 
| 311 |   void *allocateNodeArray(size_t sz) { | 
| 312 |     return Alloc.allocate(sizeof(Node *) * sz); | 
| 313 |   } | 
| 314 | }; | 
| 315 | }  // unnamed namespace | 
| 316 |  | 
| 317 | //===----------------------------------------------------------------------===// | 
| 318 | // Code beyond this point should not be synchronized with LLVM. | 
| 319 | //===----------------------------------------------------------------------===// | 
| 320 |  | 
| 321 | using Demangler = itanium_demangle::ManglingParser<DefaultAllocator>; | 
| 322 |  | 
| 323 | namespace { | 
| 324 | enum : int { | 
| 325 |   demangle_invalid_args = -3, | 
| 326 |   demangle_invalid_mangled_name = -2, | 
| 327 |   demangle_memory_alloc_failure = -1, | 
| 328 |   demangle_success = 0, | 
| 329 | }; | 
| 330 | } | 
| 331 |  | 
| 332 | namespace __cxxabiv1 { | 
| 333 | extern "C"  _LIBCXXABI_FUNC_VIS char * | 
| 334 | __cxa_demangle(const char *MangledName, char *Buf, size_t *N, int *Status) { | 
| 335 |   if (MangledName == nullptr || (Buf != nullptr && N == nullptr)) { | 
| 336 |     if (Status) | 
| 337 |       *Status = demangle_invalid_args; | 
| 338 |     return nullptr; | 
| 339 |   } | 
| 340 |  | 
| 341 |   int InternalStatus = demangle_success; | 
| 342 |   Demangler Parser(MangledName, MangledName + std::strlen(MangledName)); | 
| 343 |   OutputStream S; | 
| 344 |  | 
| 345 |   Node *AST = Parser.parse(); | 
| 346 |  | 
| 347 |   if (AST == nullptr) | 
| 348 |     InternalStatus = demangle_invalid_mangled_name; | 
| 349 |   else if (!initializeOutputStream(Buf, N, S, 1024)) | 
| 350 |     InternalStatus = demangle_memory_alloc_failure; | 
| 351 |   else { | 
| 352 |     assert(Parser.ForwardTemplateRefs.empty()); | 
| 353 |     AST->print(S); | 
| 354 |     S += '\0'; | 
| 355 |     if (N != nullptr) | 
| 356 |       *N = S.getCurrentPosition(); | 
| 357 |     Buf = S.getBuffer(); | 
| 358 |   } | 
| 359 |  | 
| 360 |   if (Status) | 
| 361 |     *Status = InternalStatus; | 
| 362 |   return InternalStatus == demangle_success ? Buf : nullptr; | 
| 363 | } | 
| 364 | }  // __cxxabiv1 | 
| 365 |  |