summaryrefslogtreecommitdiffhomepage
path: root/demangle/third_party/llvm/lib
diff options
context:
space:
mode:
Diffstat (limited to 'demangle/third_party/llvm/lib')
-rw-r--r--demangle/third_party/llvm/lib/Demangle/Demangle.cpp68
-rw-r--r--demangle/third_party/llvm/lib/Demangle/ItaniumDemangle.cpp608
-rw-r--r--demangle/third_party/llvm/lib/Demangle/MicrosoftDemangle.cpp2368
-rw-r--r--demangle/third_party/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp658
4 files changed, 3702 insertions, 0 deletions
diff --git a/demangle/third_party/llvm/lib/Demangle/Demangle.cpp b/demangle/third_party/llvm/lib/Demangle/Demangle.cpp
new file mode 100644
index 00000000..b5f2369d
--- /dev/null
+++ b/demangle/third_party/llvm/lib/Demangle/Demangle.cpp
@@ -0,0 +1,68 @@
+//===-- Demangle.cpp - Common demangling functions ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file contains definitions of common demangling functions.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Demangle/Demangle.h"
+#include <cstdlib>
+#include <cstring>
+
+static bool isItaniumEncoding(const char *S) {
+ // Itanium encoding requires 1 or 3 leading underscores, followed by 'Z'.
+ return std::strncmp(S, "_Z", 2) == 0 || std::strncmp(S, "___Z", 4) == 0;
+}
+
+#if 0
+static bool isRustEncoding(const char *S) { return S[0] == '_' && S[1] == 'R'; }
+
+static bool isDLangEncoding(const std::string &MangledName) {
+ return MangledName.size() >= 2 && MangledName[0] == '_' &&
+ MangledName[1] == 'D';
+}
+#endif
+
+std::string llvm::demangle(const std::string &MangledName) {
+ std::string Result;
+ const char *S = MangledName.c_str();
+
+ if (nonMicrosoftDemangle(S, Result))
+ return Result;
+
+ if (S[0] == '_' && nonMicrosoftDemangle(S + 1, Result))
+ return Result;
+
+ if (char *Demangled =
+ microsoftDemangle(S, nullptr, nullptr, nullptr, nullptr)) {
+ Result = Demangled;
+ std::free(Demangled);
+ return Result;
+ }
+
+ return MangledName;
+}
+
+bool llvm::nonMicrosoftDemangle(const char *MangledName, std::string &Result) {
+ char *Demangled = nullptr;
+ if (isItaniumEncoding(MangledName))
+ Demangled = itaniumDemangle(MangledName, nullptr, nullptr, nullptr);
+#if 0
+ else if (isRustEncoding(MangledName))
+ Demangled = rustDemangle(MangledName);
+ else if (isDLangEncoding(MangledName))
+ Demangled = dlangDemangle(MangledName);
+#endif
+
+ if (!Demangled)
+ return false;
+
+ Result = Demangled;
+ std::free(Demangled);
+ return true;
+}
diff --git a/demangle/third_party/llvm/lib/Demangle/ItaniumDemangle.cpp b/demangle/third_party/llvm/lib/Demangle/ItaniumDemangle.cpp
new file mode 100644
index 00000000..9b646ea8
--- /dev/null
+++ b/demangle/third_party/llvm/lib/Demangle/ItaniumDemangle.cpp
@@ -0,0 +1,608 @@
+//===------------------------- ItaniumDemangle.cpp ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// FIXME: (possibly) incomplete list of features that clang mangles that this
+// file does not yet support:
+// - C++ modules TS
+
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Demangle/ItaniumDemangle.h"
+
+#include <cassert>
+#include <cctype>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <functional>
+#include <utility>
+
+using namespace llvm;
+using namespace llvm::itanium_demangle;
+
+constexpr const char *itanium_demangle::FloatData<float>::spec;
+constexpr const char *itanium_demangle::FloatData<double>::spec;
+constexpr const char *itanium_demangle::FloatData<long double>::spec;
+
+// <discriminator> := _ <non-negative number> # when number < 10
+// := __ <non-negative number> _ # when number >= 10
+// extension := decimal-digit+ # at the end of string
+const char *itanium_demangle::parse_discriminator(const char *first,
+ const char *last) {
+ // parse but ignore discriminator
+ if (first != last) {
+ if (*first == '_') {
+ const char *t1 = first + 1;
+ if (t1 != last) {
+ if (std::isdigit(*t1))
+ first = t1 + 1;
+ else if (*t1 == '_') {
+ for (++t1; t1 != last && std::isdigit(*t1); ++t1)
+ ;
+ if (t1 != last && *t1 == '_')
+ first = t1 + 1;
+ }
+ }
+ } else if (std::isdigit(*first)) {
+ const char *t1 = first + 1;
+ for (; t1 != last && std::isdigit(*t1); ++t1)
+ ;
+ if (t1 == last)
+ first = last;
+ }
+ }
+ return first;
+}
+
+#ifndef NDEBUG
+namespace {
+struct DumpVisitor {
+ unsigned Depth = 0;
+ bool PendingNewline = false;
+
+ template<typename NodeT> static constexpr bool wantsNewline(const NodeT *) {
+ return true;
+ }
+ static bool wantsNewline(NodeArray A) { return !A.empty(); }
+ static constexpr bool wantsNewline(...) { return false; }
+
+ template<typename ...Ts> static bool anyWantNewline(Ts ...Vs) {
+ for (bool B : {wantsNewline(Vs)...})
+ if (B)
+ return true;
+ return false;
+ }
+
+ void printStr(const char *S) { fprintf(stderr, "%s", S); }
+ void print(StringView SV) {
+ fprintf(stderr, "\"%.*s\"", (int)SV.size(), SV.begin());
+ }
+ void print(const Node *N) {
+ if (N)
+ N->visit(std::ref(*this));
+ else
+ printStr("<null>");
+ }
+ void print(NodeArray A) {
+ ++Depth;
+ printStr("{");
+ bool First = true;
+ for (const Node *N : A) {
+ if (First)
+ print(N);
+ else
+ printWithComma(N);
+ First = false;
+ }
+ printStr("}");
+ --Depth;
+ }
+
+ // Overload used when T is exactly 'bool', not merely convertible to 'bool'.
+ void print(bool B) { printStr(B ? "true" : "false"); }
+
+ template <class T> std::enable_if_t<std::is_unsigned<T>::value> print(T N) {
+ fprintf(stderr, "%llu", (unsigned long long)N);
+ }
+
+ template <class T> std::enable_if_t<std::is_signed<T>::value> print(T N) {
+ fprintf(stderr, "%lld", (long long)N);
+ }
+
+ void print(ReferenceKind RK) {
+ switch (RK) {
+ case ReferenceKind::LValue:
+ return printStr("ReferenceKind::LValue");
+ case ReferenceKind::RValue:
+ return printStr("ReferenceKind::RValue");
+ }
+ }
+ void print(FunctionRefQual RQ) {
+ switch (RQ) {
+ case FunctionRefQual::FrefQualNone:
+ return printStr("FunctionRefQual::FrefQualNone");
+ case FunctionRefQual::FrefQualLValue:
+ return printStr("FunctionRefQual::FrefQualLValue");
+ case FunctionRefQual::FrefQualRValue:
+ return printStr("FunctionRefQual::FrefQualRValue");
+ }
+ }
+ void print(Qualifiers Qs) {
+ if (!Qs) return printStr("QualNone");
+ struct QualName { Qualifiers Q; const char *Name; } Names[] = {
+ {QualConst, "QualConst"},
+ {QualVolatile, "QualVolatile"},
+ {QualRestrict, "QualRestrict"},
+ };
+ for (QualName Name : Names) {
+ if (Qs & Name.Q) {
+ printStr(Name.Name);
+ Qs = Qualifiers(Qs & ~Name.Q);
+ if (Qs) printStr(" | ");
+ }
+ }
+ }
+ void print(SpecialSubKind SSK) {
+ switch (SSK) {
+ case SpecialSubKind::allocator:
+ return printStr("SpecialSubKind::allocator");
+ case SpecialSubKind::basic_string:
+ return printStr("SpecialSubKind::basic_string");
+ case SpecialSubKind::string:
+ return printStr("SpecialSubKind::string");
+ case SpecialSubKind::istream:
+ return printStr("SpecialSubKind::istream");
+ case SpecialSubKind::ostream:
+ return printStr("SpecialSubKind::ostream");
+ case SpecialSubKind::iostream:
+ return printStr("SpecialSubKind::iostream");
+ }
+ }
+ void print(TemplateParamKind TPK) {
+ switch (TPK) {
+ case TemplateParamKind::Type:
+ return printStr("TemplateParamKind::Type");
+ case TemplateParamKind::NonType:
+ return printStr("TemplateParamKind::NonType");
+ case TemplateParamKind::Template:
+ return printStr("TemplateParamKind::Template");
+ }
+ }
+ void print(Node::Prec P) {
+ switch (P) {
+ case Node::Prec::Primary:
+ return printStr("Node::Prec::Primary");
+ case Node::Prec::Postfix:
+ return printStr("Node::Prec::Postfix");
+ case Node::Prec::Unary:
+ return printStr("Node::Prec::Unary");
+ case Node::Prec::Cast:
+ return printStr("Node::Prec::Cast");
+ case Node::Prec::PtrMem:
+ return printStr("Node::Prec::PtrMem");
+ case Node::Prec::Multiplicative:
+ return printStr("Node::Prec::Multiplicative");
+ case Node::Prec::Additive:
+ return printStr("Node::Prec::Additive");
+ case Node::Prec::Shift:
+ return printStr("Node::Prec::Shift");
+ case Node::Prec::Spaceship:
+ return printStr("Node::Prec::Spaceship");
+ case Node::Prec::Relational:
+ return printStr("Node::Prec::Relational");
+ case Node::Prec::Equality:
+ return printStr("Node::Prec::Equality");
+ case Node::Prec::And:
+ return printStr("Node::Prec::And");
+ case Node::Prec::Xor:
+ return printStr("Node::Prec::Xor");
+ case Node::Prec::Ior:
+ return printStr("Node::Prec::Ior");
+ case Node::Prec::AndIf:
+ return printStr("Node::Prec::AndIf");
+ case Node::Prec::OrIf:
+ return printStr("Node::Prec::OrIf");
+ case Node::Prec::Conditional:
+ return printStr("Node::Prec::Conditional");
+ case Node::Prec::Assign:
+ return printStr("Node::Prec::Assign");
+ case Node::Prec::Comma:
+ return printStr("Node::Prec::Comma");
+ case Node::Prec::Default:
+ return printStr("Node::Prec::Default");
+ }
+ }
+
+ void newLine() {
+ printStr("\n");
+ for (unsigned I = 0; I != Depth; ++I)
+ printStr(" ");
+ PendingNewline = false;
+ }
+
+ template<typename T> void printWithPendingNewline(T V) {
+ print(V);
+ if (wantsNewline(V))
+ PendingNewline = true;
+ }
+
+ template<typename T> void printWithComma(T V) {
+ if (PendingNewline || wantsNewline(V)) {
+ printStr(",");
+ newLine();
+ } else {
+ printStr(", ");
+ }
+
+ printWithPendingNewline(V);
+ }
+
+ struct CtorArgPrinter {
+ DumpVisitor &Visitor;
+
+ template<typename T, typename ...Rest> void operator()(T V, Rest ...Vs) {
+ if (Visitor.anyWantNewline(V, Vs...))
+ Visitor.newLine();
+ Visitor.printWithPendingNewline(V);
+ int PrintInOrder[] = { (Visitor.printWithComma(Vs), 0)..., 0 };
+ (void)PrintInOrder;
+ }
+ };
+
+ template<typename NodeT> void operator()(const NodeT *Node) {
+ Depth += 2;
+ fprintf(stderr, "%s(", itanium_demangle::NodeKind<NodeT>::name());
+ Node->match(CtorArgPrinter{*this});
+ fprintf(stderr, ")");
+ Depth -= 2;
+ }
+
+ void operator()(const ForwardTemplateReference *Node) {
+ Depth += 2;
+ fprintf(stderr, "ForwardTemplateReference(");
+ if (Node->Ref && !Node->Printing) {
+ Node->Printing = true;
+ CtorArgPrinter{*this}(Node->Ref);
+ Node->Printing = false;
+ } else {
+ CtorArgPrinter{*this}(Node->Index);
+ }
+ fprintf(stderr, ")");
+ Depth -= 2;
+ }
+};
+}
+
+void itanium_demangle::Node::dump() const {
+ DumpVisitor V;
+ visit(std::ref(V));
+ V.newLine();
+}
+#endif
+
+namespace {
+class BumpPointerAllocator {
+ struct BlockMeta {
+ BlockMeta* Next;
+ size_t Current;
+ };
+
+ static constexpr size_t AllocSize = 4096;
+ static constexpr size_t UsableAllocSize = AllocSize - sizeof(BlockMeta);
+
+ alignas(long double) char InitialBuffer[AllocSize];
+ BlockMeta* BlockList = nullptr;
+
+ void grow() {
+ char* NewMeta = static_cast<char *>(std::malloc(AllocSize));
+ if (NewMeta == nullptr)
+ std::terminate();
+ BlockList = new (NewMeta) BlockMeta{BlockList, 0};
+ }
+
+ void* allocateMassive(size_t NBytes) {
+ NBytes += sizeof(BlockMeta);
+ BlockMeta* NewMeta = reinterpret_cast<BlockMeta*>(std::malloc(NBytes));
+ if (NewMeta == nullptr)
+ std::terminate();
+ BlockList->Next = new (NewMeta) BlockMeta{BlockList->Next, 0};
+ return static_cast<void*>(NewMeta + 1);
+ }
+
+public:
+ BumpPointerAllocator()
+ : BlockList(new (InitialBuffer) BlockMeta{nullptr, 0}) {}
+
+ void* allocate(size_t N) {
+ N = (N + 15u) & ~15u;
+ if (N + BlockList->Current >= UsableAllocSize) {
+ if (N > UsableAllocSize)
+ return allocateMassive(N);
+ grow();
+ }
+ BlockList->Current += N;
+ return static_cast<void*>(reinterpret_cast<char*>(BlockList + 1) +
+ BlockList->Current - N);
+ }
+
+ void reset() {
+ while (BlockList) {
+ BlockMeta* Tmp = BlockList;
+ BlockList = BlockList->Next;
+ if (reinterpret_cast<char*>(Tmp) != InitialBuffer)
+ std::free(Tmp);
+ }
+ BlockList = new (InitialBuffer) BlockMeta{nullptr, 0};
+ }
+
+ ~BumpPointerAllocator() { reset(); }
+};
+
+class DefaultAllocator {
+ BumpPointerAllocator Alloc;
+
+public:
+ void reset() { Alloc.reset(); }
+
+ template<typename T, typename ...Args> T *makeNode(Args &&...args) {
+ return new (Alloc.allocate(sizeof(T)))
+ T(std::forward<Args>(args)...);
+ }
+
+ void *allocateNodeArray(size_t sz) {
+ return Alloc.allocate(sizeof(Node *) * sz);
+ }
+};
+} // unnamed namespace
+
+//===----------------------------------------------------------------------===//
+// Code beyond this point should not be synchronized with libc++abi.
+//===----------------------------------------------------------------------===//
+
+using Demangler = itanium_demangle::ManglingParser<DefaultAllocator>;
+
+char *llvm::itaniumDemangle(const char *MangledName, char *Buf,
+ size_t *N, int *Status) {
+ if (MangledName == nullptr || (Buf != nullptr && N == nullptr)) {
+ if (Status)
+ *Status = demangle_invalid_args;
+ return nullptr;
+ }
+
+ int InternalStatus = demangle_success;
+ Demangler Parser(MangledName, MangledName + std::strlen(MangledName));
+ Node *AST = Parser.parse();
+
+ if (AST == nullptr)
+ InternalStatus = demangle_invalid_mangled_name;
+ else {
+ OutputBuffer OB(Buf, N);
+ assert(Parser.ForwardTemplateRefs.empty());
+ AST->print(OB);
+ OB += '\0';
+ if (N != nullptr)
+ *N = OB.getCurrentPosition();
+ Buf = OB.getBuffer();
+ }
+
+ if (Status)
+ *Status = InternalStatus;
+ return InternalStatus == demangle_success ? Buf : nullptr;
+}
+
+ItaniumPartialDemangler::ItaniumPartialDemangler()
+ : RootNode(nullptr), Context(new Demangler{nullptr, nullptr}) {}
+
+ItaniumPartialDemangler::~ItaniumPartialDemangler() {
+ delete static_cast<Demangler *>(Context);
+}
+
+ItaniumPartialDemangler::ItaniumPartialDemangler(
+ ItaniumPartialDemangler &&Other)
+ : RootNode(Other.RootNode), Context(Other.Context) {
+ Other.Context = Other.RootNode = nullptr;
+}
+
+ItaniumPartialDemangler &ItaniumPartialDemangler::
+operator=(ItaniumPartialDemangler &&Other) {
+ std::swap(RootNode, Other.RootNode);
+ std::swap(Context, Other.Context);
+ return *this;
+}
+
+// Demangle MangledName into an AST, storing it into this->RootNode.
+bool ItaniumPartialDemangler::partialDemangle(const char *MangledName) {
+ Demangler *Parser = static_cast<Demangler *>(Context);
+ size_t Len = std::strlen(MangledName);
+ Parser->reset(MangledName, MangledName + Len);
+ RootNode = Parser->parse();
+ return RootNode == nullptr;
+}
+
+static char *printNode(const Node *RootNode, char *Buf, size_t *N) {
+ OutputBuffer OB(Buf, N);
+ RootNode->print(OB);
+ OB += '\0';
+ if (N != nullptr)
+ *N = OB.getCurrentPosition();
+ return OB.getBuffer();
+}
+
+char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const {
+ if (!isFunction())
+ return nullptr;
+
+ const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName();
+
+ while (true) {
+ switch (Name->getKind()) {
+ case Node::KAbiTagAttr:
+ Name = static_cast<const AbiTagAttr *>(Name)->Base;
+ continue;
+ case Node::KModuleEntity:
+ Name = static_cast<const ModuleEntity *>(Name)->Name;
+ continue;
+ case Node::KNestedName:
+ Name = static_cast<const NestedName *>(Name)->Name;
+ continue;
+ case Node::KLocalName:
+ Name = static_cast<const LocalName *>(Name)->Entity;
+ continue;
+ case Node::KNameWithTemplateArgs:
+ Name = static_cast<const NameWithTemplateArgs *>(Name)->Name;
+ continue;
+ default:
+ return printNode(Name, Buf, N);
+ }
+ }
+}
+
+char *ItaniumPartialDemangler::getFunctionDeclContextName(char *Buf,
+ size_t *N) const {
+ if (!isFunction())
+ return nullptr;
+ const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName();
+
+ OutputBuffer OB(Buf, N);
+
+ KeepGoingLocalFunction:
+ while (true) {
+ if (Name->getKind() == Node::KAbiTagAttr) {
+ Name = static_cast<const AbiTagAttr *>(Name)->Base;
+ continue;
+ }
+ if (Name->getKind() == Node::KNameWithTemplateArgs) {
+ Name = static_cast<const NameWithTemplateArgs *>(Name)->Name;
+ continue;
+ }
+ break;
+ }
+
+ if (Name->getKind() == Node::KModuleEntity)
+ Name = static_cast<const ModuleEntity *>(Name)->Name;
+
+ switch (Name->getKind()) {
+ case Node::KNestedName:
+ static_cast<const NestedName *>(Name)->Qual->print(OB);
+ break;
+ case Node::KLocalName: {
+ auto *LN = static_cast<const LocalName *>(Name);
+ LN->Encoding->print(OB);
+ OB += "::";
+ Name = LN->Entity;
+ goto KeepGoingLocalFunction;
+ }
+ default:
+ break;
+ }
+ OB += '\0';
+ if (N != nullptr)
+ *N = OB.getCurrentPosition();
+ return OB.getBuffer();
+}
+
+char *ItaniumPartialDemangler::getFunctionName(char *Buf, size_t *N) const {
+ if (!isFunction())
+ return nullptr;
+ auto *Name = static_cast<FunctionEncoding *>(RootNode)->getName();
+ return printNode(Name, Buf, N);
+}
+
+char *ItaniumPartialDemangler::getFunctionParameters(char *Buf,
+ size_t *N) const {
+ if (!isFunction())
+ return nullptr;
+ NodeArray Params = static_cast<FunctionEncoding *>(RootNode)->getParams();
+
+ OutputBuffer OB(Buf, N);
+
+ OB += '(';
+ Params.printWithComma(OB);
+ OB += ')';
+ OB += '\0';
+ if (N != nullptr)
+ *N = OB.getCurrentPosition();
+ return OB.getBuffer();
+}
+
+char *ItaniumPartialDemangler::getFunctionReturnType(
+ char *Buf, size_t *N) const {
+ if (!isFunction())
+ return nullptr;
+
+ OutputBuffer OB(Buf, N);
+
+ if (const Node *Ret =
+ static_cast<const FunctionEncoding *>(RootNode)->getReturnType())
+ Ret->print(OB);
+
+ OB += '\0';
+ if (N != nullptr)
+ *N = OB.getCurrentPosition();
+ return OB.getBuffer();
+}
+
+char *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const {
+ assert(RootNode != nullptr && "must call partialDemangle()");
+ return printNode(static_cast<Node *>(RootNode), Buf, N);
+}
+
+bool ItaniumPartialDemangler::hasFunctionQualifiers() const {
+ assert(RootNode != nullptr && "must call partialDemangle()");
+ if (!isFunction())
+ return false;
+ auto *E = static_cast<const FunctionEncoding *>(RootNode);
+ return E->getCVQuals() != QualNone || E->getRefQual() != FrefQualNone;
+}
+
+bool ItaniumPartialDemangler::isCtorOrDtor() const {
+ const Node *N = static_cast<const Node *>(RootNode);
+ while (N) {
+ switch (N->getKind()) {
+ default:
+ return false;
+ case Node::KCtorDtorName:
+ return true;
+
+ case Node::KAbiTagAttr:
+ N = static_cast<const AbiTagAttr *>(N)->Base;
+ break;
+ case Node::KFunctionEncoding:
+ N = static_cast<const FunctionEncoding *>(N)->getName();
+ break;
+ case Node::KLocalName:
+ N = static_cast<const LocalName *>(N)->Entity;
+ break;
+ case Node::KNameWithTemplateArgs:
+ N = static_cast<const NameWithTemplateArgs *>(N)->Name;
+ break;
+ case Node::KNestedName:
+ N = static_cast<const NestedName *>(N)->Name;
+ break;
+ case Node::KModuleEntity:
+ N = static_cast<const ModuleEntity *>(N)->Name;
+ break;
+ }
+ }
+ return false;
+}
+
+bool ItaniumPartialDemangler::isFunction() const {
+ assert(RootNode != nullptr && "must call partialDemangle()");
+ return static_cast<const Node *>(RootNode)->getKind() ==
+ Node::KFunctionEncoding;
+}
+
+bool ItaniumPartialDemangler::isSpecialName() const {
+ assert(RootNode != nullptr && "must call partialDemangle()");
+ auto K = static_cast<const Node *>(RootNode)->getKind();
+ return K == Node::KSpecialName || K == Node::KCtorVtableSpecialName;
+}
+
+bool ItaniumPartialDemangler::isData() const {
+ return !isFunction() && !isSpecialName();
+}
diff --git a/demangle/third_party/llvm/lib/Demangle/MicrosoftDemangle.cpp b/demangle/third_party/llvm/lib/Demangle/MicrosoftDemangle.cpp
new file mode 100644
index 00000000..c21b0a30
--- /dev/null
+++ b/demangle/third_party/llvm/lib/Demangle/MicrosoftDemangle.cpp
@@ -0,0 +1,2368 @@
+//===- MicrosoftDemangle.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a demangler for MSVC-style mangled symbols.
+//
+// This file has no dependencies on the rest of LLVM so that it can be
+// easily reused in other programs such as libcxxabi.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Demangle/MicrosoftDemangle.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Demangle/MicrosoftDemangleNodes.h"
+
+#include "llvm/Demangle/DemangleConfig.h"
+#include "llvm/Demangle/StringView.h"
+#include "llvm/Demangle/Utility.h"
+
+#include <array>
+#include <cctype>
+#include <cstdio>
+#include <tuple>
+
+using namespace llvm;
+using namespace ms_demangle;
+
+static bool startsWithDigit(StringView S) {
+ return !S.empty() && std::isdigit(S.front());
+}
+
+
+struct NodeList {
+ Node *N = nullptr;
+ NodeList *Next = nullptr;
+};
+
+static bool isMemberPointer(StringView MangledName, bool &Error) {
+ Error = false;
+ switch (MangledName.popFront()) {
+ case '$':
+ // This is probably an rvalue reference (e.g. $$Q), and you cannot have an
+ // rvalue reference to a member.
+ return false;
+ case 'A':
+ // 'A' indicates a reference, and you cannot have a reference to a member
+ // function or member.
+ return false;
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ // These 4 values indicate some kind of pointer, but we still don't know
+ // what.
+ break;
+ default:
+ // isMemberPointer() is called only if isPointerType() returns true,
+ // and it rejects other prefixes.
+ DEMANGLE_UNREACHABLE;
+ }
+
+ // If it starts with a number, then 6 indicates a non-member function
+ // pointer, and 8 indicates a member function pointer.
+ if (startsWithDigit(MangledName)) {
+ if (MangledName[0] != '6' && MangledName[0] != '8') {
+ Error = true;
+ return false;
+ }
+ return (MangledName[0] == '8');
+ }
+
+ // Remove ext qualifiers since those can appear on either type and are
+ // therefore not indicative.
+ MangledName.consumeFront('E'); // 64-bit
+ MangledName.consumeFront('I'); // restrict
+ MangledName.consumeFront('F'); // unaligned
+
+ if (MangledName.empty()) {
+ Error = true;
+ return false;
+ }
+
+ // The next value should be either ABCD (non-member) or QRST (member).
+ switch (MangledName.front()) {
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ return false;
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ return true;
+ default:
+ Error = true;
+ return false;
+ }
+}
+
+static SpecialIntrinsicKind
+consumeSpecialIntrinsicKind(StringView &MangledName) {
+ if (MangledName.consumeFront("?_7"))
+ return SpecialIntrinsicKind::Vftable;
+ if (MangledName.consumeFront("?_8"))
+ return SpecialIntrinsicKind::Vbtable;
+ if (MangledName.consumeFront("?_9"))
+ return SpecialIntrinsicKind::VcallThunk;
+ if (MangledName.consumeFront("?_A"))
+ return SpecialIntrinsicKind::Typeof;
+ if (MangledName.consumeFront("?_B"))
+ return SpecialIntrinsicKind::LocalStaticGuard;
+ if (MangledName.consumeFront("?_C"))
+ return SpecialIntrinsicKind::StringLiteralSymbol;
+ if (MangledName.consumeFront("?_P"))
+ return SpecialIntrinsicKind::UdtReturning;
+ if (MangledName.consumeFront("?_R0"))
+ return SpecialIntrinsicKind::RttiTypeDescriptor;
+ if (MangledName.consumeFront("?_R1"))
+ return SpecialIntrinsicKind::RttiBaseClassDescriptor;
+ if (MangledName.consumeFront("?_R2"))
+ return SpecialIntrinsicKind::RttiBaseClassArray;
+ if (MangledName.consumeFront("?_R3"))
+ return SpecialIntrinsicKind::RttiClassHierarchyDescriptor;
+ if (MangledName.consumeFront("?_R4"))
+ return SpecialIntrinsicKind::RttiCompleteObjLocator;
+ if (MangledName.consumeFront("?_S"))
+ return SpecialIntrinsicKind::LocalVftable;
+ if (MangledName.consumeFront("?__E"))
+ return SpecialIntrinsicKind::DynamicInitializer;
+ if (MangledName.consumeFront("?__F"))
+ return SpecialIntrinsicKind::DynamicAtexitDestructor;
+ if (MangledName.consumeFront("?__J"))
+ return SpecialIntrinsicKind::LocalStaticThreadGuard;
+ return SpecialIntrinsicKind::None;
+}
+
+static bool startsWithLocalScopePattern(StringView S) {
+ if (!S.consumeFront('?'))
+ return false;
+
+ size_t End = S.find('?');
+ if (End == StringView::npos)
+ return false;
+ StringView Candidate = S.substr(0, End);
+ if (Candidate.empty())
+ return false;
+
+ // \?[0-9]\?
+ // ?@? is the discriminator 0.
+ if (Candidate.size() == 1)
+ return Candidate[0] == '@' || (Candidate[0] >= '0' && Candidate[0] <= '9');
+
+ // If it's not 0-9, then it's an encoded number terminated with an @
+ if (Candidate.back() != '@')
+ return false;
+ Candidate = Candidate.dropBack();
+
+ // An encoded number starts with B-P and all subsequent digits are in A-P.
+ // Note that the reason the first digit cannot be A is two fold. First, it
+ // would create an ambiguity with ?A which delimits the beginning of an
+ // anonymous namespace. Second, A represents 0, and you don't start a multi
+ // digit number with a leading 0. Presumably the anonymous namespace
+ // ambiguity is also why single digit encoded numbers use 0-9 rather than A-J.
+ if (Candidate[0] < 'B' || Candidate[0] > 'P')
+ return false;
+ Candidate = Candidate.dropFront();
+ while (!Candidate.empty()) {
+ if (Candidate[0] < 'A' || Candidate[0] > 'P')
+ return false;
+ Candidate = Candidate.dropFront();
+ }
+
+ return true;
+}
+
+static bool isTagType(StringView S) {
+ switch (S.front()) {
+ case 'T': // union
+ case 'U': // struct
+ case 'V': // class
+ case 'W': // enum
+ return true;
+ }
+ return false;
+}
+
+static bool isCustomType(StringView S) { return S[0] == '?'; }
+
+static bool isPointerType(StringView S) {
+ if (S.startsWith("$$Q")) // foo &&
+ return true;
+
+ switch (S.front()) {
+ case 'A': // foo &
+ case 'P': // foo *
+ case 'Q': // foo *const
+ case 'R': // foo *volatile
+ case 'S': // foo *const volatile
+ return true;
+ }
+ return false;
+}
+
+static bool isArrayType(StringView S) { return S[0] == 'Y'; }
+
+static bool isFunctionType(StringView S) {
+ return S.startsWith("$$A8@@") || S.startsWith("$$A6");
+}
+
+static FunctionRefQualifier
+demangleFunctionRefQualifier(StringView &MangledName) {
+ if (MangledName.consumeFront('G'))
+ return FunctionRefQualifier::Reference;
+ else if (MangledName.consumeFront('H'))
+ return FunctionRefQualifier::RValueReference;
+ return FunctionRefQualifier::None;
+}
+
+static std::pair<Qualifiers, PointerAffinity>
+demanglePointerCVQualifiers(StringView &MangledName) {
+ if (MangledName.consumeFront("$$Q"))
+ return std::make_pair(Q_None, PointerAffinity::RValueReference);
+
+ switch (MangledName.popFront()) {
+ case 'A':
+ return std::make_pair(Q_None, PointerAffinity::Reference);
+ case 'P':
+ return std::make_pair(Q_None, PointerAffinity::Pointer);
+ case 'Q':
+ return std::make_pair(Q_Const, PointerAffinity::Pointer);
+ case 'R':
+ return std::make_pair(Q_Volatile, PointerAffinity::Pointer);
+ case 'S':
+ return std::make_pair(Qualifiers(Q_Const | Q_Volatile),
+ PointerAffinity::Pointer);
+ }
+ // This function is only called if isPointerType() returns true,
+ // and it only returns true for the six cases listed above.
+ DEMANGLE_UNREACHABLE;
+}
+
+StringView Demangler::copyString(StringView Borrowed) {
+ char *Stable = Arena.allocUnalignedBuffer(Borrowed.size());
+ // This is not a micro-optimization, it avoids UB, should Borrowed be an null
+ // buffer.
+ if (Borrowed.size())
+ std::memcpy(Stable, Borrowed.begin(), Borrowed.size());
+
+ return {Stable, Borrowed.size()};
+}
+
+SpecialTableSymbolNode *
+Demangler::demangleSpecialTableSymbolNode(StringView &MangledName,
+ SpecialIntrinsicKind K) {
+ NamedIdentifierNode *NI = Arena.alloc<NamedIdentifierNode>();
+ switch (K) {
+ case SpecialIntrinsicKind::Vftable:
+ NI->Name = "`vftable'";
+ break;
+ case SpecialIntrinsicKind::Vbtable:
+ NI->Name = "`vbtable'";
+ break;
+ case SpecialIntrinsicKind::LocalVftable:
+ NI->Name = "`local vftable'";
+ break;
+ case SpecialIntrinsicKind::RttiCompleteObjLocator:
+ NI->Name = "`RTTI Complete Object Locator'";
+ break;
+ default:
+ DEMANGLE_UNREACHABLE;
+ }
+ QualifiedNameNode *QN = demangleNameScopeChain(MangledName, NI);
+ SpecialTableSymbolNode *STSN = Arena.alloc<SpecialTableSymbolNode>();
+ STSN->Name = QN;
+ bool IsMember = false;
+ if (MangledName.empty()) {
+ Error = true;
+ return nullptr;
+ }
+ char Front = MangledName.popFront();
+ if (Front != '6' && Front != '7') {
+ Error = true;
+ return nullptr;
+ }
+
+ std::tie(STSN->Quals, IsMember) = demangleQualifiers(MangledName);
+ if (!MangledName.consumeFront('@'))
+ STSN->TargetName = demangleFullyQualifiedTypeName(MangledName);
+ return STSN;
+}
+
+LocalStaticGuardVariableNode *
+Demangler::demangleLocalStaticGuard(StringView &MangledName, bool IsThread) {
+ LocalStaticGuardIdentifierNode *LSGI =
+ Arena.alloc<LocalStaticGuardIdentifierNode>();
+ LSGI->IsThread = IsThread;
+ QualifiedNameNode *QN = demangleNameScopeChain(MangledName, LSGI);
+ LocalStaticGuardVariableNode *LSGVN =
+ Arena.alloc<LocalStaticGuardVariableNode>();
+ LSGVN->Name = QN;
+
+ if (MangledName.consumeFront("4IA"))
+ LSGVN->IsVisible = false;
+ else if (MangledName.consumeFront("5"))
+ LSGVN->IsVisible = true;
+ else {
+ Error = true;
+ return nullptr;
+ }
+
+ if (!MangledName.empty())
+ LSGI->ScopeIndex = demangleUnsigned(MangledName);
+ return LSGVN;
+}
+
+static NamedIdentifierNode *synthesizeNamedIdentifier(ArenaAllocator &Arena,
+ StringView Name) {
+ NamedIdentifierNode *Id = Arena.alloc<NamedIdentifierNode>();
+ Id->Name = Name;
+ return Id;
+}
+
+static QualifiedNameNode *synthesizeQualifiedName(ArenaAllocator &Arena,
+ IdentifierNode *Identifier) {
+ QualifiedNameNode *QN = Arena.alloc<QualifiedNameNode>();
+ QN->Components = Arena.alloc<NodeArrayNode>();
+ QN->Components->Count = 1;
+ QN->Components->Nodes = Arena.allocArray<Node *>(1);
+ QN->Components->Nodes[0] = Identifier;
+ return QN;
+}
+
+static QualifiedNameNode *synthesizeQualifiedName(ArenaAllocator &Arena,
+ StringView Name) {
+ NamedIdentifierNode *Id = synthesizeNamedIdentifier(Arena, Name);
+ return synthesizeQualifiedName(Arena, Id);
+}
+
+static VariableSymbolNode *synthesizeVariable(ArenaAllocator &Arena,
+ TypeNode *Type,
+ StringView VariableName) {
+ VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();
+ VSN->Type = Type;
+ VSN->Name = synthesizeQualifiedName(Arena, VariableName);
+ return VSN;
+}
+
+VariableSymbolNode *Demangler::demangleUntypedVariable(
+ ArenaAllocator &Arena, StringView &MangledName, StringView VariableName) {
+ NamedIdentifierNode *NI = synthesizeNamedIdentifier(Arena, VariableName);
+ QualifiedNameNode *QN = demangleNameScopeChain(MangledName, NI);
+ VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();
+ VSN->Name = QN;
+ if (MangledName.consumeFront("8"))
+ return VSN;
+
+ Error = true;
+ return nullptr;
+}
+
+VariableSymbolNode *
+Demangler::demangleRttiBaseClassDescriptorNode(ArenaAllocator &Arena,
+ StringView &MangledName) {
+ RttiBaseClassDescriptorNode *RBCDN =
+ Arena.alloc<RttiBaseClassDescriptorNode>();
+ RBCDN->NVOffset = demangleUnsigned(MangledName);
+ RBCDN->VBPtrOffset = demangleSigned(MangledName);
+ RBCDN->VBTableOffset = demangleUnsigned(MangledName);
+ RBCDN->Flags = demangleUnsigned(MangledName);
+ if (Error)
+ return nullptr;
+
+ VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();
+ VSN->Name = demangleNameScopeChain(MangledName, RBCDN);
+ MangledName.consumeFront('8');
+ return VSN;
+}
+
+FunctionSymbolNode *Demangler::demangleInitFiniStub(StringView &MangledName,
+ bool IsDestructor) {
+ DynamicStructorIdentifierNode *DSIN =
+ Arena.alloc<DynamicStructorIdentifierNode>();
+ DSIN->IsDestructor = IsDestructor;
+
+ bool IsKnownStaticDataMember = false;
+ if (MangledName.consumeFront('?'))
+ IsKnownStaticDataMember = true;
+
+ SymbolNode *Symbol = demangleDeclarator(MangledName);
+ if (Error)
+ return nullptr;
+
+ FunctionSymbolNode *FSN = nullptr;
+
+ if (Symbol->kind() == NodeKind::VariableSymbol) {
+ DSIN->Variable = static_cast<VariableSymbolNode *>(Symbol);
+
+ // Older versions of clang mangled this type of symbol incorrectly. They
+ // would omit the leading ? and they would only emit a single @ at the end.
+ // The correct mangling is a leading ? and 2 trailing @ signs. Handle
+ // both cases.
+ int AtCount = IsKnownStaticDataMember ? 2 : 1;
+ for (int I = 0; I < AtCount; ++I) {
+ if (MangledName.consumeFront('@'))
+ continue;
+ Error = true;
+ return nullptr;
+ }
+
+ FSN = demangleFunctionEncoding(MangledName);
+ if (FSN)
+ FSN->Name = synthesizeQualifiedName(Arena, DSIN);
+ } else {
+ if (IsKnownStaticDataMember) {
+ // This was supposed to be a static data member, but we got a function.
+ Error = true;
+ return nullptr;
+ }
+
+ FSN = static_cast<FunctionSymbolNode *>(Symbol);
+ DSIN->Name = Symbol->Name;
+ FSN->Name = synthesizeQualifiedName(Arena, DSIN);
+ }
+
+ return FSN;
+}
+
+SymbolNode *Demangler::demangleSpecialIntrinsic(StringView &MangledName) {
+ SpecialIntrinsicKind SIK = consumeSpecialIntrinsicKind(MangledName);
+
+ switch (SIK) {
+ case SpecialIntrinsicKind::None:
+ return nullptr;
+ case SpecialIntrinsicKind::StringLiteralSymbol:
+ return demangleStringLiteral(MangledName);
+ case SpecialIntrinsicKind::Vftable:
+ case SpecialIntrinsicKind::Vbtable:
+ case SpecialIntrinsicKind::LocalVftable:
+ case SpecialIntrinsicKind::RttiCompleteObjLocator:
+ return demangleSpecialTableSymbolNode(MangledName, SIK);
+ case SpecialIntrinsicKind::VcallThunk:
+ return demangleVcallThunkNode(MangledName);
+ case SpecialIntrinsicKind::LocalStaticGuard:
+ return demangleLocalStaticGuard(MangledName, /*IsThread=*/false);
+ case SpecialIntrinsicKind::LocalStaticThreadGuard:
+ return demangleLocalStaticGuard(MangledName, /*IsThread=*/true);
+ case SpecialIntrinsicKind::RttiTypeDescriptor: {
+ TypeNode *T = demangleType(MangledName, QualifierMangleMode::Result);
+ if (Error)
+ break;
+ if (!MangledName.consumeFront("@8"))
+ break;
+ if (!MangledName.empty())
+ break;
+ return synthesizeVariable(Arena, T, "`RTTI Type Descriptor'");
+ }
+ case SpecialIntrinsicKind::RttiBaseClassArray:
+ return demangleUntypedVariable(Arena, MangledName,
+ "`RTTI Base Class Array'");
+ case SpecialIntrinsicKind::RttiClassHierarchyDescriptor:
+ return demangleUntypedVariable(Arena, MangledName,
+ "`RTTI Class Hierarchy Descriptor'");
+ case SpecialIntrinsicKind::RttiBaseClassDescriptor:
+ return demangleRttiBaseClassDescriptorNode(Arena, MangledName);
+ case SpecialIntrinsicKind::DynamicInitializer:
+ return demangleInitFiniStub(MangledName, /*IsDestructor=*/false);
+ case SpecialIntrinsicKind::DynamicAtexitDestructor:
+ return demangleInitFiniStub(MangledName, /*IsDestructor=*/true);
+ case SpecialIntrinsicKind::Typeof:
+ case SpecialIntrinsicKind::UdtReturning:
+ // It's unclear which tools produces these manglings, so demangling
+ // support is not (yet?) implemented.
+ break;
+ case SpecialIntrinsicKind::Unknown:
+ DEMANGLE_UNREACHABLE; // Never returned by consumeSpecialIntrinsicKind.
+ }
+ Error = true;
+ return nullptr;
+}
+
+IdentifierNode *
+Demangler::demangleFunctionIdentifierCode(StringView &MangledName) {
+ assert(MangledName.startsWith('?'));
+ MangledName = MangledName.dropFront();
+ if (MangledName.empty()) {
+ Error = true;
+ return nullptr;
+ }
+
+ if (MangledName.consumeFront("__"))
+ return demangleFunctionIdentifierCode(
+ MangledName, FunctionIdentifierCodeGroup::DoubleUnder);
+ if (MangledName.consumeFront("_"))
+ return demangleFunctionIdentifierCode(MangledName,
+ FunctionIdentifierCodeGroup::Under);
+ return demangleFunctionIdentifierCode(MangledName,
+ FunctionIdentifierCodeGroup::Basic);
+}
+
+StructorIdentifierNode *
+Demangler::demangleStructorIdentifier(StringView &MangledName,
+ bool IsDestructor) {
+ StructorIdentifierNode *N = Arena.alloc<StructorIdentifierNode>();
+ N->IsDestructor = IsDestructor;
+ return N;
+}
+
+ConversionOperatorIdentifierNode *
+Demangler::demangleConversionOperatorIdentifier(StringView &MangledName) {
+ ConversionOperatorIdentifierNode *N =
+ Arena.alloc<ConversionOperatorIdentifierNode>();
+ return N;
+}
+
+LiteralOperatorIdentifierNode *
+Demangler::demangleLiteralOperatorIdentifier(StringView &MangledName) {
+ LiteralOperatorIdentifierNode *N =
+ Arena.alloc<LiteralOperatorIdentifierNode>();
+ N->Name = demangleSimpleString(MangledName, /*Memorize=*/false);
+ return N;
+}
+
+IntrinsicFunctionKind
+Demangler::translateIntrinsicFunctionCode(char CH,
+ FunctionIdentifierCodeGroup Group) {
+ using IFK = IntrinsicFunctionKind;
+ if (!(CH >= '0' && CH <= '9') && !(CH >= 'A' && CH <= 'Z')) {
+ Error = true;
+ return IFK::None;
+ }
+
+ // Not all ? identifiers are intrinsics *functions*. This function only maps
+ // operator codes for the special functions, all others are handled elsewhere,
+ // hence the IFK::None entries in the table.
+ static IFK Basic[36] = {
+ IFK::None, // ?0 # Foo::Foo()
+ IFK::None, // ?1 # Foo::~Foo()
+ IFK::New, // ?2 # operator new
+ IFK::Delete, // ?3 # operator delete
+ IFK::Assign, // ?4 # operator=
+ IFK::RightShift, // ?5 # operator>>
+ IFK::LeftShift, // ?6 # operator<<
+ IFK::LogicalNot, // ?7 # operator!
+ IFK::Equals, // ?8 # operator==
+ IFK::NotEquals, // ?9 # operator!=
+ IFK::ArraySubscript, // ?A # operator[]
+ IFK::None, // ?B # Foo::operator <type>()
+ IFK::Pointer, // ?C # operator->
+ IFK::Dereference, // ?D # operator*
+ IFK::Increment, // ?E # operator++
+ IFK::Decrement, // ?F # operator--
+ IFK::Minus, // ?G # operator-
+ IFK::Plus, // ?H # operator+
+ IFK::BitwiseAnd, // ?I # operator&
+ IFK::MemberPointer, // ?J # operator->*
+ IFK::Divide, // ?K # operator/
+ IFK::Modulus, // ?L # operator%
+ IFK::LessThan, // ?M operator<
+ IFK::LessThanEqual, // ?N operator<=
+ IFK::GreaterThan, // ?O operator>
+ IFK::GreaterThanEqual, // ?P operator>=
+ IFK::Comma, // ?Q operator,
+ IFK::Parens, // ?R operator()
+ IFK::BitwiseNot, // ?S operator~
+ IFK::BitwiseXor, // ?T operator^
+ IFK::BitwiseOr, // ?U operator|
+ IFK::LogicalAnd, // ?V operator&&
+ IFK::LogicalOr, // ?W operator||
+ IFK::TimesEqual, // ?X operator*=
+ IFK::PlusEqual, // ?Y operator+=
+ IFK::MinusEqual, // ?Z operator-=
+ };
+ static IFK Under[36] = {
+ IFK::DivEqual, // ?_0 operator/=
+ IFK::ModEqual, // ?_1 operator%=
+ IFK::RshEqual, // ?_2 operator>>=
+ IFK::LshEqual, // ?_3 operator<<=
+ IFK::BitwiseAndEqual, // ?_4 operator&=
+ IFK::BitwiseOrEqual, // ?_5 operator|=
+ IFK::BitwiseXorEqual, // ?_6 operator^=
+ IFK::None, // ?_7 # vftable
+ IFK::None, // ?_8 # vbtable
+ IFK::None, // ?_9 # vcall
+ IFK::None, // ?_A # typeof
+ IFK::None, // ?_B # local static guard
+ IFK::None, // ?_C # string literal
+ IFK::VbaseDtor, // ?_D # vbase destructor
+ IFK::VecDelDtor, // ?_E # vector deleting destructor
+ IFK::DefaultCtorClosure, // ?_F # default constructor closure
+ IFK::ScalarDelDtor, // ?_G # scalar deleting destructor
+ IFK::VecCtorIter, // ?_H # vector constructor iterator
+ IFK::VecDtorIter, // ?_I # vector destructor iterator
+ IFK::VecVbaseCtorIter, // ?_J # vector vbase constructor iterator
+ IFK::VdispMap, // ?_K # virtual displacement map
+ IFK::EHVecCtorIter, // ?_L # eh vector constructor iterator
+ IFK::EHVecDtorIter, // ?_M # eh vector destructor iterator
+ IFK::EHVecVbaseCtorIter, // ?_N # eh vector vbase constructor iterator
+ IFK::CopyCtorClosure, // ?_O # copy constructor closure
+ IFK::None, // ?_P<name> # udt returning <name>
+ IFK::None, // ?_Q # <unknown>
+ IFK::None, // ?_R0 - ?_R4 # RTTI Codes
+ IFK::None, // ?_S # local vftable
+ IFK::LocalVftableCtorClosure, // ?_T # local vftable constructor closure
+ IFK::ArrayNew, // ?_U operator new[]
+ IFK::ArrayDelete, // ?_V operator delete[]
+ IFK::None, // ?_W <unused>
+ IFK::None, // ?_X <unused>
+ IFK::None, // ?_Y <unused>
+ IFK::None, // ?_Z <unused>
+ };
+ static IFK DoubleUnder[36] = {
+ IFK::None, // ?__0 <unused>
+ IFK::None, // ?__1 <unused>
+ IFK::None, // ?__2 <unused>
+ IFK::None, // ?__3 <unused>
+ IFK::None, // ?__4 <unused>
+ IFK::None, // ?__5 <unused>
+ IFK::None, // ?__6 <unused>
+ IFK::None, // ?__7 <unused>
+ IFK::None, // ?__8 <unused>
+ IFK::None, // ?__9 <unused>
+ IFK::ManVectorCtorIter, // ?__A managed vector ctor iterator
+ IFK::ManVectorDtorIter, // ?__B managed vector dtor iterator
+ IFK::EHVectorCopyCtorIter, // ?__C EH vector copy ctor iterator
+ IFK::EHVectorVbaseCopyCtorIter, // ?__D EH vector vbase copy ctor iter
+ IFK::None, // ?__E dynamic initializer for `T'
+ IFK::None, // ?__F dynamic atexit destructor for `T'
+ IFK::VectorCopyCtorIter, // ?__G vector copy constructor iter
+ IFK::VectorVbaseCopyCtorIter, // ?__H vector vbase copy ctor iter
+ IFK::ManVectorVbaseCopyCtorIter, // ?__I managed vector vbase copy ctor
+ // iter
+ IFK::None, // ?__J local static thread guard
+ IFK::None, // ?__K operator ""_name
+ IFK::CoAwait, // ?__L operator co_await
+ IFK::Spaceship, // ?__M operator<=>
+ IFK::None, // ?__N <unused>
+ IFK::None, // ?__O <unused>
+ IFK::None, // ?__P <unused>
+ IFK::None, // ?__Q <unused>
+ IFK::None, // ?__R <unused>
+ IFK::None, // ?__S <unused>
+ IFK::None, // ?__T <unused>
+ IFK::None, // ?__U <unused>
+ IFK::None, // ?__V <unused>
+ IFK::None, // ?__W <unused>
+ IFK::None, // ?__X <unused>
+ IFK::None, // ?__Y <unused>
+ IFK::None, // ?__Z <unused>
+ };
+
+ int Index = (CH >= '0' && CH <= '9') ? (CH - '0') : (CH - 'A' + 10);
+ switch (Group) {
+ case FunctionIdentifierCodeGroup::Basic:
+ return Basic[Index];
+ case FunctionIdentifierCodeGroup::Under:
+ return Under[Index];
+ case FunctionIdentifierCodeGroup::DoubleUnder:
+ return DoubleUnder[Index];
+ }
+ DEMANGLE_UNREACHABLE;
+}
+
+IdentifierNode *
+Demangler::demangleFunctionIdentifierCode(StringView &MangledName,
+ FunctionIdentifierCodeGroup Group) {
+ if (MangledName.empty()) {
+ Error = true;
+ return nullptr;
+ }
+ switch (Group) {
+ case FunctionIdentifierCodeGroup::Basic:
+ switch (char CH = MangledName.popFront()) {
+ case '0':
+ case '1':
+ return demangleStructorIdentifier(MangledName, CH == '1');
+ case 'B':
+ return demangleConversionOperatorIdentifier(MangledName);
+ default:
+ return Arena.alloc<IntrinsicFunctionIdentifierNode>(
+ translateIntrinsicFunctionCode(CH, Group));
+ }
+ case FunctionIdentifierCodeGroup::Under:
+ return Arena.alloc<IntrinsicFunctionIdentifierNode>(
+ translateIntrinsicFunctionCode(MangledName.popFront(), Group));
+ case FunctionIdentifierCodeGroup::DoubleUnder:
+ switch (char CH = MangledName.popFront()) {
+ case 'K':
+ return demangleLiteralOperatorIdentifier(MangledName);
+ default:
+ return Arena.alloc<IntrinsicFunctionIdentifierNode>(
+ translateIntrinsicFunctionCode(CH, Group));
+ }
+ }
+
+ DEMANGLE_UNREACHABLE;
+}
+
+SymbolNode *Demangler::demangleEncodedSymbol(StringView &MangledName,
+ QualifiedNameNode *Name) {
+ if (MangledName.empty()) {
+ Error = true;
+ return nullptr;
+ }
+
+ // Read a variable.
+ switch (MangledName.front()) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4': {
+ StorageClass SC = demangleVariableStorageClass(MangledName);
+ return demangleVariableEncoding(MangledName, SC);
+ }
+ }
+ FunctionSymbolNode *FSN = demangleFunctionEncoding(MangledName);
+
+ IdentifierNode *UQN = Name->getUnqualifiedIdentifier();
+ if (UQN->kind() == NodeKind::ConversionOperatorIdentifier) {
+ ConversionOperatorIdentifierNode *COIN =
+ static_cast<ConversionOperatorIdentifierNode *>(UQN);
+ if (FSN)
+ COIN->TargetType = FSN->Signature->ReturnType;
+ }
+ return FSN;
+}
+
+SymbolNode *Demangler::demangleDeclarator(StringView &MangledName) {
+ // What follows is a main symbol name. This may include namespaces or class
+ // back references.
+ QualifiedNameNode *QN = demangleFullyQualifiedSymbolName(MangledName);
+ if (Error)
+ return nullptr;
+
+ SymbolNode *Symbol = demangleEncodedSymbol(MangledName, QN);
+ if (Error)
+ return nullptr;
+ Symbol->Name = QN;
+
+ IdentifierNode *UQN = QN->getUnqualifiedIdentifier();
+ if (UQN->kind() == NodeKind::ConversionOperatorIdentifier) {
+ ConversionOperatorIdentifierNode *COIN =
+ static_cast<ConversionOperatorIdentifierNode *>(UQN);
+ if (!COIN->TargetType) {
+ Error = true;
+ return nullptr;
+ }
+ }
+ return Symbol;
+}
+
+SymbolNode *Demangler::demangleMD5Name(StringView &MangledName) {
+ assert(MangledName.startsWith("??@"));
+ // This is an MD5 mangled name. We can't demangle it, just return the
+ // mangled name.
+ // An MD5 mangled name is ??@ followed by 32 characters and a terminating @.
+ size_t MD5Last = MangledName.find('@', strlen("??@"));
+ if (MD5Last == StringView::npos) {
+ Error = true;
+ return nullptr;
+ }
+ const char *Start = MangledName.begin();
+ MangledName = MangledName.dropFront(MD5Last + 1);
+
+ // There are two additional special cases for MD5 names:
+ // 1. For complete object locators where the object name is long enough
+ // for the object to have an MD5 name, the complete object locator is
+ // called ??@...@??_R4@ (with a trailing "??_R4@" instead of the usual
+ // leading "??_R4". This is handled here.
+ // 2. For catchable types, in versions of MSVC before 2015 (<1900) or after
+ // 2017.2 (>= 1914), the catchable type mangling is _CT??@...@??@...@8
+ // instead of_CT??@...@8 with just one MD5 name. Since we don't yet
+ // demangle catchable types anywhere, this isn't handled for MD5 names
+ // either.
+ MangledName.consumeFront("??_R4@");
+
+ StringView MD5(Start, MangledName.begin());
+ SymbolNode *S = Arena.alloc<SymbolNode>(NodeKind::Md5Symbol);
+ S->Name = synthesizeQualifiedName(Arena, MD5);
+
+ return S;
+}
+
+SymbolNode *Demangler::demangleTypeinfoName(StringView &MangledName) {
+ assert(MangledName.startsWith('.'));
+ MangledName.consumeFront('.');
+
+ TypeNode *T = demangleType(MangledName, QualifierMangleMode::Result);
+ if (Error || !MangledName.empty()) {
+ Error = true;
+ return nullptr;
+ }
+ return synthesizeVariable(Arena, T, "`RTTI Type Descriptor Name'");
+}
+
+// Parser entry point.
+SymbolNode *Demangler::parse(StringView &MangledName) {
+ // Typeinfo names are strings stored in RTTI data. They're not symbol names.
+ // It's still useful to demangle them. They're the only demangled entity
+ // that doesn't start with a "?" but a ".".
+ if (MangledName.startsWith('.'))
+ return demangleTypeinfoName(MangledName);
+
+ if (MangledName.startsWith("??@"))
+ return demangleMD5Name(MangledName);
+
+ // MSVC-style mangled symbols must start with '?'.
+ if (!MangledName.startsWith('?')) {
+ Error = true;
+ return nullptr;
+ }
+
+ MangledName.consumeFront('?');
+
+ // ?$ is a template instantiation, but all other names that start with ? are
+ // operators / special names.
+ if (SymbolNode *SI = demangleSpecialIntrinsic(MangledName))
+ return SI;
+
+ return demangleDeclarator(MangledName);
+}
+
+TagTypeNode *Demangler::parseTagUniqueName(StringView &MangledName) {
+ if (!MangledName.consumeFront(".?A")) {
+ Error = true;
+ return nullptr;
+ }
+ MangledName.consumeFront(".?A");
+ if (MangledName.empty()) {
+ Error = true;
+ return nullptr;
+ }
+
+ return demangleClassType(MangledName);
+}
+
+// <type-encoding> ::= <storage-class> <variable-type>
+// <storage-class> ::= 0 # private static member
+// ::= 1 # protected static member
+// ::= 2 # public static member
+// ::= 3 # global
+// ::= 4 # static local
+
+VariableSymbolNode *Demangler::demangleVariableEncoding(StringView &MangledName,
+ StorageClass SC) {
+ VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();
+
+ VSN->Type = demangleType(MangledName, QualifierMangleMode::Drop);
+ VSN->SC = SC;
+
+ if (Error)
+ return nullptr;
+
+ // <variable-type> ::= <type> <cvr-qualifiers>
+ // ::= <type> <pointee-cvr-qualifiers> # pointers, references
+ switch (VSN->Type->kind()) {
+ case NodeKind::PointerType: {
+ PointerTypeNode *PTN = static_cast<PointerTypeNode *>(VSN->Type);
+
+ Qualifiers ExtraChildQuals = Q_None;
+ PTN->Quals = Qualifiers(VSN->Type->Quals |
+ demanglePointerExtQualifiers(MangledName));
+
+ bool IsMember = false;
+ std::tie(ExtraChildQuals, IsMember) = demangleQualifiers(MangledName);
+
+ if (PTN->ClassParent) {
+ QualifiedNameNode *BackRefName =
+ demangleFullyQualifiedTypeName(MangledName);
+ (void)BackRefName;
+ }
+ PTN->Pointee->Quals = Qualifiers(PTN->Pointee->Quals | ExtraChildQuals);
+
+ break;
+ }
+ default:
+ VSN->Type->Quals = demangleQualifiers(MangledName).first;
+ break;
+ }
+
+ return VSN;
+}
+
+// Sometimes numbers are encoded in mangled symbols. For example,
+// "int (*x)[20]" is a valid C type (x is a pointer to an array of
+// length 20), so we need some way to embed numbers as part of symbols.
+// This function parses it.
+//
+// <number> ::= [?] <non-negative integer>
+//
+// <non-negative integer> ::= <decimal digit> # when 1 <= Number <= 10
+// ::= <hex digit>+ @ # when Number == 0 or >= 10
+//
+// <hex-digit> ::= [A-P] # A = 0, B = 1, ...
+std::pair<uint64_t, bool> Demangler::demangleNumber(StringView &MangledName) {
+ bool IsNegative = MangledName.consumeFront('?');
+
+ if (startsWithDigit(MangledName)) {
+ uint64_t Ret = MangledName[0] - '0' + 1;
+ MangledName = MangledName.dropFront(1);
+ return {Ret, IsNegative};
+ }
+
+ uint64_t Ret = 0;
+ for (size_t i = 0; i < MangledName.size(); ++i) {
+ char C = MangledName[i];
+ if (C == '@') {
+ MangledName = MangledName.dropFront(i + 1);
+ return {Ret, IsNegative};
+ }
+ if ('A' <= C && C <= 'P') {
+ Ret = (Ret << 4) + (C - 'A');
+ continue;
+ }
+ break;
+ }
+
+ Error = true;
+ return {0ULL, false};
+}
+
+uint64_t Demangler::demangleUnsigned(StringView &MangledName) {
+ bool IsNegative = false;
+ uint64_t Number = 0;
+ std::tie(Number, IsNegative) = demangleNumber(MangledName);
+ if (IsNegative)
+ Error = true;
+ return Number;
+}
+
+int64_t Demangler::demangleSigned(StringView &MangledName) {
+ bool IsNegative = false;
+ uint64_t Number = 0;
+ std::tie(Number, IsNegative) = demangleNumber(MangledName);
+ if (Number > INT64_MAX)
+ Error = true;
+ int64_t I = static_cast<int64_t>(Number);
+ return IsNegative ? -I : I;
+}
+
+// First 10 strings can be referenced by special BackReferences ?0, ?1, ..., ?9.
+// Memorize it.
+void Demangler::memorizeString(StringView S) {
+ if (Backrefs.NamesCount >= BackrefContext::Max)
+ return;
+ for (size_t i = 0; i < Backrefs.NamesCount; ++i)
+ if (S == Backrefs.Names[i]->Name)
+ return;
+ NamedIdentifierNode *N = Arena.alloc<NamedIdentifierNode>();
+ N->Name = S;
+ Backrefs.Names[Backrefs.NamesCount++] = N;
+}
+
+NamedIdentifierNode *Demangler::demangleBackRefName(StringView &MangledName) {
+ assert(startsWithDigit(MangledName));
+
+ size_t I = MangledName[0] - '0';
+ if (I >= Backrefs.NamesCount) {
+ Error = true;
+ return nullptr;
+ }
+
+ MangledName = MangledName.dropFront();
+ return Backrefs.Names[I];
+}
+
+void Demangler::memorizeIdentifier(IdentifierNode *Identifier) {
+ // Render this class template name into a string buffer so that we can
+ // memorize it for the purpose of back-referencing.
+ OutputBuffer OB;
+ Identifier->output(OB, OF_Default);
+ StringView Owned = copyString(OB);
+ memorizeString(Owned);
+ std::free(OB.getBuffer());
+}
+
+IdentifierNode *
+Demangler::demangleTemplateInstantiationName(StringView &MangledName,
+ NameBackrefBehavior NBB) {
+ assert(MangledName.startsWith("?$"));
+ MangledName.consumeFront("?$");
+
+ BackrefContext OuterContext;
+ std::swap(OuterContext, Backrefs);
+
+ IdentifierNode *Identifier =
+ demangleUnqualifiedSymbolName(MangledName, NBB_Simple);
+ if (!Error)
+ Identifier->TemplateParams = demangleTemplateParameterList(MangledName);
+
+ std::swap(OuterContext, Backrefs);
+ if (Error)
+ return nullptr;
+
+ if (NBB & NBB_Template) {
+ // NBB_Template is only set for types and non-leaf names ("a::" in "a::b").
+ // Structors and conversion operators only makes sense in a leaf name, so
+ // reject them in NBB_Template contexts.
+ if (Identifier->kind() == NodeKind::ConversionOperatorIdentifier ||
+ Identifier->kind() == NodeKind::StructorIdentifier) {
+ Error = true;
+ return nullptr;
+ }
+
+ memorizeIdentifier(Identifier);
+ }
+
+ return Identifier;
+}
+
+NamedIdentifierNode *Demangler::demangleSimpleName(StringView &MangledName,
+ bool Memorize) {
+ StringView S = demangleSimpleString(MangledName, Memorize);
+ if (Error)
+ return nullptr;
+
+ NamedIdentifierNode *Name = Arena.alloc<NamedIdentifierNode>();
+ Name->Name = S;
+ return Name;
+}
+
+static bool isRebasedHexDigit(char C) { return (C >= 'A' && C <= 'P'); }
+
+static uint8_t rebasedHexDigitToNumber(char C) {
+ assert(isRebasedHexDigit(C));
+ return (C <= 'J') ? (C - 'A') : (10 + C - 'K');
+}
+
+uint8_t Demangler::demangleCharLiteral(StringView &MangledName) {
+ assert(!MangledName.empty());
+ if (!MangledName.startsWith('?'))
+ return MangledName.popFront();
+
+ MangledName = MangledName.dropFront();
+ if (MangledName.empty())
+ goto CharLiteralError;
+
+ if (MangledName.consumeFront('$')) {
+ // Two hex digits
+ if (MangledName.size() < 2)
+ goto CharLiteralError;
+ StringView Nibbles = MangledName.substr(0, 2);
+ if (!isRebasedHexDigit(Nibbles[0]) || !isRebasedHexDigit(Nibbles[1]))
+ goto CharLiteralError;
+ // Don't append the null terminator.
+ uint8_t C1 = rebasedHexDigitToNumber(Nibbles[0]);
+ uint8_t C2 = rebasedHexDigitToNumber(Nibbles[1]);
+ MangledName = MangledName.dropFront(2);
+ return (C1 << 4) | C2;
+ }
+
+ if (startsWithDigit(MangledName)) {
+ const char *Lookup = ",/\\:. \n\t'-";
+ char C = Lookup[MangledName[0] - '0'];
+ MangledName = MangledName.dropFront();
+ return C;
+ }
+
+ if (MangledName[0] >= 'a' && MangledName[0] <= 'z') {
+ char Lookup[26] = {'\xE1', '\xE2', '\xE3', '\xE4', '\xE5', '\xE6', '\xE7',
+ '\xE8', '\xE9', '\xEA', '\xEB', '\xEC', '\xED', '\xEE',
+ '\xEF', '\xF0', '\xF1', '\xF2', '\xF3', '\xF4', '\xF5',
+ '\xF6', '\xF7', '\xF8', '\xF9', '\xFA'};
+ char C = Lookup[MangledName[0] - 'a'];
+ MangledName = MangledName.dropFront();
+ return C;
+ }
+
+ if (MangledName[0] >= 'A' && MangledName[0] <= 'Z') {
+ char Lookup[26] = {'\xC1', '\xC2', '\xC3', '\xC4', '\xC5', '\xC6', '\xC7',
+ '\xC8', '\xC9', '\xCA', '\xCB', '\xCC', '\xCD', '\xCE',
+ '\xCF', '\xD0', '\xD1', '\xD2', '\xD3', '\xD4', '\xD5',
+ '\xD6', '\xD7', '\xD8', '\xD9', '\xDA'};
+ char C = Lookup[MangledName[0] - 'A'];
+ MangledName = MangledName.dropFront();
+ return C;
+ }
+
+CharLiteralError:
+ Error = true;
+ return '\0';
+}
+
+wchar_t Demangler::demangleWcharLiteral(StringView &MangledName) {
+ uint8_t C1, C2;
+
+ C1 = demangleCharLiteral(MangledName);
+ if (Error || MangledName.empty())
+ goto WCharLiteralError;
+ C2 = demangleCharLiteral(MangledName);
+ if (Error)
+ goto WCharLiteralError;
+
+ return ((wchar_t)C1 << 8) | (wchar_t)C2;
+
+WCharLiteralError:
+ Error = true;
+ return L'\0';
+}
+
+static void writeHexDigit(char *Buffer, uint8_t Digit) {
+ assert(Digit <= 15);
+ *Buffer = (Digit < 10) ? ('0' + Digit) : ('A' + Digit - 10);
+}
+
+static void outputHex(OutputBuffer &OB, unsigned C) {
+ assert (C != 0);
+
+ // It's easier to do the math if we can work from right to left, but we need
+ // to print the numbers from left to right. So render this into a temporary
+ // buffer first, then output the temporary buffer. Each byte is of the form
+ // \xAB, which means that each byte needs 4 characters. Since there are at
+ // most 4 bytes, we need a 4*4+1 = 17 character temporary buffer.
+ char TempBuffer[17];
+
+ ::memset(TempBuffer, 0, sizeof(TempBuffer));
+ constexpr int MaxPos = sizeof(TempBuffer) - 1;
+
+ int Pos = MaxPos - 1; // TempBuffer[MaxPos] is the terminating \0.
+ while (C != 0) {
+ for (int I = 0; I < 2; ++I) {
+ writeHexDigit(&TempBuffer[Pos--], C % 16);
+ C /= 16;
+ }
+ }
+ TempBuffer[Pos--] = 'x';
+ assert(Pos >= 0);
+ TempBuffer[Pos--] = '\\';
+ OB << StringView(&TempBuffer[Pos + 1]);
+}
+
+static void outputEscapedChar(OutputBuffer &OB, unsigned C) {
+ switch (C) {
+ case '\0': // nul
+ OB << "\\0";
+ return;
+ case '\'': // single quote
+ OB << "\\\'";
+ return;
+ case '\"': // double quote
+ OB << "\\\"";
+ return;
+ case '\\': // backslash
+ OB << "\\\\";
+ return;
+ case '\a': // bell
+ OB << "\\a";
+ return;
+ case '\b': // backspace
+ OB << "\\b";
+ return;
+ case '\f': // form feed
+ OB << "\\f";
+ return;
+ case '\n': // new line
+ OB << "\\n";
+ return;
+ case '\r': // carriage return
+ OB << "\\r";
+ return;
+ case '\t': // tab
+ OB << "\\t";
+ return;
+ case '\v': // vertical tab
+ OB << "\\v";
+ return;
+ default:
+ break;
+ }
+
+ if (C > 0x1F && C < 0x7F) {
+ // Standard ascii char.
+ OB << (char)C;
+ return;
+ }
+
+ outputHex(OB, C);
+}
+
+static unsigned countTrailingNullBytes(const uint8_t *StringBytes, int Length) {
+ const uint8_t *End = StringBytes + Length - 1;
+ unsigned Count = 0;
+ while (Length > 0 && *End == 0) {
+ --Length;
+ --End;
+ ++Count;
+ }
+ return Count;
+}
+
+static unsigned countEmbeddedNulls(const uint8_t *StringBytes,
+ unsigned Length) {
+ unsigned Result = 0;
+ for (unsigned I = 0; I < Length; ++I) {
+ if (*StringBytes++ == 0)
+ ++Result;
+ }
+ return Result;
+}
+
+// A mangled (non-wide) string literal stores the total length of the string it
+// refers to (passed in NumBytes), and it contains up to 32 bytes of actual text
+// (passed in StringBytes, NumChars).
+static unsigned guessCharByteSize(const uint8_t *StringBytes, unsigned NumChars,
+ uint64_t NumBytes) {
+ assert(NumBytes > 0);
+
+ // If the number of bytes is odd, this is guaranteed to be a char string.
+ if (NumBytes % 2 == 1)
+ return 1;
+
+ // All strings can encode at most 32 bytes of data. If it's less than that,
+ // then we encoded the entire string. In this case we check for a 1-byte,
+ // 2-byte, or 4-byte null terminator.
+ if (NumBytes < 32) {
+ unsigned TrailingNulls = countTrailingNullBytes(StringBytes, NumChars);
+ if (TrailingNulls >= 4 && NumBytes % 4 == 0)
+ return 4;
+ if (TrailingNulls >= 2)
+ return 2;
+ return 1;
+ }
+
+ // The whole string was not able to be encoded. Try to look at embedded null
+ // terminators to guess. The heuristic is that we count all embedded null
+ // terminators. If more than 2/3 are null, it's a char32. If more than 1/3
+ // are null, it's a char16. Otherwise it's a char8. This obviously isn't
+ // perfect and is biased towards languages that have ascii alphabets, but this
+ // was always going to be best effort since the encoding is lossy.
+ unsigned Nulls = countEmbeddedNulls(StringBytes, NumChars);
+ if (Nulls >= 2 * NumChars / 3 && NumBytes % 4 == 0)
+ return 4;
+ if (Nulls >= NumChars / 3)
+ return 2;
+ return 1;
+}
+
+static unsigned decodeMultiByteChar(const uint8_t *StringBytes,
+ unsigned CharIndex, unsigned CharBytes) {
+ assert(CharBytes == 1 || CharBytes == 2 || CharBytes == 4);
+ unsigned Offset = CharIndex * CharBytes;
+ unsigned Result = 0;
+ StringBytes = StringBytes + Offset;
+ for (unsigned I = 0; I < CharBytes; ++I) {
+ unsigned C = static_cast<unsigned>(StringBytes[I]);
+ Result |= C << (8 * I);
+ }
+ return Result;
+}
+
+FunctionSymbolNode *Demangler::demangleVcallThunkNode(StringView &MangledName) {
+ FunctionSymbolNode *FSN = Arena.alloc<FunctionSymbolNode>();
+ VcallThunkIdentifierNode *VTIN = Arena.alloc<VcallThunkIdentifierNode>();
+ FSN->Signature = Arena.alloc<ThunkSignatureNode>();
+ FSN->Signature->FunctionClass = FC_NoParameterList;
+
+ FSN->Name = demangleNameScopeChain(MangledName, VTIN);
+ if (!Error)
+ Error = !MangledName.consumeFront("$B");
+ if (!Error)
+ VTIN->OffsetInVTable = demangleUnsigned(MangledName);
+ if (!Error)
+ Error = !MangledName.consumeFront('A');
+ if (!Error)
+ FSN->Signature->CallConvention = demangleCallingConvention(MangledName);
+ return (Error) ? nullptr : FSN;
+}
+
+EncodedStringLiteralNode *
+Demangler::demangleStringLiteral(StringView &MangledName) {
+ // This function uses goto, so declare all variables up front.
+ OutputBuffer OB;
+ StringView CRC;
+ uint64_t StringByteSize;
+ bool IsWcharT = false;
+ bool IsNegative = false;
+ size_t CrcEndPos = 0;
+
+ EncodedStringLiteralNode *Result = Arena.alloc<EncodedStringLiteralNode>();
+
+ // Prefix indicating the beginning of a string literal
+ if (!MangledName.consumeFront("@_"))
+ goto StringLiteralError;
+ if (MangledName.empty())
+ goto StringLiteralError;
+
+ // Char Type (regular or wchar_t)
+ switch (MangledName.popFront()) {
+ case '1':
+ IsWcharT = true;
+ DEMANGLE_FALLTHROUGH;
+ case '0':
+ break;
+ default:
+ goto StringLiteralError;
+ }
+
+ // Encoded Length
+ std::tie(StringByteSize, IsNegative) = demangleNumber(MangledName);
+ if (Error || IsNegative || StringByteSize < (IsWcharT ? 2 : 1))
+ goto StringLiteralError;
+
+ // CRC 32 (always 8 characters plus a terminator)
+ CrcEndPos = MangledName.find('@');
+ if (CrcEndPos == StringView::npos)
+ goto StringLiteralError;
+ CRC = MangledName.substr(0, CrcEndPos);
+ MangledName = MangledName.dropFront(CrcEndPos + 1);
+ if (MangledName.empty())
+ goto StringLiteralError;
+
+ if (IsWcharT) {
+ Result->Char = CharKind::Wchar;
+ if (StringByteSize > 64)
+ Result->IsTruncated = true;
+
+ while (!MangledName.consumeFront('@')) {
+ if (MangledName.size() < 2)
+ goto StringLiteralError;
+ wchar_t W = demangleWcharLiteral(MangledName);
+ if (StringByteSize != 2 || Result->IsTruncated)
+ outputEscapedChar(OB, W);
+ StringByteSize -= 2;
+ if (Error)
+ goto StringLiteralError;
+ }
+ } else {
+ // The max byte length is actually 32, but some compilers mangled strings
+ // incorrectly, so we have to assume it can go higher.
+ constexpr unsigned MaxStringByteLength = 32 * 4;
+ uint8_t StringBytes[MaxStringByteLength];
+
+ unsigned BytesDecoded = 0;
+ while (!MangledName.consumeFront('@')) {
+ if (MangledName.size() < 1 || BytesDecoded >= MaxStringByteLength)
+ goto StringLiteralError;
+ StringBytes[BytesDecoded++] = demangleCharLiteral(MangledName);
+ }
+
+ if (StringByteSize > BytesDecoded)
+ Result->IsTruncated = true;
+
+ unsigned CharBytes =
+ guessCharByteSize(StringBytes, BytesDecoded, StringByteSize);
+ assert(StringByteSize % CharBytes == 0);
+ switch (CharBytes) {
+ case 1:
+ Result->Char = CharKind::Char;
+ break;
+ case 2:
+ Result->Char = CharKind::Char16;
+ break;
+ case 4:
+ Result->Char = CharKind::Char32;
+ break;
+ default:
+ DEMANGLE_UNREACHABLE;
+ }
+ const unsigned NumChars = BytesDecoded / CharBytes;
+ for (unsigned CharIndex = 0; CharIndex < NumChars; ++CharIndex) {
+ unsigned NextChar =
+ decodeMultiByteChar(StringBytes, CharIndex, CharBytes);
+ if (CharIndex + 1 < NumChars || Result->IsTruncated)
+ outputEscapedChar(OB, NextChar);
+ }
+ }
+
+ Result->DecodedString = copyString(OB);
+ std::free(OB.getBuffer());
+ return Result;
+
+StringLiteralError:
+ Error = true;
+ std::free(OB.getBuffer());
+ return nullptr;
+}
+
+// Returns MangledName's prefix before the first '@', or an error if
+// MangledName contains no '@' or the prefix has length 0.
+StringView Demangler::demangleSimpleString(StringView &MangledName,
+ bool Memorize) {
+ StringView S;
+ for (size_t i = 0; i < MangledName.size(); ++i) {
+ if (MangledName[i] != '@')
+ continue;
+ if (i == 0)
+ break;
+ S = MangledName.substr(0, i);
+ MangledName = MangledName.dropFront(i + 1);
+
+ if (Memorize)
+ memorizeString(S);
+ return S;
+ }
+
+ Error = true;
+ return {};
+}
+
+NamedIdentifierNode *
+Demangler::demangleAnonymousNamespaceName(StringView &MangledName) {
+ assert(MangledName.startsWith("?A"));
+ MangledName.consumeFront("?A");
+
+ NamedIdentifierNode *Node = Arena.alloc<NamedIdentifierNode>();
+ Node->Name = "`anonymous namespace'";
+ size_t EndPos = MangledName.find('@');
+ if (EndPos == StringView::npos) {
+ Error = true;
+ return nullptr;
+ }
+ StringView NamespaceKey = MangledName.substr(0, EndPos);
+ memorizeString(NamespaceKey);
+ MangledName = MangledName.substr(EndPos + 1);
+ return Node;
+}
+
+NamedIdentifierNode *
+Demangler::demangleLocallyScopedNamePiece(StringView &MangledName) {
+ assert(startsWithLocalScopePattern(MangledName));
+
+ NamedIdentifierNode *Identifier = Arena.alloc<NamedIdentifierNode>();
+ MangledName.consumeFront('?');
+ uint64_t Number = 0;
+ bool IsNegative = false;
+ std::tie(Number, IsNegative) = demangleNumber(MangledName);
+ assert(!IsNegative);
+
+ // One ? to terminate the number
+ MangledName.consumeFront('?');
+
+ assert(!Error);
+ Node *Scope = parse(MangledName);
+ if (Error)
+ return nullptr;
+
+ // Render the parent symbol's name into a buffer.
+ OutputBuffer OB;
+ OB << '`';
+ Scope->output(OB, OF_Default);
+ OB << '\'';
+ OB << "::`" << Number << "'";
+
+ Identifier->Name = copyString(OB);
+ std::free(OB.getBuffer());
+ return Identifier;
+}
+
+// Parses a type name in the form of A@B@C@@ which represents C::B::A.
+QualifiedNameNode *
+Demangler::demangleFullyQualifiedTypeName(StringView &MangledName) {
+ IdentifierNode *Identifier =
+ demangleUnqualifiedTypeName(MangledName, /*Memorize=*/true);
+ if (Error)
+ return nullptr;
+ assert(Identifier);
+
+ QualifiedNameNode *QN = demangleNameScopeChain(MangledName, Identifier);
+ if (Error)
+ return nullptr;
+ assert(QN);
+ return QN;
+}
+
+// Parses a symbol name in the form of A@B@C@@ which represents C::B::A.
+// Symbol names have slightly different rules regarding what can appear
+// so we separate out the implementations for flexibility.
+QualifiedNameNode *
+Demangler::demangleFullyQualifiedSymbolName(StringView &MangledName) {
+ // This is the final component of a symbol name (i.e. the leftmost component
+ // of a mangled name. Since the only possible template instantiation that
+ // can appear in this context is a function template, and since those are
+ // not saved for the purposes of name backreferences, only backref simple
+ // names.
+ IdentifierNode *Identifier =
+ demangleUnqualifiedSymbolName(MangledName, NBB_Simple);
+ if (Error)
+ return nullptr;
+
+ QualifiedNameNode *QN = demangleNameScopeChain(MangledName, Identifier);
+ if (Error)
+ return nullptr;
+
+ if (Identifier->kind() == NodeKind::StructorIdentifier) {
+ if (QN->Components->Count < 2) {
+ Error = true;
+ return nullptr;
+ }
+ StructorIdentifierNode *SIN =
+ static_cast<StructorIdentifierNode *>(Identifier);
+ Node *ClassNode = QN->Components->Nodes[QN->Components->Count - 2];
+ SIN->Class = static_cast<IdentifierNode *>(ClassNode);
+ }
+ assert(QN);
+ return QN;
+}
+
+IdentifierNode *Demangler::demangleUnqualifiedTypeName(StringView &MangledName,
+ bool Memorize) {
+ // An inner-most name can be a back-reference, because a fully-qualified name
+ // (e.g. Scope + Inner) can contain other fully qualified names inside of
+ // them (for example template parameters), and these nested parameters can
+ // refer to previously mangled types.
+ if (startsWithDigit(MangledName))
+ return demangleBackRefName(MangledName);
+
+ if (MangledName.startsWith("?$"))
+ return demangleTemplateInstantiationName(MangledName, NBB_Template);
+
+ return demangleSimpleName(MangledName, Memorize);
+}
+
+IdentifierNode *
+Demangler::demangleUnqualifiedSymbolName(StringView &MangledName,
+ NameBackrefBehavior NBB) {
+ if (startsWithDigit(MangledName))
+ return demangleBackRefName(MangledName);
+ if (MangledName.startsWith("?$"))
+ return demangleTemplateInstantiationName(MangledName, NBB);
+ if (MangledName.startsWith('?'))
+ return demangleFunctionIdentifierCode(MangledName);
+ return demangleSimpleName(MangledName, /*Memorize=*/(NBB & NBB_Simple) != 0);
+}
+
+IdentifierNode *Demangler::demangleNameScopePiece(StringView &MangledName) {
+ if (startsWithDigit(MangledName))
+ return demangleBackRefName(MangledName);
+
+ if (MangledName.startsWith("?$"))
+ return demangleTemplateInstantiationName(MangledName, NBB_Template);
+
+ if (MangledName.startsWith("?A"))
+ return demangleAnonymousNamespaceName(MangledName);
+
+ if (startsWithLocalScopePattern(MangledName))
+ return demangleLocallyScopedNamePiece(MangledName);
+
+ return demangleSimpleName(MangledName, /*Memorize=*/true);
+}
+
+static NodeArrayNode *nodeListToNodeArray(ArenaAllocator &Arena, NodeList *Head,
+ size_t Count) {
+ NodeArrayNode *N = Arena.alloc<NodeArrayNode>();
+ N->Count = Count;
+ N->Nodes = Arena.allocArray<Node *>(Count);
+ for (size_t I = 0; I < Count; ++I) {
+ N->Nodes[I] = Head->N;
+ Head = Head->Next;
+ }
+ return N;
+}
+
+QualifiedNameNode *
+Demangler::demangleNameScopeChain(StringView &MangledName,
+ IdentifierNode *UnqualifiedName) {
+ NodeList *Head = Arena.alloc<NodeList>();
+
+ Head->N = UnqualifiedName;
+
+ size_t Count = 1;
+ while (!MangledName.consumeFront("@")) {
+ ++Count;
+ NodeList *NewHead = Arena.alloc<NodeList>();
+ NewHead->Next = Head;
+ Head = NewHead;
+
+ if (MangledName.empty()) {
+ Error = true;
+ return nullptr;
+ }
+
+ assert(!Error);
+ IdentifierNode *Elem = demangleNameScopePiece(MangledName);
+ if (Error)
+ return nullptr;
+
+ Head->N = Elem;
+ }
+
+ QualifiedNameNode *QN = Arena.alloc<QualifiedNameNode>();
+ QN->Components = nodeListToNodeArray(Arena, Head, Count);
+ return QN;
+}
+
+FuncClass Demangler::demangleFunctionClass(StringView &MangledName) {
+ switch (MangledName.popFront()) {
+ case '9':
+ return FuncClass(FC_ExternC | FC_NoParameterList);
+ case 'A':
+ return FC_Private;
+ case 'B':
+ return FuncClass(FC_Private | FC_Far);
+ case 'C':
+ return FuncClass(FC_Private | FC_Static);
+ case 'D':
+ return FuncClass(FC_Private | FC_Static | FC_Far);
+ case 'E':
+ return FuncClass(FC_Private | FC_Virtual);
+ case 'F':
+ return FuncClass(FC_Private | FC_Virtual | FC_Far);
+ case 'G':
+ return FuncClass(FC_Private | FC_StaticThisAdjust);
+ case 'H':
+ return FuncClass(FC_Private | FC_StaticThisAdjust | FC_Far);
+ case 'I':
+ return FuncClass(FC_Protected);
+ case 'J':
+ return FuncClass(FC_Protected | FC_Far);
+ case 'K':
+ return FuncClass(FC_Protected | FC_Static);
+ case 'L':
+ return FuncClass(FC_Protected | FC_Static | FC_Far);
+ case 'M':
+ return FuncClass(FC_Protected | FC_Virtual);
+ case 'N':
+ return FuncClass(FC_Protected | FC_Virtual | FC_Far);
+ case 'O':
+ return FuncClass(FC_Protected | FC_Virtual | FC_StaticThisAdjust);
+ case 'P':
+ return FuncClass(FC_Protected | FC_Virtual | FC_StaticThisAdjust | FC_Far);
+ case 'Q':
+ return FuncClass(FC_Public);
+ case 'R':
+ return FuncClass(FC_Public | FC_Far);
+ case 'S':
+ return FuncClass(FC_Public | FC_Static);
+ case 'T':
+ return FuncClass(FC_Public | FC_Static | FC_Far);
+ case 'U':
+ return FuncClass(FC_Public | FC_Virtual);
+ case 'V':
+ return FuncClass(FC_Public | FC_Virtual | FC_Far);
+ case 'W':
+ return FuncClass(FC_Public | FC_Virtual | FC_StaticThisAdjust);
+ case 'X':
+ return FuncClass(FC_Public | FC_Virtual | FC_StaticThisAdjust | FC_Far);
+ case 'Y':
+ return FuncClass(FC_Global);
+ case 'Z':
+ return FuncClass(FC_Global | FC_Far);
+ case '$': {
+ FuncClass VFlag = FC_VirtualThisAdjust;
+ if (MangledName.consumeFront('R'))
+ VFlag = FuncClass(VFlag | FC_VirtualThisAdjustEx);
+ if (MangledName.empty())
+ break;
+ switch (MangledName.popFront()) {
+ case '0':
+ return FuncClass(FC_Private | FC_Virtual | VFlag);
+ case '1':
+ return FuncClass(FC_Private | FC_Virtual | VFlag | FC_Far);
+ case '2':
+ return FuncClass(FC_Protected | FC_Virtual | VFlag);
+ case '3':
+ return FuncClass(FC_Protected | FC_Virtual | VFlag | FC_Far);
+ case '4':
+ return FuncClass(FC_Public | FC_Virtual | VFlag);
+ case '5':
+ return FuncClass(FC_Public | FC_Virtual | VFlag | FC_Far);
+ }
+ }
+ }
+
+ Error = true;
+ return FC_Public;
+}
+
+CallingConv Demangler::demangleCallingConvention(StringView &MangledName) {
+ if (MangledName.empty()) {
+ Error = true;
+ return CallingConv::None;
+ }
+
+ switch (MangledName.popFront()) {
+ case 'A':
+ case 'B':
+ return CallingConv::Cdecl;
+ case 'C':
+ case 'D':
+ return CallingConv::Pascal;
+ case 'E':
+ case 'F':
+ return CallingConv::Thiscall;
+ case 'G':
+ case 'H':
+ return CallingConv::Stdcall;
+ case 'I':
+ case 'J':
+ return CallingConv::Fastcall;
+ case 'M':
+ case 'N':
+ return CallingConv::Clrcall;
+ case 'O':
+ case 'P':
+ return CallingConv::Eabi;
+ case 'Q':
+ return CallingConv::Vectorcall;
+ case 'S':
+ return CallingConv::Swift;
+ case 'W':
+ return CallingConv::SwiftAsync;
+ }
+
+ return CallingConv::None;
+}
+
+StorageClass Demangler::demangleVariableStorageClass(StringView &MangledName) {
+ assert(MangledName.front() >= '0' && MangledName.front() <= '4');
+
+ switch (MangledName.popFront()) {
+ case '0':
+ return StorageClass::PrivateStatic;
+ case '1':
+ return StorageClass::ProtectedStatic;
+ case '2':
+ return StorageClass::PublicStatic;
+ case '3':
+ return StorageClass::Global;
+ case '4':
+ return StorageClass::FunctionLocalStatic;
+ }
+ DEMANGLE_UNREACHABLE;
+}
+
+std::pair<Qualifiers, bool>
+Demangler::demangleQualifiers(StringView &MangledName) {
+ if (MangledName.empty()) {
+ Error = true;
+ return std::make_pair(Q_None, false);
+ }
+
+ switch (MangledName.popFront()) {
+ // Member qualifiers
+ case 'Q':
+ return std::make_pair(Q_None, true);
+ case 'R':
+ return std::make_pair(Q_Const, true);
+ case 'S':
+ return std::make_pair(Q_Volatile, true);
+ case 'T':
+ return std::make_pair(Qualifiers(Q_Const | Q_Volatile), true);
+ // Non-Member qualifiers
+ case 'A':
+ return std::make_pair(Q_None, false);
+ case 'B':
+ return std::make_pair(Q_Const, false);
+ case 'C':
+ return std::make_pair(Q_Volatile, false);
+ case 'D':
+ return std::make_pair(Qualifiers(Q_Const | Q_Volatile), false);
+ }
+ Error = true;
+ return std::make_pair(Q_None, false);
+}
+
+// <variable-type> ::= <type> <cvr-qualifiers>
+// ::= <type> <pointee-cvr-qualifiers> # pointers, references
+TypeNode *Demangler::demangleType(StringView &MangledName,
+ QualifierMangleMode QMM) {
+ Qualifiers Quals = Q_None;
+ bool IsMember = false;
+ if (QMM == QualifierMangleMode::Mangle) {
+ std::tie(Quals, IsMember) = demangleQualifiers(MangledName);
+ } else if (QMM == QualifierMangleMode::Result) {
+ if (MangledName.consumeFront('?'))
+ std::tie(Quals, IsMember) = demangleQualifiers(MangledName);
+ }
+
+ if (MangledName.empty()) {
+ Error = true;
+ return nullptr;
+ }
+
+ TypeNode *Ty = nullptr;
+ if (isTagType(MangledName))
+ Ty = demangleClassType(MangledName);
+ else if (isPointerType(MangledName)) {
+ if (isMemberPointer(MangledName, Error))
+ Ty = demangleMemberPointerType(MangledName);
+ else if (!Error)
+ Ty = demanglePointerType(MangledName);
+ else
+ return nullptr;
+ } else if (isArrayType(MangledName))
+ Ty = demangleArrayType(MangledName);
+ else if (isFunctionType(MangledName)) {
+ if (MangledName.consumeFront("$$A8@@"))
+ Ty = demangleFunctionType(MangledName, true);
+ else {
+ assert(MangledName.startsWith("$$A6"));
+ MangledName.consumeFront("$$A6");
+ Ty = demangleFunctionType(MangledName, false);
+ }
+ } else if (isCustomType(MangledName)) {
+ Ty = demangleCustomType(MangledName);
+ } else {
+ Ty = demanglePrimitiveType(MangledName);
+ }
+
+ if (!Ty || Error)
+ return Ty;
+ Ty->Quals = Qualifiers(Ty->Quals | Quals);
+ return Ty;
+}
+
+bool Demangler::demangleThrowSpecification(StringView &MangledName) {
+ if (MangledName.consumeFront("_E"))
+ return true;
+ if (MangledName.consumeFront('Z'))
+ return false;
+
+ Error = true;
+ return false;
+}
+
+FunctionSignatureNode *Demangler::demangleFunctionType(StringView &MangledName,
+ bool HasThisQuals) {
+ FunctionSignatureNode *FTy = Arena.alloc<FunctionSignatureNode>();
+
+ if (HasThisQuals) {
+ FTy->Quals = demanglePointerExtQualifiers(MangledName);
+ FTy->RefQualifier = demangleFunctionRefQualifier(MangledName);
+ FTy->Quals = Qualifiers(FTy->Quals | demangleQualifiers(MangledName).first);
+ }
+
+ // Fields that appear on both member and non-member functions.
+ FTy->CallConvention = demangleCallingConvention(MangledName);
+
+ // <return-type> ::= <type>
+ // ::= @ # structors (they have no declared return type)
+ bool IsStructor = MangledName.consumeFront('@');
+ if (!IsStructor)
+ FTy->ReturnType = demangleType(MangledName, QualifierMangleMode::Result);
+
+ FTy->Params = demangleFunctionParameterList(MangledName, FTy->IsVariadic);
+
+ FTy->IsNoexcept = demangleThrowSpecification(MangledName);
+
+ return FTy;
+}
+
+FunctionSymbolNode *
+Demangler::demangleFunctionEncoding(StringView &MangledName) {
+ FuncClass ExtraFlags = FC_None;
+ if (MangledName.consumeFront("$$J0"))
+ ExtraFlags = FC_ExternC;
+
+ if (MangledName.empty()) {
+ Error = true;
+ return nullptr;
+ }
+
+ FuncClass FC = demangleFunctionClass(MangledName);
+ FC = FuncClass(ExtraFlags | FC);
+
+ FunctionSignatureNode *FSN = nullptr;
+ ThunkSignatureNode *TTN = nullptr;
+ if (FC & FC_StaticThisAdjust) {
+ TTN = Arena.alloc<ThunkSignatureNode>();
+ TTN->ThisAdjust.StaticOffset = demangleSigned(MangledName);
+ } else if (FC & FC_VirtualThisAdjust) {
+ TTN = Arena.alloc<ThunkSignatureNode>();
+ if (FC & FC_VirtualThisAdjustEx) {
+ TTN->ThisAdjust.VBPtrOffset = demangleSigned(MangledName);
+ TTN->ThisAdjust.VBOffsetOffset = demangleSigned(MangledName);
+ }
+ TTN->ThisAdjust.VtordispOffset = demangleSigned(MangledName);
+ TTN->ThisAdjust.StaticOffset = demangleSigned(MangledName);
+ }
+
+ if (FC & FC_NoParameterList) {
+ // This is an extern "C" function whose full signature hasn't been mangled.
+ // This happens when we need to mangle a local symbol inside of an extern
+ // "C" function.
+ FSN = Arena.alloc<FunctionSignatureNode>();
+ } else {
+ bool HasThisQuals = !(FC & (FC_Global | FC_Static));
+ FSN = demangleFunctionType(MangledName, HasThisQuals);
+ }
+
+ if (Error)
+ return nullptr;
+
+ if (TTN) {
+ *static_cast<FunctionSignatureNode *>(TTN) = *FSN;
+ FSN = TTN;
+ }
+ FSN->FunctionClass = FC;
+
+ FunctionSymbolNode *Symbol = Arena.alloc<FunctionSymbolNode>();
+ Symbol->Signature = FSN;
+ return Symbol;
+}
+
+CustomTypeNode *Demangler::demangleCustomType(StringView &MangledName) {
+ assert(MangledName.startsWith('?'));
+ MangledName.popFront();
+
+ CustomTypeNode *CTN = Arena.alloc<CustomTypeNode>();
+ CTN->Identifier = demangleUnqualifiedTypeName(MangledName, /*Memorize=*/true);
+ if (!MangledName.consumeFront('@'))
+ Error = true;
+ if (Error)
+ return nullptr;
+ return CTN;
+}
+
+// Reads a primitive type.
+PrimitiveTypeNode *Demangler::demanglePrimitiveType(StringView &MangledName) {
+ if (MangledName.consumeFront("$$T"))
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Nullptr);
+
+ switch (MangledName.popFront()) {
+ case 'X':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Void);
+ case 'D':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char);
+ case 'C':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Schar);
+ case 'E':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uchar);
+ case 'F':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Short);
+ case 'G':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ushort);
+ case 'H':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Int);
+ case 'I':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uint);
+ case 'J':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Long);
+ case 'K':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ulong);
+ case 'M':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Float);
+ case 'N':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Double);
+ case 'O':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ldouble);
+ case '_': {
+ if (MangledName.empty()) {
+ Error = true;
+ return nullptr;
+ }
+ switch (MangledName.popFront()) {
+ case 'N':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Bool);
+ case 'J':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Int64);
+ case 'K':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uint64);
+ case 'W':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Wchar);
+ case 'Q':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char8);
+ case 'S':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char16);
+ case 'U':
+ return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char32);
+ }
+ break;
+ }
+ }
+ Error = true;
+ return nullptr;
+}
+
+TagTypeNode *Demangler::demangleClassType(StringView &MangledName) {
+ TagTypeNode *TT = nullptr;
+
+ switch (MangledName.popFront()) {
+ case 'T':
+ TT = Arena.alloc<TagTypeNode>(TagKind::Union);
+ break;
+ case 'U':
+ TT = Arena.alloc<TagTypeNode>(TagKind::Struct);
+ break;
+ case 'V':
+ TT = Arena.alloc<TagTypeNode>(TagKind::Class);
+ break;
+ case 'W':
+ if (!MangledName.consumeFront('4')) {
+ Error = true;
+ return nullptr;
+ }
+ TT = Arena.alloc<TagTypeNode>(TagKind::Enum);
+ break;
+ default:
+ assert(false);
+ }
+
+ TT->QualifiedName = demangleFullyQualifiedTypeName(MangledName);
+ return TT;
+}
+
+// <pointer-type> ::= E? <pointer-cvr-qualifiers> <ext-qualifiers> <type>
+// # the E is required for 64-bit non-static pointers
+PointerTypeNode *Demangler::demanglePointerType(StringView &MangledName) {
+ PointerTypeNode *Pointer = Arena.alloc<PointerTypeNode>();
+
+ std::tie(Pointer->Quals, Pointer->Affinity) =
+ demanglePointerCVQualifiers(MangledName);
+
+ if (MangledName.consumeFront("6")) {
+ Pointer->Pointee = demangleFunctionType(MangledName, false);
+ return Pointer;
+ }
+
+ Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName);
+ Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals);
+
+ Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Mangle);
+ return Pointer;
+}
+
+PointerTypeNode *Demangler::demangleMemberPointerType(StringView &MangledName) {
+ PointerTypeNode *Pointer = Arena.alloc<PointerTypeNode>();
+
+ std::tie(Pointer->Quals, Pointer->Affinity) =
+ demanglePointerCVQualifiers(MangledName);
+ assert(Pointer->Affinity == PointerAffinity::Pointer);
+
+ Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName);
+ Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals);
+
+ // isMemberPointer() only returns true if there is at least one character
+ // after the qualifiers.
+ if (MangledName.consumeFront("8")) {
+ Pointer->ClassParent = demangleFullyQualifiedTypeName(MangledName);
+ Pointer->Pointee = demangleFunctionType(MangledName, true);
+ } else {
+ Qualifiers PointeeQuals = Q_None;
+ bool IsMember = false;
+ std::tie(PointeeQuals, IsMember) = demangleQualifiers(MangledName);
+ assert(IsMember || Error);
+ Pointer->ClassParent = demangleFullyQualifiedTypeName(MangledName);
+
+ Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Drop);
+ if (Pointer->Pointee)
+ Pointer->Pointee->Quals = PointeeQuals;
+ }
+
+ return Pointer;
+}
+
+Qualifiers Demangler::demanglePointerExtQualifiers(StringView &MangledName) {
+ Qualifiers Quals = Q_None;
+ if (MangledName.consumeFront('E'))
+ Quals = Qualifiers(Quals | Q_Pointer64);
+ if (MangledName.consumeFront('I'))
+ Quals = Qualifiers(Quals | Q_Restrict);
+ if (MangledName.consumeFront('F'))
+ Quals = Qualifiers(Quals | Q_Unaligned);
+
+ return Quals;
+}
+
+ArrayTypeNode *Demangler::demangleArrayType(StringView &MangledName) {
+ assert(MangledName.front() == 'Y');
+ MangledName.popFront();
+
+ uint64_t Rank = 0;
+ bool IsNegative = false;
+ std::tie(Rank, IsNegative) = demangleNumber(MangledName);
+ if (IsNegative || Rank == 0) {
+ Error = true;
+ return nullptr;
+ }
+
+ ArrayTypeNode *ATy = Arena.alloc<ArrayTypeNode>();
+ NodeList *Head = Arena.alloc<NodeList>();
+ NodeList *Tail = Head;
+
+ for (uint64_t I = 0; I < Rank; ++I) {
+ uint64_t D = 0;
+ std::tie(D, IsNegative) = demangleNumber(MangledName);
+ if (Error || IsNegative) {
+ Error = true;
+ return nullptr;
+ }
+ Tail->N = Arena.alloc<IntegerLiteralNode>(D, IsNegative);
+ if (I + 1 < Rank) {
+ Tail->Next = Arena.alloc<NodeList>();
+ Tail = Tail->Next;
+ }
+ }
+ ATy->Dimensions = nodeListToNodeArray(Arena, Head, Rank);
+
+ if (MangledName.consumeFront("$$C")) {
+ bool IsMember = false;
+ std::tie(ATy->Quals, IsMember) = demangleQualifiers(MangledName);
+ if (IsMember) {
+ Error = true;
+ return nullptr;
+ }
+ }
+
+ ATy->ElementType = demangleType(MangledName, QualifierMangleMode::Drop);
+ return ATy;
+}
+
+// Reads a function's parameters.
+NodeArrayNode *Demangler::demangleFunctionParameterList(StringView &MangledName,
+ bool &IsVariadic) {
+ // Empty parameter list.
+ if (MangledName.consumeFront('X'))
+ return nullptr;
+
+ NodeList *Head = Arena.alloc<NodeList>();
+ NodeList **Current = &Head;
+ size_t Count = 0;
+ while (!Error && !MangledName.startsWith('@') &&
+ !MangledName.startsWith('Z')) {
+ ++Count;
+
+ if (startsWithDigit(MangledName)) {
+ size_t N = MangledName[0] - '0';
+ if (N >= Backrefs.FunctionParamCount) {
+ Error = true;
+ return nullptr;
+ }
+ MangledName = MangledName.dropFront();
+
+ *Current = Arena.alloc<NodeList>();
+ (*Current)->N = Backrefs.FunctionParams[N];
+ Current = &(*Current)->Next;
+ continue;
+ }
+
+ size_t OldSize = MangledName.size();
+
+ *Current = Arena.alloc<NodeList>();
+ TypeNode *TN = demangleType(MangledName, QualifierMangleMode::Drop);
+ if (!TN || Error)
+ return nullptr;
+
+ (*Current)->N = TN;
+
+ size_t CharsConsumed = OldSize - MangledName.size();
+ assert(CharsConsumed != 0);
+
+ // Single-letter types are ignored for backreferences because memorizing
+ // them doesn't save anything.
+ if (Backrefs.FunctionParamCount <= 9 && CharsConsumed > 1)
+ Backrefs.FunctionParams[Backrefs.FunctionParamCount++] = TN;
+
+ Current = &(*Current)->Next;
+ }
+
+ if (Error)
+ return nullptr;
+
+ NodeArrayNode *NA = nodeListToNodeArray(Arena, Head, Count);
+ // A non-empty parameter list is terminated by either 'Z' (variadic) parameter
+ // list or '@' (non variadic). Careful not to consume "@Z", as in that case
+ // the following Z could be a throw specifier.
+ if (MangledName.consumeFront('@'))
+ return NA;
+
+ if (MangledName.consumeFront('Z')) {
+ IsVariadic = true;
+ return NA;
+ }
+
+ DEMANGLE_UNREACHABLE;
+}
+
+NodeArrayNode *
+Demangler::demangleTemplateParameterList(StringView &MangledName) {
+ NodeList *Head = nullptr;
+ NodeList **Current = &Head;
+ size_t Count = 0;
+
+ while (!MangledName.startsWith('@')) {
+ if (MangledName.consumeFront("$S") || MangledName.consumeFront("$$V") ||
+ MangledName.consumeFront("$$$V") || MangledName.consumeFront("$$Z")) {
+ // parameter pack separator
+ continue;
+ }
+
+ ++Count;
+
+ // Template parameter lists don't participate in back-referencing.
+ *Current = Arena.alloc<NodeList>();
+
+ NodeList &TP = **Current;
+
+ TemplateParameterReferenceNode *TPRN = nullptr;
+ if (MangledName.consumeFront("$$Y")) {
+ // Template alias
+ TP.N = demangleFullyQualifiedTypeName(MangledName);
+ } else if (MangledName.consumeFront("$$B")) {
+ // Array
+ TP.N = demangleType(MangledName, QualifierMangleMode::Drop);
+ } else if (MangledName.consumeFront("$$C")) {
+ // Type has qualifiers.
+ TP.N = demangleType(MangledName, QualifierMangleMode::Mangle);
+ } else if (MangledName.startsWith("$1") || MangledName.startsWith("$H") ||
+ MangledName.startsWith("$I") || MangledName.startsWith("$J")) {
+ // Pointer to member
+ TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();
+ TPRN->IsMemberPointer = true;
+
+ MangledName = MangledName.dropFront();
+ // 1 - single inheritance <name>
+ // H - multiple inheritance <name> <number>
+ // I - virtual inheritance <name> <number> <number>
+ // J - unspecified inheritance <name> <number> <number> <number>
+ char InheritanceSpecifier = MangledName.popFront();
+ SymbolNode *S = nullptr;
+ if (MangledName.startsWith('?')) {
+ S = parse(MangledName);
+ if (Error || !S->Name) {
+ Error = true;
+ return nullptr;
+ }
+ memorizeIdentifier(S->Name->getUnqualifiedIdentifier());
+ }
+
+ switch (InheritanceSpecifier) {
+ case 'J':
+ TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
+ demangleSigned(MangledName);
+ DEMANGLE_FALLTHROUGH;
+ case 'I':
+ TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
+ demangleSigned(MangledName);
+ DEMANGLE_FALLTHROUGH;
+ case 'H':
+ TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
+ demangleSigned(MangledName);
+ DEMANGLE_FALLTHROUGH;
+ case '1':
+ break;
+ default:
+ DEMANGLE_UNREACHABLE;
+ }
+ TPRN->Affinity = PointerAffinity::Pointer;
+ TPRN->Symbol = S;
+ } else if (MangledName.startsWith("$E?")) {
+ MangledName.consumeFront("$E");
+ // Reference to symbol
+ TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();
+ TPRN->Symbol = parse(MangledName);
+ TPRN->Affinity = PointerAffinity::Reference;
+ } else if (MangledName.startsWith("$F") || MangledName.startsWith("$G")) {
+ TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();
+
+ // Data member pointer.
+ MangledName = MangledName.dropFront();
+ char InheritanceSpecifier = MangledName.popFront();
+
+ switch (InheritanceSpecifier) {
+ case 'G':
+ TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
+ demangleSigned(MangledName);
+ DEMANGLE_FALLTHROUGH;
+ case 'F':
+ TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
+ demangleSigned(MangledName);
+ TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
+ demangleSigned(MangledName);
+ break;
+ default:
+ DEMANGLE_UNREACHABLE;
+ }
+ TPRN->IsMemberPointer = true;
+
+ } else if (MangledName.consumeFront("$0")) {
+ // Integral non-type template parameter
+ bool IsNegative = false;
+ uint64_t Value = 0;
+ std::tie(Value, IsNegative) = demangleNumber(MangledName);
+
+ TP.N = Arena.alloc<IntegerLiteralNode>(Value, IsNegative);
+ } else {
+ TP.N = demangleType(MangledName, QualifierMangleMode::Drop);
+ }
+ if (Error)
+ return nullptr;
+
+ Current = &TP.Next;
+ }
+
+ // The loop above returns nullptr on Error.
+ assert(!Error);
+
+ // Template parameter lists cannot be variadic, so it can only be terminated
+ // by @ (as opposed to 'Z' in the function parameter case).
+ assert(MangledName.startsWith('@')); // The above loop exits only on '@'.
+ MangledName.consumeFront('@');
+ return nodeListToNodeArray(Arena, Head, Count);
+}
+
+void Demangler::dumpBackReferences() {
+ std::printf("%d function parameter backreferences\n",
+ (int)Backrefs.FunctionParamCount);
+
+ // Create an output stream so we can render each type.
+ OutputBuffer OB;
+ for (size_t I = 0; I < Backrefs.FunctionParamCount; ++I) {
+ OB.setCurrentPosition(0);
+
+ TypeNode *T = Backrefs.FunctionParams[I];
+ T->output(OB, OF_Default);
+
+ StringView B = OB;
+ std::printf(" [%d] - %.*s\n", (int)I, (int)B.size(), B.begin());
+ }
+ std::free(OB.getBuffer());
+
+ if (Backrefs.FunctionParamCount > 0)
+ std::printf("\n");
+ std::printf("%d name backreferences\n", (int)Backrefs.NamesCount);
+ for (size_t I = 0; I < Backrefs.NamesCount; ++I) {
+ std::printf(" [%d] - %.*s\n", (int)I, (int)Backrefs.Names[I]->Name.size(),
+ Backrefs.Names[I]->Name.begin());
+ }
+ if (Backrefs.NamesCount > 0)
+ std::printf("\n");
+}
+
+char *llvm::microsoftDemangle(const char *MangledName, size_t *NMangled,
+ char *Buf, size_t *N,
+ int *Status, MSDemangleFlags Flags) {
+ Demangler D;
+
+ StringView Name{MangledName};
+ SymbolNode *AST = D.parse(Name);
+ if (!D.Error && NMangled)
+ *NMangled = Name.begin() - MangledName;
+
+ if (Flags & MSDF_DumpBackrefs)
+ D.dumpBackReferences();
+
+ OutputFlags OF = OF_Default;
+ if (Flags & MSDF_NoCallingConvention)
+ OF = OutputFlags(OF | OF_NoCallingConvention);
+ if (Flags & MSDF_NoAccessSpecifier)
+ OF = OutputFlags(OF | OF_NoAccessSpecifier);
+ if (Flags & MSDF_NoReturnType)
+ OF = OutputFlags(OF | OF_NoReturnType);
+ if (Flags & MSDF_NoMemberType)
+ OF = OutputFlags(OF | OF_NoMemberType);
+ if (Flags & MSDF_NoVariableType)
+ OF = OutputFlags(OF | OF_NoVariableType);
+
+ int InternalStatus = demangle_success;
+ if (D.Error)
+ InternalStatus = demangle_invalid_mangled_name;
+ else {
+ OutputBuffer OB(Buf, N);
+ AST->output(OB, OF);
+ OB += '\0';
+ if (N != nullptr)
+ *N = OB.getCurrentPosition();
+ Buf = OB.getBuffer();
+ }
+
+ if (Status)
+ *Status = InternalStatus;
+ return InternalStatus == demangle_success ? Buf : nullptr;
+}
diff --git a/demangle/third_party/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp b/demangle/third_party/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp
new file mode 100644
index 00000000..975649f2
--- /dev/null
+++ b/demangle/third_party/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp
@@ -0,0 +1,658 @@
+//===- MicrosoftDemangle.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a demangler for MSVC-style mangled symbols.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Demangle/MicrosoftDemangleNodes.h"
+#include "llvm/Demangle/Utility.h"
+#include <cctype>
+#include <string>
+
+using namespace llvm;
+using namespace ms_demangle;
+
+#define OUTPUT_ENUM_CLASS_VALUE(Enum, Value, Desc) \
+ case Enum::Value: \
+ OB << Desc; \
+ break;
+
+// Writes a space if the last token does not end with a punctuation.
+static void outputSpaceIfNecessary(OutputBuffer &OB) {
+ if (OB.empty())
+ return;
+
+ char C = OB.back();
+ if (std::isalnum(C) || C == '>')
+ OB << " ";
+}
+
+static void outputSingleQualifier(OutputBuffer &OB, Qualifiers Q) {
+ switch (Q) {
+ case Q_Const:
+ OB << "const";
+ break;
+ case Q_Volatile:
+ OB << "volatile";
+ break;
+ case Q_Restrict:
+ OB << "__restrict";
+ break;
+ default:
+ break;
+ }
+}
+
+static bool outputQualifierIfPresent(OutputBuffer &OB, Qualifiers Q,
+ Qualifiers Mask, bool NeedSpace) {
+ if (!(Q & Mask))
+ return NeedSpace;
+
+ if (NeedSpace)
+ OB << " ";
+
+ outputSingleQualifier(OB, Mask);
+ return true;
+}
+
+static void outputQualifiers(OutputBuffer &OB, Qualifiers Q, bool SpaceBefore,
+ bool SpaceAfter) {
+ if (Q == Q_None)
+ return;
+
+ size_t Pos1 = OB.getCurrentPosition();
+ SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Const, SpaceBefore);
+ SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Volatile, SpaceBefore);
+ SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Restrict, SpaceBefore);
+ size_t Pos2 = OB.getCurrentPosition();
+ if (SpaceAfter && Pos2 > Pos1)
+ OB << " ";
+}
+
+static void outputCallingConvention(OutputBuffer &OB, CallingConv CC) {
+ outputSpaceIfNecessary(OB);
+
+ switch (CC) {
+ case CallingConv::Cdecl:
+ OB << "__cdecl";
+ break;
+ case CallingConv::Fastcall:
+ OB << "__fastcall";
+ break;
+ case CallingConv::Pascal:
+ OB << "__pascal";
+ break;
+ case CallingConv::Regcall:
+ OB << "__regcall";
+ break;
+ case CallingConv::Stdcall:
+ OB << "__stdcall";
+ break;
+ case CallingConv::Thiscall:
+ OB << "__thiscall";
+ break;
+ case CallingConv::Eabi:
+ OB << "__eabi";
+ break;
+ case CallingConv::Vectorcall:
+ OB << "__vectorcall";
+ break;
+ case CallingConv::Clrcall:
+ OB << "__clrcall";
+ break;
+ case CallingConv::Swift:
+ OB << "__attribute__((__swiftcall__)) ";
+ break;
+ case CallingConv::SwiftAsync:
+ OB << "__attribute__((__swiftasynccall__)) ";
+ break;
+ default:
+ break;
+ }
+}
+
+std::string Node::toString(OutputFlags Flags) const {
+ OutputBuffer OB;
+ this->output(OB, Flags);
+ StringView SV = OB;
+ std::string Owned(SV.begin(), SV.end());
+ std::free(OB.getBuffer());
+ return Owned;
+}
+
+void PrimitiveTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
+ switch (PrimKind) {
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Void, "void");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Bool, "bool");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char, "char");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Schar, "signed char");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uchar, "unsigned char");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char8, "char8_t");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char16, "char16_t");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char32, "char32_t");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Short, "short");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ushort, "unsigned short");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Int, "int");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uint, "unsigned int");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Long, "long");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ulong, "unsigned long");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Int64, "__int64");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uint64, "unsigned __int64");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Wchar, "wchar_t");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Float, "float");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Double, "double");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ldouble, "long double");
+ OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Nullptr, "std::nullptr_t");
+ }
+ outputQualifiers(OB, Quals, true, false);
+}
+
+void NodeArrayNode::output(OutputBuffer &OB, OutputFlags Flags) const {
+ output(OB, Flags, ", ");
+}
+
+void NodeArrayNode::output(OutputBuffer &OB, OutputFlags Flags,
+ StringView Separator) const {
+ if (Count == 0)
+ return;
+ if (Nodes[0])
+ Nodes[0]->output(OB, Flags);
+ for (size_t I = 1; I < Count; ++I) {
+ OB << Separator;
+ Nodes[I]->output(OB, Flags);
+ }
+}
+
+void EncodedStringLiteralNode::output(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ switch (Char) {
+ case CharKind::Wchar:
+ OB << "L\"";
+ break;
+ case CharKind::Char:
+ OB << "\"";
+ break;
+ case CharKind::Char16:
+ OB << "u\"";
+ break;
+ case CharKind::Char32:
+ OB << "U\"";
+ break;
+ }
+ OB << DecodedString << "\"";
+ if (IsTruncated)
+ OB << "...";
+}
+
+void IntegerLiteralNode::output(OutputBuffer &OB, OutputFlags Flags) const {
+ if (IsNegative)
+ OB << '-';
+ OB << Value;
+}
+
+void TemplateParameterReferenceNode::output(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ if (ThunkOffsetCount > 0)
+ OB << "{";
+ else if (Affinity == PointerAffinity::Pointer)
+ OB << "&";
+
+ if (Symbol) {
+ Symbol->output(OB, Flags);
+ if (ThunkOffsetCount > 0)
+ OB << ", ";
+ }
+
+ if (ThunkOffsetCount > 0)
+ OB << ThunkOffsets[0];
+ for (int I = 1; I < ThunkOffsetCount; ++I) {
+ OB << ", " << ThunkOffsets[I];
+ }
+ if (ThunkOffsetCount > 0)
+ OB << "}";
+}
+
+void IdentifierNode::outputTemplateParameters(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ if (!TemplateParams)
+ return;
+ OB << "<";
+ TemplateParams->output(OB, Flags);
+ OB << ">";
+}
+
+void DynamicStructorIdentifierNode::output(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ if (IsDestructor)
+ OB << "`dynamic atexit destructor for ";
+ else
+ OB << "`dynamic initializer for ";
+
+ if (Variable) {
+ OB << "`";
+ Variable->output(OB, Flags);
+ OB << "''";
+ } else {
+ OB << "'";
+ Name->output(OB, Flags);
+ OB << "''";
+ }
+}
+
+void NamedIdentifierNode::output(OutputBuffer &OB, OutputFlags Flags) const {
+ OB << Name;
+ outputTemplateParameters(OB, Flags);
+}
+
+void IntrinsicFunctionIdentifierNode::output(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ switch (Operator) {
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, New, "operator new");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Delete, "operator delete");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Assign, "operator=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, RightShift, "operator>>");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LeftShift, "operator<<");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalNot, "operator!");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Equals, "operator==");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, NotEquals, "operator!=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArraySubscript,
+ "operator[]");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Pointer, "operator->");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Increment, "operator++");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Decrement, "operator--");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Minus, "operator-");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Plus, "operator+");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Dereference, "operator*");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseAnd, "operator&");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, MemberPointer,
+ "operator->*");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Divide, "operator/");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Modulus, "operator%");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LessThan, "operator<");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LessThanEqual, "operator<=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, GreaterThan, "operator>");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, GreaterThanEqual,
+ "operator>=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Comma, "operator,");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Parens, "operator()");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseNot, "operator~");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseXor, "operator^");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseOr, "operator|");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalAnd, "operator&&");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalOr, "operator||");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, TimesEqual, "operator*=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, PlusEqual, "operator+=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, MinusEqual, "operator-=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, DivEqual, "operator/=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ModEqual, "operator%=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, RshEqual, "operator>>=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LshEqual, "operator<<=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseAndEqual,
+ "operator&=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseOrEqual,
+ "operator|=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseXorEqual,
+ "operator^=");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VbaseDtor, "`vbase dtor'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecDelDtor,
+ "`vector deleting dtor'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, DefaultCtorClosure,
+ "`default ctor closure'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ScalarDelDtor,
+ "`scalar deleting dtor'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecCtorIter,
+ "`vector ctor iterator'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecDtorIter,
+ "`vector dtor iterator'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecVbaseCtorIter,
+ "`vector vbase ctor iterator'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VdispMap,
+ "`virtual displacement map'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecCtorIter,
+ "`eh vector ctor iterator'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecDtorIter,
+ "`eh vector dtor iterator'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecVbaseCtorIter,
+ "`eh vector vbase ctor iterator'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, CopyCtorClosure,
+ "`copy ctor closure'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LocalVftableCtorClosure,
+ "`local vftable ctor closure'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArrayNew, "operator new[]");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArrayDelete,
+ "operator delete[]");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorCtorIter,
+ "`managed vector ctor iterator'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorDtorIter,
+ "`managed vector dtor iterator'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVectorCopyCtorIter,
+ "`EH vector copy ctor iterator'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVectorVbaseCopyCtorIter,
+ "`EH vector vbase copy ctor iterator'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VectorCopyCtorIter,
+ "`vector copy ctor iterator'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VectorVbaseCopyCtorIter,
+ "`vector vbase copy constructor iterator'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorVbaseCopyCtorIter,
+ "`managed vector vbase copy constructor iterator'");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, CoAwait,
+ "operator co_await");
+ OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Spaceship, "operator<=>");
+ case IntrinsicFunctionKind::MaxIntrinsic:
+ case IntrinsicFunctionKind::None:
+ break;
+ }
+ outputTemplateParameters(OB, Flags);
+}
+
+void LocalStaticGuardIdentifierNode::output(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ if (IsThread)
+ OB << "`local static thread guard'";
+ else
+ OB << "`local static guard'";
+ if (ScopeIndex > 0)
+ OB << "{" << ScopeIndex << "}";
+}
+
+void ConversionOperatorIdentifierNode::output(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ OB << "operator";
+ outputTemplateParameters(OB, Flags);
+ OB << " ";
+ TargetType->output(OB, Flags);
+}
+
+void StructorIdentifierNode::output(OutputBuffer &OB, OutputFlags Flags) const {
+ if (IsDestructor)
+ OB << "~";
+ Class->output(OB, Flags);
+ outputTemplateParameters(OB, Flags);
+}
+
+void LiteralOperatorIdentifierNode::output(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ OB << "operator \"\"" << Name;
+ outputTemplateParameters(OB, Flags);
+}
+
+void FunctionSignatureNode::outputPre(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ if (!(Flags & OF_NoAccessSpecifier)) {
+ if (FunctionClass & FC_Public)
+ OB << "public: ";
+ if (FunctionClass & FC_Protected)
+ OB << "protected: ";
+ if (FunctionClass & FC_Private)
+ OB << "private: ";
+ }
+
+ if (!(Flags & OF_NoMemberType)) {
+ if (!(FunctionClass & FC_Global)) {
+ if (FunctionClass & FC_Static)
+ OB << "static ";
+ }
+ if (FunctionClass & FC_Virtual)
+ OB << "virtual ";
+
+ if (FunctionClass & FC_ExternC)
+ OB << "extern \"C\" ";
+ }
+
+ if (!(Flags & OF_NoReturnType) && ReturnType) {
+ ReturnType->outputPre(OB, Flags);
+ OB << " ";
+ }
+
+ if (!(Flags & OF_NoCallingConvention))
+ outputCallingConvention(OB, CallConvention);
+}
+
+void FunctionSignatureNode::outputPost(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ if (!(FunctionClass & FC_NoParameterList)) {
+ OB << "(";
+ if (Params)
+ Params->output(OB, Flags);
+ else
+ OB << "void";
+
+ if (IsVariadic) {
+ if (OB.back() != '(')
+ OB << ", ";
+ OB << "...";
+ }
+ OB << ")";
+ }
+
+ if (Quals & Q_Const)
+ OB << " const";
+ if (Quals & Q_Volatile)
+ OB << " volatile";
+ if (Quals & Q_Restrict)
+ OB << " __restrict";
+ if (Quals & Q_Unaligned)
+ OB << " __unaligned";
+
+ if (IsNoexcept)
+ OB << " noexcept";
+
+ if (RefQualifier == FunctionRefQualifier::Reference)
+ OB << " &";
+ else if (RefQualifier == FunctionRefQualifier::RValueReference)
+ OB << " &&";
+
+ if (!(Flags & OF_NoReturnType) && ReturnType)
+ ReturnType->outputPost(OB, Flags);
+}
+
+void ThunkSignatureNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
+ OB << "[thunk]: ";
+
+ FunctionSignatureNode::outputPre(OB, Flags);
+}
+
+void ThunkSignatureNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {
+ if (FunctionClass & FC_StaticThisAdjust) {
+ OB << "`adjustor{" << ThisAdjust.StaticOffset << "}'";
+ } else if (FunctionClass & FC_VirtualThisAdjust) {
+ if (FunctionClass & FC_VirtualThisAdjustEx) {
+ OB << "`vtordispex{" << ThisAdjust.VBPtrOffset << ", "
+ << ThisAdjust.VBOffsetOffset << ", " << ThisAdjust.VtordispOffset
+ << ", " << ThisAdjust.StaticOffset << "}'";
+ } else {
+ OB << "`vtordisp{" << ThisAdjust.VtordispOffset << ", "
+ << ThisAdjust.StaticOffset << "}'";
+ }
+ }
+
+ FunctionSignatureNode::outputPost(OB, Flags);
+}
+
+void PointerTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
+ if (Pointee->kind() == NodeKind::FunctionSignature) {
+ // If this is a pointer to a function, don't output the calling convention.
+ // It needs to go inside the parentheses.
+ const FunctionSignatureNode *Sig =
+ static_cast<const FunctionSignatureNode *>(Pointee);
+ Sig->outputPre(OB, OF_NoCallingConvention);
+ } else
+ Pointee->outputPre(OB, Flags);
+
+ outputSpaceIfNecessary(OB);
+
+ if (Quals & Q_Unaligned)
+ OB << "__unaligned ";
+
+ if (Pointee->kind() == NodeKind::ArrayType) {
+ OB << "(";
+ } else if (Pointee->kind() == NodeKind::FunctionSignature) {
+ OB << "(";
+ const FunctionSignatureNode *Sig =
+ static_cast<const FunctionSignatureNode *>(Pointee);
+ outputCallingConvention(OB, Sig->CallConvention);
+ OB << " ";
+ }
+
+ if (ClassParent) {
+ ClassParent->output(OB, Flags);
+ OB << "::";
+ }
+
+ switch (Affinity) {
+ case PointerAffinity::Pointer:
+ OB << "*";
+ break;
+ case PointerAffinity::Reference:
+ OB << "&";
+ break;
+ case PointerAffinity::RValueReference:
+ OB << "&&";
+ break;
+ default:
+ assert(false);
+ }
+ outputQualifiers(OB, Quals, false, false);
+}
+
+void PointerTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {
+ if (Pointee->kind() == NodeKind::ArrayType ||
+ Pointee->kind() == NodeKind::FunctionSignature)
+ OB << ")";
+
+ Pointee->outputPost(OB, Flags);
+}
+
+void TagTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
+ if (!(Flags & OF_NoTagSpecifier)) {
+ switch (Tag) {
+ OUTPUT_ENUM_CLASS_VALUE(TagKind, Class, "class");
+ OUTPUT_ENUM_CLASS_VALUE(TagKind, Struct, "struct");
+ OUTPUT_ENUM_CLASS_VALUE(TagKind, Union, "union");
+ OUTPUT_ENUM_CLASS_VALUE(TagKind, Enum, "enum");
+ }
+ OB << " ";
+ }
+ QualifiedName->output(OB, Flags);
+ outputQualifiers(OB, Quals, true, false);
+}
+
+void TagTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {}
+
+void ArrayTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
+ ElementType->outputPre(OB, Flags);
+ outputQualifiers(OB, Quals, true, false);
+}
+
+void ArrayTypeNode::outputOneDimension(OutputBuffer &OB, OutputFlags Flags,
+ Node *N) const {
+ assert(N->kind() == NodeKind::IntegerLiteral);
+ IntegerLiteralNode *ILN = static_cast<IntegerLiteralNode *>(N);
+ if (ILN->Value != 0)
+ ILN->output(OB, Flags);
+}
+
+void ArrayTypeNode::outputDimensionsImpl(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ if (Dimensions->Count == 0)
+ return;
+
+ outputOneDimension(OB, Flags, Dimensions->Nodes[0]);
+ for (size_t I = 1; I < Dimensions->Count; ++I) {
+ OB << "][";
+ outputOneDimension(OB, Flags, Dimensions->Nodes[I]);
+ }
+}
+
+void ArrayTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {
+ OB << "[";
+ outputDimensionsImpl(OB, Flags);
+ OB << "]";
+
+ ElementType->outputPost(OB, Flags);
+}
+
+void SymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
+ Name->output(OB, Flags);
+}
+
+void FunctionSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
+ Signature->outputPre(OB, Flags);
+ outputSpaceIfNecessary(OB);
+ Name->output(OB, Flags);
+ Signature->outputPost(OB, Flags);
+}
+
+void VariableSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
+ const char *AccessSpec = nullptr;
+ bool IsStatic = true;
+ switch (SC) {
+ case StorageClass::PrivateStatic:
+ AccessSpec = "private";
+ break;
+ case StorageClass::PublicStatic:
+ AccessSpec = "public";
+ break;
+ case StorageClass::ProtectedStatic:
+ AccessSpec = "protected";
+ break;
+ default:
+ IsStatic = false;
+ break;
+ }
+ if (!(Flags & OF_NoAccessSpecifier) && AccessSpec)
+ OB << AccessSpec << ": ";
+ if (!(Flags & OF_NoMemberType) && IsStatic)
+ OB << "static ";
+
+ if (!(Flags & OF_NoVariableType) && Type) {
+ Type->outputPre(OB, Flags);
+ outputSpaceIfNecessary(OB);
+ }
+ Name->output(OB, Flags);
+ if (!(Flags & OF_NoVariableType) && Type)
+ Type->outputPost(OB, Flags);
+}
+
+void CustomTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
+ Identifier->output(OB, Flags);
+}
+void CustomTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {}
+
+void QualifiedNameNode::output(OutputBuffer &OB, OutputFlags Flags) const {
+ Components->output(OB, Flags, "::");
+}
+
+void RttiBaseClassDescriptorNode::output(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ OB << "`RTTI Base Class Descriptor at (";
+ OB << NVOffset << ", " << VBPtrOffset << ", " << VBTableOffset << ", "
+ << this->Flags;
+ OB << ")'";
+}
+
+void LocalStaticGuardVariableNode::output(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ Name->output(OB, Flags);
+}
+
+void VcallThunkIdentifierNode::output(OutputBuffer &OB,
+ OutputFlags Flags) const {
+ OB << "`vcall'{" << OffsetInVTable << ", {flat}}";
+}
+
+void SpecialTableSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
+ outputQualifiers(OB, Quals, false, true);
+ Name->output(OB, Flags);
+ if (TargetName) {
+ OB << "{for `";
+ TargetName->output(OB, Flags);
+ OB << "'}";
+ }
+}