605 lines · cpp
1//===------------------------- ItaniumDemangle.cpp ------------------------===//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 "llvm/Demangle/Demangle.h"14#include "llvm/Demangle/ItaniumDemangle.h"15 16#include <cassert>17#include <cctype>18#include <cstdio>19#include <cstdlib>20#include <cstring>21#include <exception>22#include <functional>23#include <utility>24 25using namespace llvm;26using namespace llvm::itanium_demangle;27 28// <discriminator> := _ <non-negative number> # when number < 1029// := __ <non-negative number> _ # when number >= 1030// extension := decimal-digit+ # at the end of string31const char *itanium_demangle::parse_discriminator(const char *first,32 const char *last) {33 // parse but ignore discriminator34 if (first != last) {35 if (*first == '_') {36 const char *t1 = first + 1;37 if (t1 != last) {38 if (std::isdigit(*t1))39 first = t1 + 1;40 else if (*t1 == '_') {41 for (++t1; t1 != last && std::isdigit(*t1); ++t1)42 ;43 if (t1 != last && *t1 == '_')44 first = t1 + 1;45 }46 }47 } else if (std::isdigit(*first)) {48 const char *t1 = first + 1;49 for (; t1 != last && std::isdigit(*t1); ++t1)50 ;51 if (t1 == last)52 first = last;53 }54 }55 return first;56}57 58#ifndef NDEBUG59namespace {60struct DumpVisitor {61 unsigned Depth = 0;62 bool PendingNewline = false;63 64 template<typename NodeT> static constexpr bool wantsNewline(const NodeT *) {65 return true;66 }67 static bool wantsNewline(NodeArray A) { return !A.empty(); }68 static constexpr bool wantsNewline(...) { return false; }69 70 template<typename ...Ts> static bool anyWantNewline(Ts ...Vs) {71 for (bool B : {wantsNewline(Vs)...})72 if (B)73 return true;74 return false;75 }76 77 void printStr(const char *S) { fprintf(stderr, "%s", S); }78 void print(std::string_view SV) {79 fprintf(stderr, "\"%.*s\"", (int)SV.size(), SV.data());80 }81 void print(const Node *N) {82 if (N)83 N->visit(std::ref(*this));84 else85 printStr("<null>");86 }87 void print(NodeArray A) {88 ++Depth;89 printStr("{");90 bool First = true;91 for (const Node *N : A) {92 if (First)93 print(N);94 else95 printWithComma(N);96 First = false;97 }98 printStr("}");99 --Depth;100 }101 102 // Overload used when T is exactly 'bool', not merely convertible to 'bool'.103 void print(bool B) { printStr(B ? "true" : "false"); }104 105 template <class T> std::enable_if_t<std::is_unsigned<T>::value> print(T N) {106 fprintf(stderr, "%llu", (unsigned long long)N);107 }108 109 template <class T> std::enable_if_t<std::is_signed<T>::value> print(T N) {110 fprintf(stderr, "%lld", (long long)N);111 }112 113 void print(ReferenceKind RK) {114 switch (RK) {115 case ReferenceKind::LValue:116 return printStr("ReferenceKind::LValue");117 case ReferenceKind::RValue:118 return printStr("ReferenceKind::RValue");119 }120 }121 void print(FunctionRefQual RQ) {122 switch (RQ) {123 case FunctionRefQual::FrefQualNone:124 return printStr("FunctionRefQual::FrefQualNone");125 case FunctionRefQual::FrefQualLValue:126 return printStr("FunctionRefQual::FrefQualLValue");127 case FunctionRefQual::FrefQualRValue:128 return printStr("FunctionRefQual::FrefQualRValue");129 }130 }131 void print(Qualifiers Qs) {132 if (!Qs) return printStr("QualNone");133 struct QualName { Qualifiers Q; const char *Name; } Names[] = {134 {QualConst, "QualConst"},135 {QualVolatile, "QualVolatile"},136 {QualRestrict, "QualRestrict"},137 };138 for (QualName Name : Names) {139 if (Qs & Name.Q) {140 printStr(Name.Name);141 Qs = Qualifiers(Qs & ~Name.Q);142 if (Qs) printStr(" | ");143 }144 }145 }146 void print(SpecialSubKind SSK) {147 switch (SSK) {148 case SpecialSubKind::allocator:149 return printStr("SpecialSubKind::allocator");150 case SpecialSubKind::basic_string:151 return printStr("SpecialSubKind::basic_string");152 case SpecialSubKind::string:153 return printStr("SpecialSubKind::string");154 case SpecialSubKind::istream:155 return printStr("SpecialSubKind::istream");156 case SpecialSubKind::ostream:157 return printStr("SpecialSubKind::ostream");158 case SpecialSubKind::iostream:159 return printStr("SpecialSubKind::iostream");160 }161 }162 void print(TemplateParamKind TPK) {163 switch (TPK) {164 case TemplateParamKind::Type:165 return printStr("TemplateParamKind::Type");166 case TemplateParamKind::NonType:167 return printStr("TemplateParamKind::NonType");168 case TemplateParamKind::Template:169 return printStr("TemplateParamKind::Template");170 }171 }172 void print(Node::Prec P) {173 switch (P) {174 case Node::Prec::Primary:175 return printStr("Node::Prec::Primary");176 case Node::Prec::Postfix:177 return printStr("Node::Prec::Postfix");178 case Node::Prec::Unary:179 return printStr("Node::Prec::Unary");180 case Node::Prec::Cast:181 return printStr("Node::Prec::Cast");182 case Node::Prec::PtrMem:183 return printStr("Node::Prec::PtrMem");184 case Node::Prec::Multiplicative:185 return printStr("Node::Prec::Multiplicative");186 case Node::Prec::Additive:187 return printStr("Node::Prec::Additive");188 case Node::Prec::Shift:189 return printStr("Node::Prec::Shift");190 case Node::Prec::Spaceship:191 return printStr("Node::Prec::Spaceship");192 case Node::Prec::Relational:193 return printStr("Node::Prec::Relational");194 case Node::Prec::Equality:195 return printStr("Node::Prec::Equality");196 case Node::Prec::And:197 return printStr("Node::Prec::And");198 case Node::Prec::Xor:199 return printStr("Node::Prec::Xor");200 case Node::Prec::Ior:201 return printStr("Node::Prec::Ior");202 case Node::Prec::AndIf:203 return printStr("Node::Prec::AndIf");204 case Node::Prec::OrIf:205 return printStr("Node::Prec::OrIf");206 case Node::Prec::Conditional:207 return printStr("Node::Prec::Conditional");208 case Node::Prec::Assign:209 return printStr("Node::Prec::Assign");210 case Node::Prec::Comma:211 return printStr("Node::Prec::Comma");212 case Node::Prec::Default:213 return printStr("Node::Prec::Default");214 }215 }216 217 void newLine() {218 printStr("\n");219 for (unsigned I = 0; I != Depth; ++I)220 printStr(" ");221 PendingNewline = false;222 }223 224 template<typename T> void printWithPendingNewline(T V) {225 print(V);226 if (wantsNewline(V))227 PendingNewline = true;228 }229 230 template<typename T> void printWithComma(T V) {231 if (PendingNewline || wantsNewline(V)) {232 printStr(",");233 newLine();234 } else {235 printStr(", ");236 }237 238 printWithPendingNewline(V);239 }240 241 struct CtorArgPrinter {242 DumpVisitor &Visitor;243 244 template<typename T, typename ...Rest> void operator()(T V, Rest ...Vs) {245 if (Visitor.anyWantNewline(V, Vs...))246 Visitor.newLine();247 Visitor.printWithPendingNewline(V);248 int PrintInOrder[] = { (Visitor.printWithComma(Vs), 0)..., 0 };249 (void)PrintInOrder;250 }251 };252 253 template<typename NodeT> void operator()(const NodeT *Node) {254 Depth += 2;255 fprintf(stderr, "%s(", itanium_demangle::NodeKind<NodeT>::name());256 Node->match(CtorArgPrinter{*this});257 fprintf(stderr, ")");258 Depth -= 2;259 }260 261 void operator()(const ForwardTemplateReference *Node) {262 Depth += 2;263 fprintf(stderr, "ForwardTemplateReference(");264 if (Node->Ref && !Node->Printing) {265 Node->Printing = true;266 CtorArgPrinter{*this}(Node->Ref);267 Node->Printing = false;268 } else {269 CtorArgPrinter{*this}(Node->Index);270 }271 fprintf(stderr, ")");272 Depth -= 2;273 }274};275}276 277void itanium_demangle::Node::dump() const {278 DumpVisitor V;279 visit(std::ref(V));280 V.newLine();281}282#endif283 284namespace {285class BumpPointerAllocator {286 struct BlockMeta {287 BlockMeta* Next;288 size_t Current;289 };290 291 static constexpr size_t AllocSize = 4096;292 static constexpr size_t UsableAllocSize = AllocSize - sizeof(BlockMeta);293 294 alignas(long double) char InitialBuffer[AllocSize];295 BlockMeta* BlockList = nullptr;296 297 void grow() {298 char* NewMeta = static_cast<char *>(std::malloc(AllocSize));299 if (NewMeta == nullptr)300 std::terminate();301 BlockList = new (NewMeta) BlockMeta{BlockList, 0};302 }303 304 void* allocateMassive(size_t NBytes) {305 NBytes += sizeof(BlockMeta);306 BlockMeta* NewMeta = reinterpret_cast<BlockMeta*>(std::malloc(NBytes));307 if (NewMeta == nullptr)308 std::terminate();309 BlockList->Next = new (NewMeta) BlockMeta{BlockList->Next, 0};310 return static_cast<void*>(NewMeta + 1);311 }312 313public:314 BumpPointerAllocator()315 : BlockList(new (InitialBuffer) BlockMeta{nullptr, 0}) {}316 317 void* allocate(size_t N) {318 N = (N + 15u) & ~15u;319 if (N + BlockList->Current >= UsableAllocSize) {320 if (N > UsableAllocSize)321 return allocateMassive(N);322 grow();323 }324 BlockList->Current += N;325 return static_cast<void*>(reinterpret_cast<char*>(BlockList + 1) +326 BlockList->Current - N);327 }328 329 void reset() {330 while (BlockList) {331 BlockMeta* Tmp = BlockList;332 BlockList = BlockList->Next;333 if (reinterpret_cast<char*>(Tmp) != InitialBuffer)334 std::free(Tmp);335 }336 BlockList = new (InitialBuffer) BlockMeta{nullptr, 0};337 }338 339 ~BumpPointerAllocator() { reset(); }340};341 342class DefaultAllocator {343 BumpPointerAllocator Alloc;344 345public:346 void reset() { Alloc.reset(); }347 348 template<typename T, typename ...Args> T *makeNode(Args &&...args) {349 return new (Alloc.allocate(sizeof(T)))350 T(std::forward<Args>(args)...);351 }352 353 void *allocateNodeArray(size_t sz) {354 return Alloc.allocate(sizeof(Node *) * sz);355 }356};357} // unnamed namespace358 359//===----------------------------------------------------------------------===//360// Code beyond this point should not be synchronized with libc++abi.361//===----------------------------------------------------------------------===//362 363using Demangler = itanium_demangle::ManglingParser<DefaultAllocator>;364 365char *llvm::itaniumDemangle(std::string_view MangledName, bool ParseParams) {366 if (MangledName.empty())367 return nullptr;368 369 Demangler Parser(MangledName.data(),370 MangledName.data() + MangledName.length());371 Node *AST = Parser.parse(ParseParams);372 if (!AST)373 return nullptr;374 375 OutputBuffer OB;376 assert(Parser.ForwardTemplateRefs.empty());377 AST->print(OB);378 OB += '\0';379 return OB.getBuffer();380}381 382ItaniumPartialDemangler::ItaniumPartialDemangler()383 : RootNode(nullptr), Context(new Demangler{nullptr, nullptr}) {}384 385ItaniumPartialDemangler::~ItaniumPartialDemangler() {386 delete static_cast<Demangler *>(Context);387}388 389ItaniumPartialDemangler::ItaniumPartialDemangler(390 ItaniumPartialDemangler &&Other)391 : RootNode(Other.RootNode), Context(Other.Context) {392 Other.Context = Other.RootNode = nullptr;393}394 395ItaniumPartialDemangler &ItaniumPartialDemangler::396operator=(ItaniumPartialDemangler &&Other) {397 std::swap(RootNode, Other.RootNode);398 std::swap(Context, Other.Context);399 return *this;400}401 402// Demangle MangledName into an AST, storing it into this->RootNode.403bool ItaniumPartialDemangler::partialDemangle(const char *MangledName) {404 Demangler *Parser = static_cast<Demangler *>(Context);405 size_t Len = std::strlen(MangledName);406 Parser->reset(MangledName, MangledName + Len);407 RootNode = Parser->parse();408 return RootNode == nullptr;409}410static char *printNode(const Node *RootNode, OutputBuffer &OB, size_t *N) {411 RootNode->print(OB);412 OB += '\0';413 if (N != nullptr)414 *N = OB.getCurrentPosition();415 return OB.getBuffer();416}417 418static char *printNode(const Node *RootNode, char *Buf, size_t *N) {419 OutputBuffer OB(Buf, N);420 return printNode(RootNode, OB, N);421}422 423char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const {424 if (!isFunction())425 return nullptr;426 427 const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName();428 429 while (true) {430 switch (Name->getKind()) {431 case Node::KAbiTagAttr:432 Name = static_cast<const AbiTagAttr *>(Name)->Base;433 continue;434 case Node::KModuleEntity:435 Name = static_cast<const ModuleEntity *>(Name)->Name;436 continue;437 case Node::KNestedName:438 Name = static_cast<const NestedName *>(Name)->Name;439 continue;440 case Node::KLocalName:441 Name = static_cast<const LocalName *>(Name)->Entity;442 continue;443 case Node::KNameWithTemplateArgs:444 Name = static_cast<const NameWithTemplateArgs *>(Name)->Name;445 continue;446 default:447 return printNode(Name, Buf, N);448 }449 }450}451 452char *ItaniumPartialDemangler::getFunctionDeclContextName(char *Buf,453 size_t *N) const {454 if (!isFunction())455 return nullptr;456 const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName();457 458 OutputBuffer OB(Buf, N);459 460 KeepGoingLocalFunction:461 while (true) {462 if (Name->getKind() == Node::KAbiTagAttr) {463 Name = static_cast<const AbiTagAttr *>(Name)->Base;464 continue;465 }466 if (Name->getKind() == Node::KNameWithTemplateArgs) {467 Name = static_cast<const NameWithTemplateArgs *>(Name)->Name;468 continue;469 }470 break;471 }472 473 if (Name->getKind() == Node::KModuleEntity)474 Name = static_cast<const ModuleEntity *>(Name)->Name;475 476 switch (Name->getKind()) {477 case Node::KNestedName:478 static_cast<const NestedName *>(Name)->Qual->print(OB);479 break;480 case Node::KLocalName: {481 auto *LN = static_cast<const LocalName *>(Name);482 LN->Encoding->print(OB);483 OB += "::";484 Name = LN->Entity;485 goto KeepGoingLocalFunction;486 }487 default:488 break;489 }490 OB += '\0';491 if (N != nullptr)492 *N = OB.getCurrentPosition();493 return OB.getBuffer();494}495 496char *ItaniumPartialDemangler::getFunctionName(char *Buf, size_t *N) const {497 if (!isFunction())498 return nullptr;499 auto *Name = static_cast<FunctionEncoding *>(RootNode)->getName();500 return printNode(Name, Buf, N);501}502 503char *ItaniumPartialDemangler::getFunctionParameters(char *Buf,504 size_t *N) const {505 if (!isFunction())506 return nullptr;507 NodeArray Params = static_cast<FunctionEncoding *>(RootNode)->getParams();508 509 OutputBuffer OB(Buf, N);510 511 OB += '(';512 Params.printWithComma(OB);513 OB += ')';514 OB += '\0';515 if (N != nullptr)516 *N = OB.getCurrentPosition();517 return OB.getBuffer();518}519 520char *ItaniumPartialDemangler::getFunctionReturnType(521 char *Buf, size_t *N) const {522 if (!isFunction())523 return nullptr;524 525 OutputBuffer OB(Buf, N);526 527 if (const Node *Ret =528 static_cast<const FunctionEncoding *>(RootNode)->getReturnType())529 Ret->print(OB);530 531 OB += '\0';532 if (N != nullptr)533 *N = OB.getCurrentPosition();534 return OB.getBuffer();535}536 537char *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const {538 assert(RootNode != nullptr && "must call partialDemangle()");539 return printNode(static_cast<Node *>(RootNode), Buf, N);540}541 542char *ItaniumPartialDemangler::finishDemangle(void *OB) const {543 assert(RootNode != nullptr && "must call partialDemangle()");544 assert(OB != nullptr && "valid OutputBuffer argument required");545 return printNode(static_cast<Node *>(RootNode),546 *static_cast<OutputBuffer *>(OB),547 /*N=*/nullptr);548}549 550bool ItaniumPartialDemangler::hasFunctionQualifiers() const {551 assert(RootNode != nullptr && "must call partialDemangle()");552 if (!isFunction())553 return false;554 auto *E = static_cast<const FunctionEncoding *>(RootNode);555 return E->getCVQuals() != QualNone || E->getRefQual() != FrefQualNone;556}557 558bool ItaniumPartialDemangler::isCtorOrDtor() const {559 const Node *N = static_cast<const Node *>(RootNode);560 while (N) {561 switch (N->getKind()) {562 default:563 return false;564 case Node::KCtorDtorName:565 return true;566 567 case Node::KAbiTagAttr:568 N = static_cast<const AbiTagAttr *>(N)->Base;569 break;570 case Node::KFunctionEncoding:571 N = static_cast<const FunctionEncoding *>(N)->getName();572 break;573 case Node::KLocalName:574 N = static_cast<const LocalName *>(N)->Entity;575 break;576 case Node::KNameWithTemplateArgs:577 N = static_cast<const NameWithTemplateArgs *>(N)->Name;578 break;579 case Node::KNestedName:580 N = static_cast<const NestedName *>(N)->Name;581 break;582 case Node::KModuleEntity:583 N = static_cast<const ModuleEntity *>(N)->Name;584 break;585 }586 }587 return false;588}589 590bool ItaniumPartialDemangler::isFunction() const {591 assert(RootNode != nullptr && "must call partialDemangle()");592 return static_cast<const Node *>(RootNode)->getKind() ==593 Node::KFunctionEncoding;594}595 596bool ItaniumPartialDemangler::isSpecialName() const {597 assert(RootNode != nullptr && "must call partialDemangle()");598 auto K = static_cast<const Node *>(RootNode)->getKind();599 return K == Node::KSpecialName || K == Node::KCtorVtableSpecialName;600}601 602bool ItaniumPartialDemangler::isData() const {603 return !isFunction() && !isSpecialName();604}605