• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
parse_discriminator(const char * first,const char * last)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 
wantsNewline__anon068f9f010111::DumpVisitor72   template<typename NodeT> static constexpr bool wantsNewline(const NodeT *) {
73     return true;
74   }
wantsNewline__anon068f9f010111::DumpVisitor75   static bool wantsNewline(NodeArray A) { return !A.empty(); }
wantsNewline__anon068f9f010111::DumpVisitor76   static constexpr bool wantsNewline(...) { return false; }
77 
anyWantNewline__anon068f9f010111::DumpVisitor78   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 
printStr__anon068f9f010111::DumpVisitor85   void printStr(const char *S) { fprintf(stderr, "%s", S); }
print__anon068f9f010111::DumpVisitor86   void print(StringView SV) {
87     fprintf(stderr, "\"%.*s\"", (int)SV.size(), SV.begin());
88   }
print__anon068f9f010111::DumpVisitor89   void print(const Node *N) {
90     if (N)
91       N->visit(std::ref(*this));
92     else
93       printStr("<null>");
94   }
print__anon068f9f010111::DumpVisitor95   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   }
print__anon068f9f010111::DumpVisitor103   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'.
print__anon068f9f010111::DumpVisitor119   void print(bool B) { printStr(B ? "true" : "false"); }
120 
121   template <class T>
print__anon068f9f010111::DumpVisitor122   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>
print__anon068f9f010111::DumpVisitor127   typename std::enable_if<std::is_signed<T>::value>::type print(T N) {
128     fprintf(stderr, "%lld", (long long)N);
129   }
130 
print__anon068f9f010111::DumpVisitor131   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   }
print__anon068f9f010111::DumpVisitor139   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   }
print__anon068f9f010111::DumpVisitor149   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   }
print__anon068f9f010111::DumpVisitor164   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 
newLine__anon068f9f010111::DumpVisitor181   void newLine() {
182     printStr("\n");
183     for (unsigned I = 0; I != Depth; ++I)
184       printStr(" ");
185     PendingNewline = false;
186   }
187 
printWithPendingNewline__anon068f9f010111::DumpVisitor188   template<typename T> void printWithPendingNewline(T V) {
189     print(V);
190     if (wantsNewline(V))
191       PendingNewline = true;
192   }
193 
printWithComma__anon068f9f010111::DumpVisitor194   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 
operator ()__anon068f9f010111::DumpVisitor::CtorArgPrinter208     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 
operator ()__anon068f9f010111::DumpVisitor217   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 
operator ()__anon068f9f010111::DumpVisitor225   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 
dump() const241 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 
grow()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 
allocateMassive(size_t NBytes)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:
BumpPointerAllocator()278   BumpPointerAllocator()
279       : BlockList(new (InitialBuffer) BlockMeta{nullptr, 0}) {}
280 
allocate(size_t N)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 
reset()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 
~BumpPointerAllocator()303   ~BumpPointerAllocator() { reset(); }
304 };
305 
306 class DefaultAllocator {
307   BumpPointerAllocator Alloc;
308 
309 public:
reset()310   void reset() { Alloc.reset(); }
311 
makeNode(Args &&...args)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 
allocateNodeArray(size_t sz)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 *
__cxa_demangle(const char * MangledName,char * Buf,size_t * N,int * Status)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.getCurrentPosition();
363     Buf = S.getBuffer();
364   }
365 
366   if (Status)
367     *Status = InternalStatus;
368   return InternalStatus == demangle_success ? Buf : nullptr;
369 }
370 }  // __cxxabiv1
371