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