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 | constexpr const char *itanium_demangle::FloatData<float>::spec; |
32 | constexpr const char *itanium_demangle::FloatData<double>::spec; |
33 | constexpr 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 |
38 | const 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 |
66 | namespace { |
67 | struct 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 | |
286 | void itanium_demangle::Node::dump() const { |
287 | DumpVisitor V; |
288 | visit(F: std::ref(t&: V)); |
289 | V.newLine(); |
290 | } |
291 | #endif |
292 | |
293 | namespace { |
294 | class 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 | |
322 | public: |
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 | |
351 | class DefaultAllocator { |
352 | BumpPointerAllocator Alloc; |
353 | |
354 | public: |
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 | |
372 | using Demangler = itanium_demangle::ManglingParser<DefaultAllocator>; |
373 | |
374 | namespace { |
375 | enum : 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 | |
383 | namespace __cxxabiv1 { |
384 | extern "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 | |