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