409 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// FIXME: (possibly) incomplete list of features that clang mangles that this10// file does not yet support:11// - C++ modules TS12 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 31// <discriminator> := _ <non-negative number> # when number < 1032// := __ <non-negative number> _ # when number >= 1033// extension := decimal-digit+ # at the end of string34const char *itanium_demangle::parse_discriminator(const char *first,35 const char *last) {36 // parse but ignore discriminator37 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 NDEBUG62namespace {63struct 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 else88 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 else98 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 282void itanium_demangle::Node::dump() const {283 DumpVisitor V;284 visit(std::ref(V));285 V.newLine();286}287#endif288 289namespace {290class 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 318public: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 347class DefaultAllocator {348 BumpPointerAllocator Alloc;349 350public: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 namespace363 364//===----------------------------------------------------------------------===//365// Code beyond this point should not be synchronized with LLVM.366//===----------------------------------------------------------------------===//367 368using Demangler = itanium_demangle::ManglingParser<DefaultAllocator>;369 370namespace {371enum : 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 379namespace __cxxabiv1 {380extern "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} // __cxxabiv1409