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

source code of libcxxabi/src/cxa_demangle.cpp