libcxxabi

libcxxabi mirror with random patches
git clone https://git.neptards.moe/neptards/libcxxabi.git
Log | Files | Refs

cxa_demangle.cpp (10066B)


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