From 413443aa95b35fd7e01ad66ddadf3082636f4738 Mon Sep 17 00:00:00 2001 From: Mary <1760003+Thog@users.noreply.github.com> Date: Fri, 18 Dec 2020 17:56:41 +0100 Subject: [PATCH] Build LLVM demangler directly with our sources This remove dep on LLVM library by building the demangler as part of the project. This should help with building on macOS. --- .github/workflows/build.yml | 2 - CMakeLists.txt | 17 +- README.md | 1 - dist/get_deps_archlinux.sh | 2 - dist/get_deps_debian.sh | 1 - dist/get_deps_fedora.sh | 1 - dist/get_deps_msys2.sh | 1 - external/llvm/Demangle/CMakeLists.txt | 13 + external/llvm/Demangle/Demangle.cpp | 36 + external/llvm/Demangle/ItaniumDemangle.cpp | 577 ++ external/llvm/Demangle/MicrosoftDemangle.cpp | 2377 +++++++ .../llvm/Demangle/MicrosoftDemangleNodes.cpp | 653 ++ .../llvm/include/llvm/Demangle/Demangle.h | 124 + .../include/llvm/Demangle/DemangleConfig.h | 92 + .../include/llvm/Demangle/ItaniumDemangle.h | 5574 +++++++++++++++++ .../include/llvm/Demangle/MicrosoftDemangle.h | 278 + .../llvm/Demangle/MicrosoftDemangleNodes.h | 629 ++ .../llvm/include/llvm/Demangle/StringView.h | 126 + external/llvm/include/llvm/Demangle/Utility.h | 191 + msys2/PKGBUILD | 1 - 20 files changed, 10675 insertions(+), 21 deletions(-) create mode 100644 external/llvm/Demangle/CMakeLists.txt create mode 100644 external/llvm/Demangle/Demangle.cpp create mode 100644 external/llvm/Demangle/ItaniumDemangle.cpp create mode 100644 external/llvm/Demangle/MicrosoftDemangle.cpp create mode 100644 external/llvm/Demangle/MicrosoftDemangleNodes.cpp create mode 100644 external/llvm/include/llvm/Demangle/Demangle.h create mode 100644 external/llvm/include/llvm/Demangle/DemangleConfig.h create mode 100644 external/llvm/include/llvm/Demangle/ItaniumDemangle.h create mode 100644 external/llvm/include/llvm/Demangle/MicrosoftDemangle.h create mode 100644 external/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h create mode 100644 external/llvm/include/llvm/Demangle/StringView.h create mode 100644 external/llvm/include/llvm/Demangle/Utility.h diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bfafcc638..68ad731e0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,7 +35,6 @@ jobs: libmagic-dev \ libssl-dev \ libcapstone-dev \ - llvm-dev \ nlohmann-json3-dev \ python3-dev \ libfreetype-dev \ @@ -82,7 +81,6 @@ jobs: mingw-w64-${{ matrix.arch }}-glfw mingw-w64-${{ matrix.arch }}-glm mingw-w64-${{ matrix.arch }}-file - mingw-w64-${{ matrix.arch }}-llvm mingw-w64-${{ matrix.arch }}-nlohmann-json mingw-w64-${{ matrix.arch }}-openssl mingw-w64-${{ matrix.arch }}-polly diff --git a/CMakeLists.txt b/CMakeLists.txt index c19de3b74..a6a32a0d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,23 +13,18 @@ pkg_search_module(GLM REQUIRED glm) pkg_search_module(CRYPTO REQUIRED libcrypto) pkg_search_module(CAPSTONE REQUIRED capstone) find_package(OpenGL REQUIRED) -find_package(LLVM REQUIRED CONFIG) find_package(nlohmann_json REQUIRED) find_package(Freetype REQUIRED) find_package(Python COMPONENTS Interpreter Development) +add_subdirectory(external/llvm/Demangle) + if(Python_VERSION LESS 3) message(STATUS ${PYTHON_VERSION_MAJOR_MINOR}) message(FATAL_ERROR "No valid version of Python 3 was found.") endif() -llvm_map_components_to_libnames(_llvm_demangle_lib demangle) -llvm_expand_dependencies(_llvm_demangle_lib ${_llvm_demangle_lib}) - -# NOTE: if the demangle library cannot be found try to fallback to libLLVM (that is required on openSUSE and Gentoo) -find_library(llvm_demangle_lib NAMES ${_llvm_demangle_lib} LLVM PATHS ${LLVM_LIBRARY_DIR}) - -include_directories(include ${GLFW_INCLUDE_DIRS} ${CAPSTONE_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} libs/ImGui/include libs/glad/include ${Python_INCLUDE_DIRS}) +include_directories(include ${GLFW_INCLUDE_DIRS} ${CAPSTONE_INCLUDE_DIRS} libs/ImGui/include libs/glad/include ${Python_INCLUDE_DIRS}) # Get Python major and minor string(REPLACE "." ";" PYTHON_VERSION_MAJOR_MINOR ${Python_VERSION}) @@ -94,14 +89,12 @@ add_executable(ImHex resource.rc ) -target_link_directories(ImHex PRIVATE ${LLVM_LIBRARY_DIR}) - if (WIN32) - target_link_libraries(ImHex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib libcrypto.a libwinpthread.a libcapstone.a ${llvm_demangle_lib} ${Python_LIBRARIES} nlohmann_json::nlohmann_json Freetype::Freetype) + target_link_libraries(ImHex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib libcrypto.a libwinpthread.a libcapstone.a LLVMDemangle ${Python_LIBRARIES} nlohmann_json::nlohmann_json Freetype::Freetype) endif (WIN32) if (UNIX) - target_link_libraries(ImHex libglfw.so libmagic.so libcrypto.so libdl.so libcapstone.so ${llvm_demangle_lib} ${Python_LIBRARIES} nlohmann_json::nlohmann_json Freetype::Freetype) + target_link_libraries(ImHex libglfw.so libmagic.so libcrypto.so libdl.so libcapstone.so LLVMDemangle ${Python_LIBRARIES} nlohmann_json::nlohmann_json Freetype::Freetype) endif (UNIX) install(TARGETS ImHex DESTINATION bin) diff --git a/README.md b/README.md index 470754dd3..f26072c9e 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,6 @@ You need a C++20 compatible compiler such as GCC 10.2.0 to compile ImHex. Moreov - libmagic, libgnurx, libtre, libintl, libiconv - libcrypto - capstone -- libLLVMDemangle - nlohmann json - Python3 diff --git a/dist/get_deps_archlinux.sh b/dist/get_deps_archlinux.sh index 5a220fb59..05e4a0b81 100755 --- a/dist/get_deps_archlinux.sh +++ b/dist/get_deps_archlinux.sh @@ -5,8 +5,6 @@ pacman -S --needed \ file \ openssl \ capstone \ - llvm \ - llvm-libs \ nlohmann-json \ glm \ python3 \ diff --git a/dist/get_deps_debian.sh b/dist/get_deps_debian.sh index 2889810b9..7f832fb23 100644 --- a/dist/get_deps_debian.sh +++ b/dist/get_deps_debian.sh @@ -20,7 +20,6 @@ apt install \ libglfw3-dev \ libglm-dev \ libjsoncpp-dev \ - llvm-dev \ libssl-dev \ libstdc++-10-dev \ python3-dev \ diff --git a/dist/get_deps_fedora.sh b/dist/get_deps_fedora.sh index c0151cea2..905734717 100755 --- a/dist/get_deps_fedora.sh +++ b/dist/get_deps_fedora.sh @@ -8,7 +8,6 @@ dnf install \ glfw-devel \ glm-devel \ json-devel \ - llvm-devel \ mesa-libGL-devel \ openssl-devel \ python-devel \ diff --git a/dist/get_deps_msys2.sh b/dist/get_deps_msys2.sh index bf8ab8108..0a5858ece 100755 --- a/dist/get_deps_msys2.sh +++ b/dist/get_deps_msys2.sh @@ -8,7 +8,6 @@ pacman -S --needed \ mingw-w64-x86_64-glfw \ mingw-w64-x86_64-glm \ mingw-w64-x86_64-file \ - mingw-w64-x86_64-llvm \ mingw-w64-x86_64-nlohmann-json \ mingw-w64-x86_64-openssl \ mingw-w64-x86_64-polly \ diff --git a/external/llvm/Demangle/CMakeLists.txt b/external/llvm/Demangle/CMakeLists.txt new file mode 100644 index 000000000..6efa4f3d0 --- /dev/null +++ b/external/llvm/Demangle/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.16) +project(LLVMDemangle) + +set(CMAKE_CXX_STANDARD 17) + +add_library(LLVMDemangle + Demangle.cpp + ItaniumDemangle.cpp + MicrosoftDemangle.cpp + MicrosoftDemangleNodes.cpp +) + +target_include_directories(LLVMDemangle PUBLIC ../include) diff --git a/external/llvm/Demangle/Demangle.cpp b/external/llvm/Demangle/Demangle.cpp new file mode 100644 index 000000000..71dafa0b2 --- /dev/null +++ b/external/llvm/Demangle/Demangle.cpp @@ -0,0 +1,36 @@ +//===-- 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 + +static bool isItaniumEncoding(const std::string &MangledName) { + size_t Pos = MangledName.find_first_not_of('_'); + // A valid Itanium encoding requires 1-4 leading underscores, followed by 'Z'. + return Pos > 0 && Pos <= 4 && MangledName[Pos] == 'Z'; +} + +std::string llvm::demangle(const std::string &MangledName) { + char *Demangled; + if (isItaniumEncoding(MangledName)) + Demangled = itaniumDemangle(MangledName.c_str(), nullptr, nullptr, nullptr); + else + Demangled = microsoftDemangle(MangledName.c_str(), nullptr, nullptr, + nullptr, nullptr); + + if (!Demangled) + return MangledName; + + std::string Ret = Demangled; + free(Demangled); + return Ret; +} diff --git a/external/llvm/Demangle/ItaniumDemangle.cpp b/external/llvm/Demangle/ItaniumDemangle.cpp new file mode 100644 index 000000000..fad9b6b7b --- /dev/null +++ b/external/llvm/Demangle/ItaniumDemangle.cpp @@ -0,0 +1,577 @@ +//===------------------------- 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 +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace llvm::itanium_demangle; + +constexpr const char *itanium_demangle::FloatData::spec; +constexpr const char *itanium_demangle::FloatData::spec; +constexpr const char *itanium_demangle::FloatData::spec; + +// := _ # when number < 10 +// := __ _ # 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 static constexpr bool wantsNewline(const NodeT *) { + return true; + } + static bool wantsNewline(NodeArray A) { return !A.empty(); } + static constexpr bool wantsNewline(...) { return false; } + + template 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(""); + } + 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 std::enable_if_t::value> print(T N) { + fprintf(stderr, "%llu", (unsigned long long)N); + } + + template std::enable_if_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 newLine() { + printStr("\n"); + for (unsigned I = 0; I != Depth; ++I) + printStr(" "); + PendingNewline = false; + } + + template void printWithPendingNewline(T V) { + print(V); + if (wantsNewline(V)) + PendingNewline = true; + } + + template void printWithComma(T V) { + if (PendingNewline || wantsNewline(V)) { + printStr(","); + newLine(); + } else { + printStr(", "); + } + + printWithPendingNewline(V); + } + + struct CtorArgPrinter { + DumpVisitor &Visitor; + + template 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 void operator()(const NodeT *Node) { + Depth += 2; + fprintf(stderr, "%s(", itanium_demangle::NodeKind::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(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(std::malloc(NBytes)); + if (NewMeta == nullptr) + std::terminate(); + BlockList->Next = new (NewMeta) BlockMeta{BlockList->Next, 0}; + return static_cast(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(reinterpret_cast(BlockList + 1) + + BlockList->Current - N); + } + + void reset() { + while (BlockList) { + BlockMeta* Tmp = BlockList; + BlockList = BlockList->Next; + if (reinterpret_cast(Tmp) != InitialBuffer) + std::free(Tmp); + } + BlockList = new (InitialBuffer) BlockMeta{nullptr, 0}; + } + + ~BumpPointerAllocator() { reset(); } +}; + +class DefaultAllocator { + BumpPointerAllocator Alloc; + +public: + void reset() { Alloc.reset(); } + + template T *makeNode(Args &&...args) { + return new (Alloc.allocate(sizeof(T))) + T(std::forward(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; + +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)); + OutputStream S; + + Node *AST = Parser.parse(); + + if (AST == nullptr) + InternalStatus = demangle_invalid_mangled_name; + else if (!initializeOutputStream(Buf, N, S, 1024)) + InternalStatus = demangle_memory_alloc_failure; + else { + assert(Parser.ForwardTemplateRefs.empty()); + AST->print(S); + S += '\0'; + if (N != nullptr) + *N = S.getCurrentPosition(); + Buf = S.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(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(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) { + OutputStream S; + if (!initializeOutputStream(Buf, N, S, 128)) + return nullptr; + RootNode->print(S); + S += '\0'; + if (N != nullptr) + *N = S.getCurrentPosition(); + return S.getBuffer(); +} + +char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const { + if (!isFunction()) + return nullptr; + + const Node *Name = static_cast(RootNode)->getName(); + + while (true) { + switch (Name->getKind()) { + case Node::KAbiTagAttr: + Name = static_cast(Name)->Base; + continue; + case Node::KStdQualifiedName: + Name = static_cast(Name)->Child; + continue; + case Node::KNestedName: + Name = static_cast(Name)->Name; + continue; + case Node::KLocalName: + Name = static_cast(Name)->Entity; + continue; + case Node::KNameWithTemplateArgs: + Name = static_cast(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(RootNode)->getName(); + + OutputStream S; + if (!initializeOutputStream(Buf, N, S, 128)) + return nullptr; + + KeepGoingLocalFunction: + while (true) { + if (Name->getKind() == Node::KAbiTagAttr) { + Name = static_cast(Name)->Base; + continue; + } + if (Name->getKind() == Node::KNameWithTemplateArgs) { + Name = static_cast(Name)->Name; + continue; + } + break; + } + + switch (Name->getKind()) { + case Node::KStdQualifiedName: + S += "std"; + break; + case Node::KNestedName: + static_cast(Name)->Qual->print(S); + break; + case Node::KLocalName: { + auto *LN = static_cast(Name); + LN->Encoding->print(S); + S += "::"; + Name = LN->Entity; + goto KeepGoingLocalFunction; + } + default: + break; + } + S += '\0'; + if (N != nullptr) + *N = S.getCurrentPosition(); + return S.getBuffer(); +} + +char *ItaniumPartialDemangler::getFunctionName(char *Buf, size_t *N) const { + if (!isFunction()) + return nullptr; + auto *Name = static_cast(RootNode)->getName(); + return printNode(Name, Buf, N); +} + +char *ItaniumPartialDemangler::getFunctionParameters(char *Buf, + size_t *N) const { + if (!isFunction()) + return nullptr; + NodeArray Params = static_cast(RootNode)->getParams(); + + OutputStream S; + if (!initializeOutputStream(Buf, N, S, 128)) + return nullptr; + + S += '('; + Params.printWithComma(S); + S += ')'; + S += '\0'; + if (N != nullptr) + *N = S.getCurrentPosition(); + return S.getBuffer(); +} + +char *ItaniumPartialDemangler::getFunctionReturnType( + char *Buf, size_t *N) const { + if (!isFunction()) + return nullptr; + + OutputStream S; + if (!initializeOutputStream(Buf, N, S, 128)) + return nullptr; + + if (const Node *Ret = + static_cast(RootNode)->getReturnType()) + Ret->print(S); + + S += '\0'; + if (N != nullptr) + *N = S.getCurrentPosition(); + return S.getBuffer(); +} + +char *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const { + assert(RootNode != nullptr && "must call partialDemangle()"); + return printNode(static_cast(RootNode), Buf, N); +} + +bool ItaniumPartialDemangler::hasFunctionQualifiers() const { + assert(RootNode != nullptr && "must call partialDemangle()"); + if (!isFunction()) + return false; + auto *E = static_cast(RootNode); + return E->getCVQuals() != QualNone || E->getRefQual() != FrefQualNone; +} + +bool ItaniumPartialDemangler::isCtorOrDtor() const { + const Node *N = static_cast(RootNode); + while (N) { + switch (N->getKind()) { + default: + return false; + case Node::KCtorDtorName: + return true; + + case Node::KAbiTagAttr: + N = static_cast(N)->Base; + break; + case Node::KFunctionEncoding: + N = static_cast(N)->getName(); + break; + case Node::KLocalName: + N = static_cast(N)->Entity; + break; + case Node::KNameWithTemplateArgs: + N = static_cast(N)->Name; + break; + case Node::KNestedName: + N = static_cast(N)->Name; + break; + case Node::KStdQualifiedName: + N = static_cast(N)->Child; + break; + } + } + return false; +} + +bool ItaniumPartialDemangler::isFunction() const { + assert(RootNode != nullptr && "must call partialDemangle()"); + return static_cast(RootNode)->getKind() == + Node::KFunctionEncoding; +} + +bool ItaniumPartialDemangler::isSpecialName() const { + assert(RootNode != nullptr && "must call partialDemangle()"); + auto K = static_cast(RootNode)->getKind(); + return K == Node::KSpecialName || K == Node::KCtorVtableSpecialName; +} + +bool ItaniumPartialDemangler::isData() const { + return !isFunction() && !isSpecialName(); +} diff --git a/external/llvm/Demangle/MicrosoftDemangle.cpp b/external/llvm/Demangle/MicrosoftDemangle.cpp new file mode 100644 index 000000000..16074314a --- /dev/null +++ b/external/llvm/Demangle/MicrosoftDemangle.cpp @@ -0,0 +1,2377 @@ +//===- 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 +#include +#include +#include + +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 +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() + 1); + std::strcpy(Stable, Borrowed.begin()); + + return {Stable, Borrowed.size()}; +} + +SpecialTableSymbolNode * +Demangler::demangleSpecialTableSymbolNode(StringView &MangledName, + SpecialIntrinsicKind K) { + NamedIdentifierNode *NI = Arena.alloc(); + 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(); + 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(); + LSGI->IsThread = IsThread; + QualifiedNameNode *QN = demangleNameScopeChain(MangledName, LSGI); + LocalStaticGuardVariableNode *LSGVN = + Arena.alloc(); + 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(); + Id->Name = Name; + return Id; +} + +static QualifiedNameNode *synthesizeQualifiedName(ArenaAllocator &Arena, + IdentifierNode *Identifier) { + QualifiedNameNode *QN = Arena.alloc(); + QN->Components = Arena.alloc(); + QN->Components->Count = 1; + QN->Components->Nodes = Arena.allocArray(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(); + 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(); + VSN->Name = QN; + if (MangledName.consumeFront("8")) + return VSN; + + Error = true; + return nullptr; +} + +VariableSymbolNode * +Demangler::demangleRttiBaseClassDescriptorNode(ArenaAllocator &Arena, + StringView &MangledName) { + RttiBaseClassDescriptorNode *RBCDN = + Arena.alloc(); + RBCDN->NVOffset = demangleUnsigned(MangledName); + RBCDN->VBPtrOffset = demangleSigned(MangledName); + RBCDN->VBTableOffset = demangleUnsigned(MangledName); + RBCDN->Flags = demangleUnsigned(MangledName); + if (Error) + return nullptr; + + VariableSymbolNode *VSN = Arena.alloc(); + VSN->Name = demangleNameScopeChain(MangledName, RBCDN); + MangledName.consumeFront('8'); + return VSN; +} + +FunctionSymbolNode *Demangler::demangleInitFiniStub(StringView &MangledName, + bool IsDestructor) { + DynamicStructorIdentifierNode *DSIN = + Arena.alloc(); + 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(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(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(); + N->IsDestructor = IsDestructor; + return N; +} + +ConversionOperatorIdentifierNode * +Demangler::demangleConversionOperatorIdentifier(StringView &MangledName) { + ConversionOperatorIdentifierNode *N = + Arena.alloc(); + return N; +} + +LiteralOperatorIdentifierNode * +Demangler::demangleLiteralOperatorIdentifier(StringView &MangledName) { + LiteralOperatorIdentifierNode *N = + Arena.alloc(); + 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 () + 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 # udt returning + IFK::None, // ?_Q # + 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 + IFK::None, // ?_X + IFK::None, // ?_Y + IFK::None, // ?_Z + }; + static IFK DoubleUnder[36] = { + IFK::None, // ?__0 + IFK::None, // ?__1 + IFK::None, // ?__2 + IFK::None, // ?__3 + IFK::None, // ?__4 + IFK::None, // ?__5 + IFK::None, // ?__6 + IFK::None, // ?__7 + IFK::None, // ?__8 + IFK::None, // ?__9 + 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 + IFK::None, // ?__O + IFK::None, // ?__P + IFK::None, // ?__Q + IFK::None, // ?__R + IFK::None, // ?__S + IFK::None, // ?__T + IFK::None, // ?__U + IFK::None, // ?__V + IFK::None, // ?__W + IFK::None, // ?__X + IFK::None, // ?__Y + IFK::None, // ?__Z + }; + + 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( + translateIntrinsicFunctionCode(CH, Group)); + } + case FunctionIdentifierCodeGroup::Under: + return Arena.alloc( + translateIntrinsicFunctionCode(MangledName.popFront(), Group)); + case FunctionIdentifierCodeGroup::DoubleUnder: + switch (char CH = MangledName.popFront()) { + case 'K': + return demangleLiteralOperatorIdentifier(MangledName); + default: + return Arena.alloc( + 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(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(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(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")) + return nullptr; + MangledName.consumeFront(".?A"); + if (MangledName.empty()) + return nullptr; + + return demangleClassType(MangledName); +} + +// ::= +// ::= 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(); + + VSN->Type = demangleType(MangledName, QualifierMangleMode::Drop); + VSN->SC = SC; + + if (Error) + return nullptr; + + // ::= + // ::= # pointers, references + switch (VSN->Type->kind()) { + case NodeKind::PointerType: { + PointerTypeNode *PTN = static_cast(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. +// +// ::= [?] +// +// ::= # when 1 <= Number <= 10 +// ::= + @ # when Number == 0 or >= 10 +// +// ::= [A-P] # A = 0, B = 1, ... +std::pair 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(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(); + 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. + OutputStream OS; + if (!initializeOutputStream(nullptr, nullptr, OS, 1024)) + // FIXME: Propagate out-of-memory as an error? + std::terminate(); + Identifier->output(OS, OF_Default); + OS << '\0'; + char *Name = OS.getBuffer(); + + StringView Owned = copyString(Name); + memorizeString(Owned); + std::free(Name); +} + +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(); + 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(OutputStream &OS, 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--] = '\\'; + OS << StringView(&TempBuffer[Pos + 1]); +} + +static void outputEscapedChar(OutputStream &OS, unsigned C) { + switch (C) { + case '\0': // nul + OS << "\\0"; + return; + case '\'': // single quote + OS << "\\\'"; + return; + case '\"': // double quote + OS << "\\\""; + return; + case '\\': // backslash + OS << "\\\\"; + return; + case '\a': // bell + OS << "\\a"; + return; + case '\b': // backspace + OS << "\\b"; + return; + case '\f': // form feed + OS << "\\f"; + return; + case '\n': // new line + OS << "\\n"; + return; + case '\r': // carriage return + OS << "\\r"; + return; + case '\t': // tab + OS << "\\t"; + return; + case '\v': // vertical tab + OS << "\\v"; + return; + default: + break; + } + + if (C > 0x1F && C < 0x7F) { + // Standard ascii char. + OS << (char)C; + return; + } + + outputHex(OS, 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(StringBytes[I]); + Result |= C << (8 * I); + } + return Result; +} + +FunctionSymbolNode *Demangler::demangleVcallThunkNode(StringView &MangledName) { + FunctionSymbolNode *FSN = Arena.alloc(); + VcallThunkIdentifierNode *VTIN = Arena.alloc(); + FSN->Signature = Arena.alloc(); + 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. + OutputStream OS; + StringView CRC; + uint64_t StringByteSize; + bool IsWcharT = false; + bool IsNegative = false; + size_t CrcEndPos = 0; + char *ResultBuffer = nullptr; + + EncodedStringLiteralNode *Result = Arena.alloc(); + + // Must happen before the first `goto StringLiteralError`. + if (!initializeOutputStream(nullptr, nullptr, OS, 1024)) + // FIXME: Propagate out-of-memory as an error? + std::terminate(); + + // 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(OS, 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(OS, NextChar); + } + } + + OS << '\0'; + ResultBuffer = OS.getBuffer(); + Result->DecodedString = copyString(ResultBuffer); + std::free(ResultBuffer); + return Result; + +StringLiteralError: + Error = true; + std::free(OS.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(); + 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(); + 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. + OutputStream OS; + if (!initializeOutputStream(nullptr, nullptr, OS, 1024)) + // FIXME: Propagate out-of-memory as an error? + std::terminate(); + OS << '`'; + Scope->output(OS, OF_Default); + OS << '\''; + OS << "::`" << Number << "'"; + OS << '\0'; + char *Result = OS.getBuffer(); + Identifier->Name = copyString(Result); + std::free(Result); + 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(Identifier); + Node *ClassNode = QN->Components->Nodes[QN->Components->Count - 2]; + SIN->Class = static_cast(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(); + N->Count = Count; + N->Nodes = Arena.allocArray(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(); + + Head->N = UnqualifiedName; + + size_t Count = 1; + while (!MangledName.consumeFront("@")) { + ++Count; + NodeList *NewHead = Arena.alloc(); + 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(); + 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; + } + + 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 +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); +} + +// ::= +// ::= # 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(); + + 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); + + // ::= + // ::= @ # 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(); + TTN->ThisAdjust.StaticOffset = demangleSigned(MangledName); + } else if (FC & FC_VirtualThisAdjust) { + TTN = Arena.alloc(); + 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(); + } else { + bool HasThisQuals = !(FC & (FC_Global | FC_Static)); + FSN = demangleFunctionType(MangledName, HasThisQuals); + } + + if (Error) + return nullptr; + + if (TTN) { + *static_cast(TTN) = *FSN; + FSN = TTN; + } + FSN->FunctionClass = FC; + + FunctionSymbolNode *Symbol = Arena.alloc(); + Symbol->Signature = FSN; + return Symbol; +} + +CustomTypeNode *Demangler::demangleCustomType(StringView &MangledName) { + assert(MangledName.startsWith('?')); + MangledName.popFront(); + + CustomTypeNode *CTN = Arena.alloc(); + 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(PrimitiveKind::Nullptr); + + switch (MangledName.popFront()) { + case 'X': + return Arena.alloc(PrimitiveKind::Void); + case 'D': + return Arena.alloc(PrimitiveKind::Char); + case 'C': + return Arena.alloc(PrimitiveKind::Schar); + case 'E': + return Arena.alloc(PrimitiveKind::Uchar); + case 'F': + return Arena.alloc(PrimitiveKind::Short); + case 'G': + return Arena.alloc(PrimitiveKind::Ushort); + case 'H': + return Arena.alloc(PrimitiveKind::Int); + case 'I': + return Arena.alloc(PrimitiveKind::Uint); + case 'J': + return Arena.alloc(PrimitiveKind::Long); + case 'K': + return Arena.alloc(PrimitiveKind::Ulong); + case 'M': + return Arena.alloc(PrimitiveKind::Float); + case 'N': + return Arena.alloc(PrimitiveKind::Double); + case 'O': + return Arena.alloc(PrimitiveKind::Ldouble); + case '_': { + if (MangledName.empty()) { + Error = true; + return nullptr; + } + switch (MangledName.popFront()) { + case 'N': + return Arena.alloc(PrimitiveKind::Bool); + case 'J': + return Arena.alloc(PrimitiveKind::Int64); + case 'K': + return Arena.alloc(PrimitiveKind::Uint64); + case 'W': + return Arena.alloc(PrimitiveKind::Wchar); + case 'Q': + return Arena.alloc(PrimitiveKind::Char8); + case 'S': + return Arena.alloc(PrimitiveKind::Char16); + case 'U': + return Arena.alloc(PrimitiveKind::Char32); + } + break; + } + } + Error = true; + return nullptr; +} + +TagTypeNode *Demangler::demangleClassType(StringView &MangledName) { + TagTypeNode *TT = nullptr; + + switch (MangledName.popFront()) { + case 'T': + TT = Arena.alloc(TagKind::Union); + break; + case 'U': + TT = Arena.alloc(TagKind::Struct); + break; + case 'V': + TT = Arena.alloc(TagKind::Class); + break; + case 'W': + if (!MangledName.consumeFront('4')) { + Error = true; + return nullptr; + } + TT = Arena.alloc(TagKind::Enum); + break; + default: + assert(false); + } + + TT->QualifiedName = demangleFullyQualifiedTypeName(MangledName); + return TT; +} + +// ::= E? +// # the E is required for 64-bit non-static pointers +PointerTypeNode *Demangler::demanglePointerType(StringView &MangledName) { + PointerTypeNode *Pointer = Arena.alloc(); + + 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(); + + 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(); + NodeList *Head = Arena.alloc(); + 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(D, IsNegative); + if (I + 1 < Rank) { + Tail->Next = Arena.alloc(); + 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 **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(); + (*Current)->N = Backrefs.FunctionParams[N]; + Current = &(*Current)->Next; + continue; + } + + size_t OldSize = MangledName.size(); + + *Current = Arena.alloc(); + 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 &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(); + TPRN->IsMemberPointer = true; + + MangledName = MangledName.dropFront(); + // 1 - single inheritance + // H - multiple inheritance + // I - virtual inheritance + // J - unspecified inheritance + 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(); + TPRN->Symbol = parse(MangledName); + TPRN->Affinity = PointerAffinity::Reference; + } else if (MangledName.startsWith("$F") || MangledName.startsWith("$G")) { + TP.N = TPRN = Arena.alloc(); + + // 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(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. + OutputStream OS; + if (!initializeOutputStream(nullptr, nullptr, OS, 1024)) + std::terminate(); + for (size_t I = 0; I < Backrefs.FunctionParamCount; ++I) { + OS.setCurrentPosition(0); + + TypeNode *T = Backrefs.FunctionParams[I]; + T->output(OS, OF_Default); + + std::printf(" [%d] - %.*s\n", (int)I, (int)OS.getCurrentPosition(), + OS.getBuffer()); + } + std::free(OS.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; + OutputStream S; + + 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); + + int InternalStatus = demangle_success; + if (D.Error) + InternalStatus = demangle_invalid_mangled_name; + else if (!initializeOutputStream(Buf, N, S, 1024)) + InternalStatus = demangle_memory_alloc_failure; + else { + AST->output(S, OF); + S += '\0'; + if (N != nullptr) + *N = S.getCurrentPosition(); + Buf = S.getBuffer(); + } + + if (Status) + *Status = InternalStatus; + return InternalStatus == demangle_success ? Buf : nullptr; +} diff --git a/external/llvm/Demangle/MicrosoftDemangleNodes.cpp b/external/llvm/Demangle/MicrosoftDemangleNodes.cpp new file mode 100644 index 000000000..9cee97523 --- /dev/null +++ b/external/llvm/Demangle/MicrosoftDemangleNodes.cpp @@ -0,0 +1,653 @@ +//===- 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/DemangleConfig.h" +#include "llvm/Demangle/Utility.h" +#include +#include + +using namespace llvm; +using namespace ms_demangle; + +#define OUTPUT_ENUM_CLASS_VALUE(Enum, Value, Desc) \ + case Enum::Value: \ + OS << Desc; \ + break; + +// Writes a space if the last token does not end with a punctuation. +static void outputSpaceIfNecessary(OutputStream &OS) { + if (OS.empty()) + return; + + char C = OS.back(); + if (std::isalnum(C) || C == '>') + OS << " "; +} + +static void outputSingleQualifier(OutputStream &OS, Qualifiers Q) { + switch (Q) { + case Q_Const: + OS << "const"; + break; + case Q_Volatile: + OS << "volatile"; + break; + case Q_Restrict: + OS << "__restrict"; + break; + default: + break; + } +} + +static bool outputQualifierIfPresent(OutputStream &OS, Qualifiers Q, + Qualifiers Mask, bool NeedSpace) { + if (!(Q & Mask)) + return NeedSpace; + + if (NeedSpace) + OS << " "; + + outputSingleQualifier(OS, Mask); + return true; +} + +static void outputQualifiers(OutputStream &OS, Qualifiers Q, bool SpaceBefore, + bool SpaceAfter) { + if (Q == Q_None) + return; + + size_t Pos1 = OS.getCurrentPosition(); + SpaceBefore = outputQualifierIfPresent(OS, Q, Q_Const, SpaceBefore); + SpaceBefore = outputQualifierIfPresent(OS, Q, Q_Volatile, SpaceBefore); + SpaceBefore = outputQualifierIfPresent(OS, Q, Q_Restrict, SpaceBefore); + size_t Pos2 = OS.getCurrentPosition(); + if (SpaceAfter && Pos2 > Pos1) + OS << " "; +} + +static void outputCallingConvention(OutputStream &OS, CallingConv CC) { + outputSpaceIfNecessary(OS); + + switch (CC) { + case CallingConv::Cdecl: + OS << "__cdecl"; + break; + case CallingConv::Fastcall: + OS << "__fastcall"; + break; + case CallingConv::Pascal: + OS << "__pascal"; + break; + case CallingConv::Regcall: + OS << "__regcall"; + break; + case CallingConv::Stdcall: + OS << "__stdcall"; + break; + case CallingConv::Thiscall: + OS << "__thiscall"; + break; + case CallingConv::Eabi: + OS << "__eabi"; + break; + case CallingConv::Vectorcall: + OS << "__vectorcall"; + break; + case CallingConv::Clrcall: + OS << "__clrcall"; + break; + default: + break; + } +} + +std::string Node::toString(OutputFlags Flags) const { + OutputStream OS; + initializeOutputStream(nullptr, nullptr, OS, 1024); + this->output(OS, Flags); + OS << '\0'; + return {OS.getBuffer()}; +} + +void PrimitiveTypeNode::outputPre(OutputStream &OS, 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(OS, Quals, true, false); +} + +void NodeArrayNode::output(OutputStream &OS, OutputFlags Flags) const { + output(OS, Flags, ", "); +} + +void NodeArrayNode::output(OutputStream &OS, OutputFlags Flags, + StringView Separator) const { + if (Count == 0) + return; + if (Nodes[0]) + Nodes[0]->output(OS, Flags); + for (size_t I = 1; I < Count; ++I) { + OS << Separator; + Nodes[I]->output(OS, Flags); + } +} + +void EncodedStringLiteralNode::output(OutputStream &OS, + OutputFlags Flags) const { + switch (Char) { + case CharKind::Wchar: + OS << "L\""; + break; + case CharKind::Char: + OS << "\""; + break; + case CharKind::Char16: + OS << "u\""; + break; + case CharKind::Char32: + OS << "U\""; + break; + } + OS << DecodedString << "\""; + if (IsTruncated) + OS << "..."; +} + +void IntegerLiteralNode::output(OutputStream &OS, OutputFlags Flags) const { + if (IsNegative) + OS << '-'; + OS << Value; +} + +void TemplateParameterReferenceNode::output(OutputStream &OS, + OutputFlags Flags) const { + if (ThunkOffsetCount > 0) + OS << "{"; + else if (Affinity == PointerAffinity::Pointer) + OS << "&"; + + if (Symbol) { + Symbol->output(OS, Flags); + if (ThunkOffsetCount > 0) + OS << ", "; + } + + if (ThunkOffsetCount > 0) + OS << ThunkOffsets[0]; + for (int I = 1; I < ThunkOffsetCount; ++I) { + OS << ", " << ThunkOffsets[I]; + } + if (ThunkOffsetCount > 0) + OS << "}"; +} + +void IdentifierNode::outputTemplateParameters(OutputStream &OS, + OutputFlags Flags) const { + if (!TemplateParams) + return; + OS << "<"; + TemplateParams->output(OS, Flags); + OS << ">"; +} + +void DynamicStructorIdentifierNode::output(OutputStream &OS, + OutputFlags Flags) const { + if (IsDestructor) + OS << "`dynamic atexit destructor for "; + else + OS << "`dynamic initializer for "; + + if (Variable) { + OS << "`"; + Variable->output(OS, Flags); + OS << "''"; + } else { + OS << "'"; + Name->output(OS, Flags); + OS << "''"; + } +} + +void NamedIdentifierNode::output(OutputStream &OS, OutputFlags Flags) const { + OS << Name; + outputTemplateParameters(OS, Flags); +} + +void IntrinsicFunctionIdentifierNode::output(OutputStream &OS, + 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(OS, Flags); +} + +void LocalStaticGuardIdentifierNode::output(OutputStream &OS, + OutputFlags Flags) const { + if (IsThread) + OS << "`local static thread guard'"; + else + OS << "`local static guard'"; + if (ScopeIndex > 0) + OS << "{" << ScopeIndex << "}"; +} + +void ConversionOperatorIdentifierNode::output(OutputStream &OS, + OutputFlags Flags) const { + OS << "operator"; + outputTemplateParameters(OS, Flags); + OS << " "; + TargetType->output(OS, Flags); +} + +void StructorIdentifierNode::output(OutputStream &OS, OutputFlags Flags) const { + if (IsDestructor) + OS << "~"; + Class->output(OS, Flags); + outputTemplateParameters(OS, Flags); +} + +void LiteralOperatorIdentifierNode::output(OutputStream &OS, + OutputFlags Flags) const { + OS << "operator \"\"" << Name; + outputTemplateParameters(OS, Flags); +} + +void FunctionSignatureNode::outputPre(OutputStream &OS, + OutputFlags Flags) const { + if (!(Flags & OF_NoAccessSpecifier)) { + if (FunctionClass & FC_Public) + OS << "public: "; + if (FunctionClass & FC_Protected) + OS << "protected: "; + if (FunctionClass & FC_Private) + OS << "private: "; + } + + if (!(Flags & OF_NoMemberType)) { + if (!(FunctionClass & FC_Global)) { + if (FunctionClass & FC_Static) + OS << "static "; + } + if (FunctionClass & FC_Virtual) + OS << "virtual "; + + if (FunctionClass & FC_ExternC) + OS << "extern \"C\" "; + } + + if (!(Flags & OF_NoReturnType) && ReturnType) { + ReturnType->outputPre(OS, Flags); + OS << " "; + } + + if (!(Flags & OF_NoCallingConvention)) + outputCallingConvention(OS, CallConvention); +} + +void FunctionSignatureNode::outputPost(OutputStream &OS, + OutputFlags Flags) const { + if (!(FunctionClass & FC_NoParameterList)) { + OS << "("; + if (Params) + Params->output(OS, Flags); + else + OS << "void"; + + if (IsVariadic) { + if (OS.back() != '(') + OS << ", "; + OS << "..."; + } + OS << ")"; + } + + if (Quals & Q_Const) + OS << " const"; + if (Quals & Q_Volatile) + OS << " volatile"; + if (Quals & Q_Restrict) + OS << " __restrict"; + if (Quals & Q_Unaligned) + OS << " __unaligned"; + + if (IsNoexcept) + OS << " noexcept"; + + if (RefQualifier == FunctionRefQualifier::Reference) + OS << " &"; + else if (RefQualifier == FunctionRefQualifier::RValueReference) + OS << " &&"; + + if (!(Flags & OF_NoReturnType) && ReturnType) + ReturnType->outputPost(OS, Flags); +} + +void ThunkSignatureNode::outputPre(OutputStream &OS, OutputFlags Flags) const { + OS << "[thunk]: "; + + FunctionSignatureNode::outputPre(OS, Flags); +} + +void ThunkSignatureNode::outputPost(OutputStream &OS, OutputFlags Flags) const { + if (FunctionClass & FC_StaticThisAdjust) { + OS << "`adjustor{" << ThisAdjust.StaticOffset << "}'"; + } else if (FunctionClass & FC_VirtualThisAdjust) { + if (FunctionClass & FC_VirtualThisAdjustEx) { + OS << "`vtordispex{" << ThisAdjust.VBPtrOffset << ", " + << ThisAdjust.VBOffsetOffset << ", " << ThisAdjust.VtordispOffset + << ", " << ThisAdjust.StaticOffset << "}'"; + } else { + OS << "`vtordisp{" << ThisAdjust.VtordispOffset << ", " + << ThisAdjust.StaticOffset << "}'"; + } + } + + FunctionSignatureNode::outputPost(OS, Flags); +} + +void PointerTypeNode::outputPre(OutputStream &OS, 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(Pointee); + Sig->outputPre(OS, OF_NoCallingConvention); + } else + Pointee->outputPre(OS, Flags); + + outputSpaceIfNecessary(OS); + + if (Quals & Q_Unaligned) + OS << "__unaligned "; + + if (Pointee->kind() == NodeKind::ArrayType) { + OS << "("; + } else if (Pointee->kind() == NodeKind::FunctionSignature) { + OS << "("; + const FunctionSignatureNode *Sig = + static_cast(Pointee); + outputCallingConvention(OS, Sig->CallConvention); + OS << " "; + } + + if (ClassParent) { + ClassParent->output(OS, Flags); + OS << "::"; + } + + switch (Affinity) { + case PointerAffinity::Pointer: + OS << "*"; + break; + case PointerAffinity::Reference: + OS << "&"; + break; + case PointerAffinity::RValueReference: + OS << "&&"; + break; + default: + assert(false); + } + outputQualifiers(OS, Quals, false, false); +} + +void PointerTypeNode::outputPost(OutputStream &OS, OutputFlags Flags) const { + if (Pointee->kind() == NodeKind::ArrayType || + Pointee->kind() == NodeKind::FunctionSignature) + OS << ")"; + + Pointee->outputPost(OS, Flags); +} + +void TagTypeNode::outputPre(OutputStream &OS, 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"); + } + OS << " "; + } + QualifiedName->output(OS, Flags); + outputQualifiers(OS, Quals, true, false); +} + +void TagTypeNode::outputPost(OutputStream &OS, OutputFlags Flags) const {} + +void ArrayTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const { + ElementType->outputPre(OS, Flags); + outputQualifiers(OS, Quals, true, false); +} + +void ArrayTypeNode::outputOneDimension(OutputStream &OS, OutputFlags Flags, + Node *N) const { + assert(N->kind() == NodeKind::IntegerLiteral); + IntegerLiteralNode *ILN = static_cast(N); + if (ILN->Value != 0) + ILN->output(OS, Flags); +} + +void ArrayTypeNode::outputDimensionsImpl(OutputStream &OS, + OutputFlags Flags) const { + if (Dimensions->Count == 0) + return; + + outputOneDimension(OS, Flags, Dimensions->Nodes[0]); + for (size_t I = 1; I < Dimensions->Count; ++I) { + OS << "]["; + outputOneDimension(OS, Flags, Dimensions->Nodes[I]); + } +} + +void ArrayTypeNode::outputPost(OutputStream &OS, OutputFlags Flags) const { + OS << "["; + outputDimensionsImpl(OS, Flags); + OS << "]"; + + ElementType->outputPost(OS, Flags); +} + +void SymbolNode::output(OutputStream &OS, OutputFlags Flags) const { + Name->output(OS, Flags); +} + +void FunctionSymbolNode::output(OutputStream &OS, OutputFlags Flags) const { + Signature->outputPre(OS, Flags); + outputSpaceIfNecessary(OS); + Name->output(OS, Flags); + Signature->outputPost(OS, Flags); +} + +void VariableSymbolNode::output(OutputStream &OS, 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) + OS << AccessSpec << ": "; + if (!(Flags & OF_NoMemberType) && IsStatic) + OS << "static "; + + if (Type) { + Type->outputPre(OS, Flags); + outputSpaceIfNecessary(OS); + } + Name->output(OS, Flags); + if (Type) + Type->outputPost(OS, Flags); +} + +void CustomTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const { + Identifier->output(OS, Flags); +} +void CustomTypeNode::outputPost(OutputStream &OS, OutputFlags Flags) const {} + +void QualifiedNameNode::output(OutputStream &OS, OutputFlags Flags) const { + Components->output(OS, Flags, "::"); +} + +void RttiBaseClassDescriptorNode::output(OutputStream &OS, + OutputFlags Flags) const { + OS << "`RTTI Base Class Descriptor at ("; + OS << NVOffset << ", " << VBPtrOffset << ", " << VBTableOffset << ", " + << this->Flags; + OS << ")'"; +} + +void LocalStaticGuardVariableNode::output(OutputStream &OS, + OutputFlags Flags) const { + Name->output(OS, Flags); +} + +void VcallThunkIdentifierNode::output(OutputStream &OS, + OutputFlags Flags) const { + OS << "`vcall'{" << OffsetInVTable << ", {flat}}"; +} + +void SpecialTableSymbolNode::output(OutputStream &OS, OutputFlags Flags) const { + outputQualifiers(OS, Quals, false, true); + Name->output(OS, Flags); + if (TargetName) { + OS << "{for `"; + TargetName->output(OS, Flags); + OS << "'}"; + } + return; +} diff --git a/external/llvm/include/llvm/Demangle/Demangle.h b/external/llvm/include/llvm/Demangle/Demangle.h new file mode 100644 index 000000000..b4006a067 --- /dev/null +++ b/external/llvm/include/llvm/Demangle/Demangle.h @@ -0,0 +1,124 @@ +//===--- Demangle.h ---------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEMANGLE_DEMANGLE_H +#define LLVM_DEMANGLE_DEMANGLE_H + +#include +#include + +namespace llvm { +/// This is a llvm local version of __cxa_demangle. Other than the name and +/// being in the llvm namespace it is identical. +/// +/// The mangled_name is demangled into buf and returned. If the buffer is not +/// large enough, realloc is used to expand it. +/// +/// The *status will be set to a value from the following enumeration +enum : int { + demangle_unknown_error = -4, + demangle_invalid_args = -3, + demangle_invalid_mangled_name = -2, + demangle_memory_alloc_failure = -1, + demangle_success = 0, +}; + +char *itaniumDemangle(const char *mangled_name, char *buf, size_t *n, + int *status); + + +enum MSDemangleFlags { + MSDF_None = 0, + MSDF_DumpBackrefs = 1 << 0, + MSDF_NoAccessSpecifier = 1 << 1, + MSDF_NoCallingConvention = 1 << 2, + MSDF_NoReturnType = 1 << 3, + MSDF_NoMemberType = 1 << 4, +}; + +/// Demangles the Microsoft symbol pointed at by mangled_name and returns it. +/// Returns a pointer to the start of a null-terminated demangled string on +/// success, or nullptr on error. +/// If n_read is non-null and demangling was successful, it receives how many +/// bytes of the input string were consumed. +/// buf can point to a *n_buf bytes large buffer where the demangled name is +/// stored. If the buffer is too small, it is grown with realloc(). If buf is +/// nullptr, then this malloc()s memory for the result. +/// *n_buf stores the size of buf on input if buf is non-nullptr, and it +/// receives the size of the demangled string on output if n_buf is not nullptr. +/// status receives one of the demangle_ enum entries above if it's not nullptr. +/// Flags controls various details of the demangled representation. +char *microsoftDemangle(const char *mangled_name, size_t *n_read, + char *buf, size_t *n_buf, + int *status, MSDemangleFlags Flags = MSDF_None); + +/// Attempt to demangle a string using different demangling schemes. +/// The function uses heuristics to determine which demangling scheme to use. +/// \param MangledName - reference to string to demangle. +/// \returns - the demangled string, or a copy of the input string if no +/// demangling occurred. +std::string demangle(const std::string &MangledName); + +/// "Partial" demangler. This supports demangling a string into an AST +/// (typically an intermediate stage in itaniumDemangle) and querying certain +/// properties or partially printing the demangled name. +struct ItaniumPartialDemangler { + ItaniumPartialDemangler(); + + ItaniumPartialDemangler(ItaniumPartialDemangler &&Other); + ItaniumPartialDemangler &operator=(ItaniumPartialDemangler &&Other); + + /// Demangle into an AST. Subsequent calls to the rest of the member functions + /// implicitly operate on the AST this produces. + /// \return true on error, false otherwise + bool partialDemangle(const char *MangledName); + + /// Just print the entire mangled name into Buf. Buf and N behave like the + /// second and third parameters to itaniumDemangle. + char *finishDemangle(char *Buf, size_t *N) const; + + /// Get the base name of a function. This doesn't include trailing template + /// arguments, ie for "a::b" this function returns "b". + char *getFunctionBaseName(char *Buf, size_t *N) const; + + /// Get the context name for a function. For "a::b::c", this function returns + /// "a::b". + char *getFunctionDeclContextName(char *Buf, size_t *N) const; + + /// Get the entire name of this function. + char *getFunctionName(char *Buf, size_t *N) const; + + /// Get the parameters for this function. + char *getFunctionParameters(char *Buf, size_t *N) const; + char *getFunctionReturnType(char *Buf, size_t *N) const; + + /// If this function has any any cv or reference qualifiers. These imply that + /// the function is a non-static member function. + bool hasFunctionQualifiers() const; + + /// If this symbol describes a constructor or destructor. + bool isCtorOrDtor() const; + + /// If this symbol describes a function. + bool isFunction() const; + + /// If this symbol describes a variable. + bool isData() const; + + /// If this symbol is a . These are generally implicitly + /// generated by the implementation, such as vtables and typeinfo names. + bool isSpecialName() const; + + ~ItaniumPartialDemangler(); +private: + void *RootNode; + void *Context; +}; +} // namespace llvm + +#endif diff --git a/external/llvm/include/llvm/Demangle/DemangleConfig.h b/external/llvm/include/llvm/Demangle/DemangleConfig.h new file mode 100644 index 000000000..b7b7dbd24 --- /dev/null +++ b/external/llvm/include/llvm/Demangle/DemangleConfig.h @@ -0,0 +1,92 @@ +//===--- DemangleConfig.h ---------------------------------------*- C++ -*-===// +// +// 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 contains a variety of feature test macros copied from +// include/llvm/Support/Compiler.h so that LLVMDemangle does not need to take +// a dependency on LLVMSupport. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEMANGLE_COMPILER_H +#define LLVM_DEMANGLE_COMPILER_H + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#ifndef __has_cpp_attribute +#define __has_cpp_attribute(x) 0 +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#ifndef DEMANGLE_GNUC_PREREQ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) +#define DEMANGLE_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \ + ((maj) << 20) + ((min) << 10) + (patch)) +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) +#define DEMANGLE_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10)) +#else +#define DEMANGLE_GNUC_PREREQ(maj, min, patch) 0 +#endif +#endif + +#if __has_attribute(used) || DEMANGLE_GNUC_PREREQ(3, 1, 0) +#define DEMANGLE_ATTRIBUTE_USED __attribute__((__used__)) +#else +#define DEMANGLE_ATTRIBUTE_USED +#endif + +#if __has_builtin(__builtin_unreachable) || DEMANGLE_GNUC_PREREQ(4, 5, 0) +#define DEMANGLE_UNREACHABLE __builtin_unreachable() +#elif defined(_MSC_VER) +#define DEMANGLE_UNREACHABLE __assume(false) +#else +#define DEMANGLE_UNREACHABLE +#endif + +#if __has_attribute(noinline) || DEMANGLE_GNUC_PREREQ(3, 4, 0) +#define DEMANGLE_ATTRIBUTE_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) +#define DEMANGLE_ATTRIBUTE_NOINLINE __declspec(noinline) +#else +#define DEMANGLE_ATTRIBUTE_NOINLINE +#endif + +#if !defined(NDEBUG) +#define DEMANGLE_DUMP_METHOD DEMANGLE_ATTRIBUTE_NOINLINE DEMANGLE_ATTRIBUTE_USED +#else +#define DEMANGLE_DUMP_METHOD DEMANGLE_ATTRIBUTE_NOINLINE +#endif + +#if __cplusplus > 201402L && __has_cpp_attribute(fallthrough) +#define DEMANGLE_FALLTHROUGH [[fallthrough]] +#elif __has_cpp_attribute(gnu::fallthrough) +#define DEMANGLE_FALLTHROUGH [[gnu::fallthrough]] +#elif !__cplusplus +// Workaround for llvm.org/PR23435, since clang 3.6 and below emit a spurious +// error when __has_cpp_attribute is given a scoped attribute in C mode. +#define DEMANGLE_FALLTHROUGH +#elif __has_cpp_attribute(clang::fallthrough) +#define DEMANGLE_FALLTHROUGH [[clang::fallthrough]] +#else +#define DEMANGLE_FALLTHROUGH +#endif + +#define DEMANGLE_NAMESPACE_BEGIN namespace llvm { namespace itanium_demangle { +#define DEMANGLE_NAMESPACE_END } } + +#endif diff --git a/external/llvm/include/llvm/Demangle/ItaniumDemangle.h b/external/llvm/include/llvm/Demangle/ItaniumDemangle.h new file mode 100644 index 000000000..6ab873218 --- /dev/null +++ b/external/llvm/include/llvm/Demangle/ItaniumDemangle.h @@ -0,0 +1,5574 @@ +//===------------------------- ItaniumDemangle.h ----------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Generic itanium demangler library. This file has two byte-per-byte identical +// copies in the source tree, one in libcxxabi, and the other in llvm. +// +//===----------------------------------------------------------------------===// + +#ifndef DEMANGLE_ITANIUMDEMANGLE_H +#define DEMANGLE_ITANIUMDEMANGLE_H + +// FIXME: (possibly) incomplete list of features that clang mangles that this +// file does not yet support: +// - C++ modules TS + +#include "DemangleConfig.h" +#include "StringView.h" +#include "Utility.h" +#include +#include +#include +#include +#include +#include +#include + +#define FOR_EACH_NODE_KIND(X) \ + X(NodeArrayNode) \ + X(DotSuffix) \ + X(VendorExtQualType) \ + X(QualType) \ + X(ConversionOperatorType) \ + X(PostfixQualifiedType) \ + X(ElaboratedTypeSpefType) \ + X(NameType) \ + X(AbiTagAttr) \ + X(EnableIfAttr) \ + X(ObjCProtoName) \ + X(PointerType) \ + X(ReferenceType) \ + X(PointerToMemberType) \ + X(ArrayType) \ + X(FunctionType) \ + X(NoexceptSpec) \ + X(DynamicExceptionSpec) \ + X(FunctionEncoding) \ + X(LiteralOperator) \ + X(SpecialName) \ + X(CtorVtableSpecialName) \ + X(QualifiedName) \ + X(NestedName) \ + X(LocalName) \ + X(VectorType) \ + X(PixelVectorType) \ + X(SyntheticTemplateParamName) \ + X(TypeTemplateParamDecl) \ + X(NonTypeTemplateParamDecl) \ + X(TemplateTemplateParamDecl) \ + X(TemplateParamPackDecl) \ + X(ParameterPack) \ + X(TemplateArgumentPack) \ + X(ParameterPackExpansion) \ + X(TemplateArgs) \ + X(ForwardTemplateReference) \ + X(NameWithTemplateArgs) \ + X(GlobalQualifiedName) \ + X(StdQualifiedName) \ + X(ExpandedSpecialSubstitution) \ + X(SpecialSubstitution) \ + X(CtorDtorName) \ + X(DtorName) \ + X(UnnamedTypeName) \ + X(ClosureTypeName) \ + X(StructuredBindingName) \ + X(BinaryExpr) \ + X(ArraySubscriptExpr) \ + X(PostfixExpr) \ + X(ConditionalExpr) \ + X(MemberExpr) \ + X(EnclosingExpr) \ + X(CastExpr) \ + X(SizeofParamPackExpr) \ + X(CallExpr) \ + X(NewExpr) \ + X(DeleteExpr) \ + X(PrefixExpr) \ + X(FunctionParam) \ + X(ConversionExpr) \ + X(InitListExpr) \ + X(FoldExpr) \ + X(ThrowExpr) \ + X(UUIDOfExpr) \ + X(BoolExpr) \ + X(StringLiteral) \ + X(LambdaExpr) \ + X(EnumLiteral) \ + X(IntegerLiteral) \ + X(FloatLiteral) \ + X(DoubleLiteral) \ + X(LongDoubleLiteral) \ + X(BracedExpr) \ + X(BracedRangeExpr) + +DEMANGLE_NAMESPACE_BEGIN + +// Base class of all AST nodes. The AST is built by the parser, then is +// traversed by the printLeft/Right functions to produce a demangled string. +class Node { +public: + enum Kind : unsigned char { +#define ENUMERATOR(NodeKind) K ## NodeKind, + FOR_EACH_NODE_KIND(ENUMERATOR) +#undef ENUMERATOR + }; + + /// Three-way bool to track a cached value. Unknown is possible if this node + /// has an unexpanded parameter pack below it that may affect this cache. + enum class Cache : unsigned char { Yes, No, Unknown, }; + +private: + Kind K; + + // FIXME: Make these protected. +public: + /// Tracks if this node has a component on its right side, in which case we + /// need to call printRight. + Cache RHSComponentCache; + + /// Track if this node is a (possibly qualified) array type. This can affect + /// how we format the output string. + Cache ArrayCache; + + /// Track if this node is a (possibly qualified) function type. This can + /// affect how we format the output string. + Cache FunctionCache; + +public: + Node(Kind K_, Cache RHSComponentCache_ = Cache::No, + Cache ArrayCache_ = Cache::No, Cache FunctionCache_ = Cache::No) + : K(K_), RHSComponentCache(RHSComponentCache_), ArrayCache(ArrayCache_), + FunctionCache(FunctionCache_) {} + + /// Visit the most-derived object corresponding to this object. + template void visit(Fn F) const; + + // The following function is provided by all derived classes: + // + // Call F with arguments that, when passed to the constructor of this node, + // would construct an equivalent node. + //template void match(Fn F) const; + + bool hasRHSComponent(OutputStream &S) const { + if (RHSComponentCache != Cache::Unknown) + return RHSComponentCache == Cache::Yes; + return hasRHSComponentSlow(S); + } + + bool hasArray(OutputStream &S) const { + if (ArrayCache != Cache::Unknown) + return ArrayCache == Cache::Yes; + return hasArraySlow(S); + } + + bool hasFunction(OutputStream &S) const { + if (FunctionCache != Cache::Unknown) + return FunctionCache == Cache::Yes; + return hasFunctionSlow(S); + } + + Kind getKind() const { return K; } + + virtual bool hasRHSComponentSlow(OutputStream &) const { return false; } + virtual bool hasArraySlow(OutputStream &) const { return false; } + virtual bool hasFunctionSlow(OutputStream &) const { return false; } + + // Dig through "glue" nodes like ParameterPack and ForwardTemplateReference to + // get at a node that actually represents some concrete syntax. + virtual const Node *getSyntaxNode(OutputStream &) const { + return this; + } + + void print(OutputStream &S) const { + printLeft(S); + if (RHSComponentCache != Cache::No) + printRight(S); + } + + // Print the "left" side of this Node into OutputStream. + virtual void printLeft(OutputStream &) const = 0; + + // Print the "right". This distinction is necessary to represent C++ types + // that appear on the RHS of their subtype, such as arrays or functions. + // Since most types don't have such a component, provide a default + // implementation. + virtual void printRight(OutputStream &) const {} + + virtual StringView getBaseName() const { return StringView(); } + + // Silence compiler warnings, this dtor will never be called. + virtual ~Node() = default; + +#ifndef NDEBUG + DEMANGLE_DUMP_METHOD void dump() const; +#endif +}; + +class NodeArray { + Node **Elements; + size_t NumElements; + +public: + NodeArray() : Elements(nullptr), NumElements(0) {} + NodeArray(Node **Elements_, size_t NumElements_) + : Elements(Elements_), NumElements(NumElements_) {} + + bool empty() const { return NumElements == 0; } + size_t size() const { return NumElements; } + + Node **begin() const { return Elements; } + Node **end() const { return Elements + NumElements; } + + Node *operator[](size_t Idx) const { return Elements[Idx]; } + + void printWithComma(OutputStream &S) const { + bool FirstElement = true; + for (size_t Idx = 0; Idx != NumElements; ++Idx) { + size_t BeforeComma = S.getCurrentPosition(); + if (!FirstElement) + S += ", "; + size_t AfterComma = S.getCurrentPosition(); + Elements[Idx]->print(S); + + // Elements[Idx] is an empty parameter pack expansion, we should erase the + // comma we just printed. + if (AfterComma == S.getCurrentPosition()) { + S.setCurrentPosition(BeforeComma); + continue; + } + + FirstElement = false; + } + } +}; + +struct NodeArrayNode : Node { + NodeArray Array; + NodeArrayNode(NodeArray Array_) : Node(KNodeArrayNode), Array(Array_) {} + + template void match(Fn F) const { F(Array); } + + void printLeft(OutputStream &S) const override { + Array.printWithComma(S); + } +}; + +class DotSuffix final : public Node { + const Node *Prefix; + const StringView Suffix; + +public: + DotSuffix(const Node *Prefix_, StringView Suffix_) + : Node(KDotSuffix), Prefix(Prefix_), Suffix(Suffix_) {} + + template void match(Fn F) const { F(Prefix, Suffix); } + + void printLeft(OutputStream &s) const override { + Prefix->print(s); + s += " ("; + s += Suffix; + s += ")"; + } +}; + +class VendorExtQualType final : public Node { + const Node *Ty; + StringView Ext; + +public: + VendorExtQualType(const Node *Ty_, StringView Ext_) + : Node(KVendorExtQualType), Ty(Ty_), Ext(Ext_) {} + + template void match(Fn F) const { F(Ty, Ext); } + + void printLeft(OutputStream &S) const override { + Ty->print(S); + S += " "; + S += Ext; + } +}; + +enum FunctionRefQual : unsigned char { + FrefQualNone, + FrefQualLValue, + FrefQualRValue, +}; + +enum Qualifiers { + QualNone = 0, + QualConst = 0x1, + QualVolatile = 0x2, + QualRestrict = 0x4, +}; + +inline Qualifiers operator|=(Qualifiers &Q1, Qualifiers Q2) { + return Q1 = static_cast(Q1 | Q2); +} + +class QualType final : public Node { +protected: + const Qualifiers Quals; + const Node *Child; + + void printQuals(OutputStream &S) const { + if (Quals & QualConst) + S += " const"; + if (Quals & QualVolatile) + S += " volatile"; + if (Quals & QualRestrict) + S += " restrict"; + } + +public: + QualType(const Node *Child_, Qualifiers Quals_) + : Node(KQualType, Child_->RHSComponentCache, + Child_->ArrayCache, Child_->FunctionCache), + Quals(Quals_), Child(Child_) {} + + template void match(Fn F) const { F(Child, Quals); } + + bool hasRHSComponentSlow(OutputStream &S) const override { + return Child->hasRHSComponent(S); + } + bool hasArraySlow(OutputStream &S) const override { + return Child->hasArray(S); + } + bool hasFunctionSlow(OutputStream &S) const override { + return Child->hasFunction(S); + } + + void printLeft(OutputStream &S) const override { + Child->printLeft(S); + printQuals(S); + } + + void printRight(OutputStream &S) const override { Child->printRight(S); } +}; + +class ConversionOperatorType final : public Node { + const Node *Ty; + +public: + ConversionOperatorType(const Node *Ty_) + : Node(KConversionOperatorType), Ty(Ty_) {} + + template void match(Fn F) const { F(Ty); } + + void printLeft(OutputStream &S) const override { + S += "operator "; + Ty->print(S); + } +}; + +class PostfixQualifiedType final : public Node { + const Node *Ty; + const StringView Postfix; + +public: + PostfixQualifiedType(Node *Ty_, StringView Postfix_) + : Node(KPostfixQualifiedType), Ty(Ty_), Postfix(Postfix_) {} + + template void match(Fn F) const { F(Ty, Postfix); } + + void printLeft(OutputStream &s) const override { + Ty->printLeft(s); + s += Postfix; + } +}; + +class NameType final : public Node { + const StringView Name; + +public: + NameType(StringView Name_) : Node(KNameType), Name(Name_) {} + + template void match(Fn F) const { F(Name); } + + StringView getName() const { return Name; } + StringView getBaseName() const override { return Name; } + + void printLeft(OutputStream &s) const override { s += Name; } +}; + +class ElaboratedTypeSpefType : public Node { + StringView Kind; + Node *Child; +public: + ElaboratedTypeSpefType(StringView Kind_, Node *Child_) + : Node(KElaboratedTypeSpefType), Kind(Kind_), Child(Child_) {} + + template void match(Fn F) const { F(Kind, Child); } + + void printLeft(OutputStream &S) const override { + S += Kind; + S += ' '; + Child->print(S); + } +}; + +struct AbiTagAttr : Node { + Node *Base; + StringView Tag; + + AbiTagAttr(Node* Base_, StringView Tag_) + : Node(KAbiTagAttr, Base_->RHSComponentCache, + Base_->ArrayCache, Base_->FunctionCache), + Base(Base_), Tag(Tag_) {} + + template void match(Fn F) const { F(Base, Tag); } + + void printLeft(OutputStream &S) const override { + Base->printLeft(S); + S += "[abi:"; + S += Tag; + S += "]"; + } +}; + +class EnableIfAttr : public Node { + NodeArray Conditions; +public: + EnableIfAttr(NodeArray Conditions_) + : Node(KEnableIfAttr), Conditions(Conditions_) {} + + template void match(Fn F) const { F(Conditions); } + + void printLeft(OutputStream &S) const override { + S += " [enable_if:"; + Conditions.printWithComma(S); + S += ']'; + } +}; + +class ObjCProtoName : public Node { + const Node *Ty; + StringView Protocol; + + friend class PointerType; + +public: + ObjCProtoName(const Node *Ty_, StringView Protocol_) + : Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {} + + template void match(Fn F) const { F(Ty, Protocol); } + + bool isObjCObject() const { + return Ty->getKind() == KNameType && + static_cast(Ty)->getName() == "objc_object"; + } + + void printLeft(OutputStream &S) const override { + Ty->print(S); + S += "<"; + S += Protocol; + S += ">"; + } +}; + +class PointerType final : public Node { + const Node *Pointee; + +public: + PointerType(const Node *Pointee_) + : Node(KPointerType, Pointee_->RHSComponentCache), + Pointee(Pointee_) {} + + template void match(Fn F) const { F(Pointee); } + + bool hasRHSComponentSlow(OutputStream &S) const override { + return Pointee->hasRHSComponent(S); + } + + void printLeft(OutputStream &s) const override { + // We rewrite objc_object* into id. + if (Pointee->getKind() != KObjCProtoName || + !static_cast(Pointee)->isObjCObject()) { + Pointee->printLeft(s); + if (Pointee->hasArray(s)) + s += " "; + if (Pointee->hasArray(s) || Pointee->hasFunction(s)) + s += "("; + s += "*"; + } else { + const auto *objcProto = static_cast(Pointee); + s += "id<"; + s += objcProto->Protocol; + s += ">"; + } + } + + void printRight(OutputStream &s) const override { + if (Pointee->getKind() != KObjCProtoName || + !static_cast(Pointee)->isObjCObject()) { + if (Pointee->hasArray(s) || Pointee->hasFunction(s)) + s += ")"; + Pointee->printRight(s); + } + } +}; + +enum class ReferenceKind { + LValue, + RValue, +}; + +// Represents either a LValue or an RValue reference type. +class ReferenceType : public Node { + const Node *Pointee; + ReferenceKind RK; + + mutable bool Printing = false; + + // Dig through any refs to refs, collapsing the ReferenceTypes as we go. The + // rule here is rvalue ref to rvalue ref collapses to a rvalue ref, and any + // other combination collapses to a lvalue ref. + std::pair collapse(OutputStream &S) const { + auto SoFar = std::make_pair(RK, Pointee); + for (;;) { + const Node *SN = SoFar.second->getSyntaxNode(S); + if (SN->getKind() != KReferenceType) + break; + auto *RT = static_cast(SN); + SoFar.second = RT->Pointee; + SoFar.first = std::min(SoFar.first, RT->RK); + } + return SoFar; + } + +public: + ReferenceType(const Node *Pointee_, ReferenceKind RK_) + : Node(KReferenceType, Pointee_->RHSComponentCache), + Pointee(Pointee_), RK(RK_) {} + + template void match(Fn F) const { F(Pointee, RK); } + + bool hasRHSComponentSlow(OutputStream &S) const override { + return Pointee->hasRHSComponent(S); + } + + void printLeft(OutputStream &s) const override { + if (Printing) + return; + SwapAndRestore SavePrinting(Printing, true); + std::pair Collapsed = collapse(s); + Collapsed.second->printLeft(s); + if (Collapsed.second->hasArray(s)) + s += " "; + if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s)) + s += "("; + + s += (Collapsed.first == ReferenceKind::LValue ? "&" : "&&"); + } + void printRight(OutputStream &s) const override { + if (Printing) + return; + SwapAndRestore SavePrinting(Printing, true); + std::pair Collapsed = collapse(s); + if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s)) + s += ")"; + Collapsed.second->printRight(s); + } +}; + +class PointerToMemberType final : public Node { + const Node *ClassType; + const Node *MemberType; + +public: + PointerToMemberType(const Node *ClassType_, const Node *MemberType_) + : Node(KPointerToMemberType, MemberType_->RHSComponentCache), + ClassType(ClassType_), MemberType(MemberType_) {} + + template void match(Fn F) const { F(ClassType, MemberType); } + + bool hasRHSComponentSlow(OutputStream &S) const override { + return MemberType->hasRHSComponent(S); + } + + void printLeft(OutputStream &s) const override { + MemberType->printLeft(s); + if (MemberType->hasArray(s) || MemberType->hasFunction(s)) + s += "("; + else + s += " "; + ClassType->print(s); + s += "::*"; + } + + void printRight(OutputStream &s) const override { + if (MemberType->hasArray(s) || MemberType->hasFunction(s)) + s += ")"; + MemberType->printRight(s); + } +}; + +class ArrayType final : public Node { + const Node *Base; + Node *Dimension; + +public: + ArrayType(const Node *Base_, Node *Dimension_) + : Node(KArrayType, + /*RHSComponentCache=*/Cache::Yes, + /*ArrayCache=*/Cache::Yes), + Base(Base_), Dimension(Dimension_) {} + + template void match(Fn F) const { F(Base, Dimension); } + + bool hasRHSComponentSlow(OutputStream &) const override { return true; } + bool hasArraySlow(OutputStream &) const override { return true; } + + void printLeft(OutputStream &S) const override { Base->printLeft(S); } + + void printRight(OutputStream &S) const override { + if (S.back() != ']') + S += " "; + S += "["; + if (Dimension) + Dimension->print(S); + S += "]"; + Base->printRight(S); + } +}; + +class FunctionType final : public Node { + const Node *Ret; + NodeArray Params; + Qualifiers CVQuals; + FunctionRefQual RefQual; + const Node *ExceptionSpec; + +public: + FunctionType(const Node *Ret_, NodeArray Params_, Qualifiers CVQuals_, + FunctionRefQual RefQual_, const Node *ExceptionSpec_) + : Node(KFunctionType, + /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No, + /*FunctionCache=*/Cache::Yes), + Ret(Ret_), Params(Params_), CVQuals(CVQuals_), RefQual(RefQual_), + ExceptionSpec(ExceptionSpec_) {} + + template void match(Fn F) const { + F(Ret, Params, CVQuals, RefQual, ExceptionSpec); + } + + bool hasRHSComponentSlow(OutputStream &) const override { return true; } + bool hasFunctionSlow(OutputStream &) const override { return true; } + + // Handle C++'s ... quirky decl grammar by using the left & right + // distinction. Consider: + // int (*f(float))(char) {} + // f is a function that takes a float and returns a pointer to a function + // that takes a char and returns an int. If we're trying to print f, start + // by printing out the return types's left, then print our parameters, then + // finally print right of the return type. + void printLeft(OutputStream &S) const override { + Ret->printLeft(S); + S += " "; + } + + void printRight(OutputStream &S) const override { + S += "("; + Params.printWithComma(S); + S += ")"; + Ret->printRight(S); + + if (CVQuals & QualConst) + S += " const"; + if (CVQuals & QualVolatile) + S += " volatile"; + if (CVQuals & QualRestrict) + S += " restrict"; + + if (RefQual == FrefQualLValue) + S += " &"; + else if (RefQual == FrefQualRValue) + S += " &&"; + + if (ExceptionSpec != nullptr) { + S += ' '; + ExceptionSpec->print(S); + } + } +}; + +class NoexceptSpec : public Node { + const Node *E; +public: + NoexceptSpec(const Node *E_) : Node(KNoexceptSpec), E(E_) {} + + template void match(Fn F) const { F(E); } + + void printLeft(OutputStream &S) const override { + S += "noexcept("; + E->print(S); + S += ")"; + } +}; + +class DynamicExceptionSpec : public Node { + NodeArray Types; +public: + DynamicExceptionSpec(NodeArray Types_) + : Node(KDynamicExceptionSpec), Types(Types_) {} + + template void match(Fn F) const { F(Types); } + + void printLeft(OutputStream &S) const override { + S += "throw("; + Types.printWithComma(S); + S += ')'; + } +}; + +class FunctionEncoding final : public Node { + const Node *Ret; + const Node *Name; + NodeArray Params; + const Node *Attrs; + Qualifiers CVQuals; + FunctionRefQual RefQual; + +public: + FunctionEncoding(const Node *Ret_, const Node *Name_, NodeArray Params_, + const Node *Attrs_, Qualifiers CVQuals_, + FunctionRefQual RefQual_) + : Node(KFunctionEncoding, + /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No, + /*FunctionCache=*/Cache::Yes), + Ret(Ret_), Name(Name_), Params(Params_), Attrs(Attrs_), + CVQuals(CVQuals_), RefQual(RefQual_) {} + + template void match(Fn F) const { + F(Ret, Name, Params, Attrs, CVQuals, RefQual); + } + + Qualifiers getCVQuals() const { return CVQuals; } + FunctionRefQual getRefQual() const { return RefQual; } + NodeArray getParams() const { return Params; } + const Node *getReturnType() const { return Ret; } + + bool hasRHSComponentSlow(OutputStream &) const override { return true; } + bool hasFunctionSlow(OutputStream &) const override { return true; } + + const Node *getName() const { return Name; } + + void printLeft(OutputStream &S) const override { + if (Ret) { + Ret->printLeft(S); + if (!Ret->hasRHSComponent(S)) + S += " "; + } + Name->print(S); + } + + void printRight(OutputStream &S) const override { + S += "("; + Params.printWithComma(S); + S += ")"; + if (Ret) + Ret->printRight(S); + + if (CVQuals & QualConst) + S += " const"; + if (CVQuals & QualVolatile) + S += " volatile"; + if (CVQuals & QualRestrict) + S += " restrict"; + + if (RefQual == FrefQualLValue) + S += " &"; + else if (RefQual == FrefQualRValue) + S += " &&"; + + if (Attrs != nullptr) + Attrs->print(S); + } +}; + +class LiteralOperator : public Node { + const Node *OpName; + +public: + LiteralOperator(const Node *OpName_) + : Node(KLiteralOperator), OpName(OpName_) {} + + template void match(Fn F) const { F(OpName); } + + void printLeft(OutputStream &S) const override { + S += "operator\"\" "; + OpName->print(S); + } +}; + +class SpecialName final : public Node { + const StringView Special; + const Node *Child; + +public: + SpecialName(StringView Special_, const Node *Child_) + : Node(KSpecialName), Special(Special_), Child(Child_) {} + + template void match(Fn F) const { F(Special, Child); } + + void printLeft(OutputStream &S) const override { + S += Special; + Child->print(S); + } +}; + +class CtorVtableSpecialName final : public Node { + const Node *FirstType; + const Node *SecondType; + +public: + CtorVtableSpecialName(const Node *FirstType_, const Node *SecondType_) + : Node(KCtorVtableSpecialName), + FirstType(FirstType_), SecondType(SecondType_) {} + + template void match(Fn F) const { F(FirstType, SecondType); } + + void printLeft(OutputStream &S) const override { + S += "construction vtable for "; + FirstType->print(S); + S += "-in-"; + SecondType->print(S); + } +}; + +struct NestedName : Node { + Node *Qual; + Node *Name; + + NestedName(Node *Qual_, Node *Name_) + : Node(KNestedName), Qual(Qual_), Name(Name_) {} + + template void match(Fn F) const { F(Qual, Name); } + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputStream &S) const override { + Qual->print(S); + S += "::"; + Name->print(S); + } +}; + +struct LocalName : Node { + Node *Encoding; + Node *Entity; + + LocalName(Node *Encoding_, Node *Entity_) + : Node(KLocalName), Encoding(Encoding_), Entity(Entity_) {} + + template void match(Fn F) const { F(Encoding, Entity); } + + void printLeft(OutputStream &S) const override { + Encoding->print(S); + S += "::"; + Entity->print(S); + } +}; + +class QualifiedName final : public Node { + // qualifier::name + const Node *Qualifier; + const Node *Name; + +public: + QualifiedName(const Node *Qualifier_, const Node *Name_) + : Node(KQualifiedName), Qualifier(Qualifier_), Name(Name_) {} + + template void match(Fn F) const { F(Qualifier, Name); } + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputStream &S) const override { + Qualifier->print(S); + S += "::"; + Name->print(S); + } +}; + +class VectorType final : public Node { + const Node *BaseType; + const Node *Dimension; + +public: + VectorType(const Node *BaseType_, Node *Dimension_) + : Node(KVectorType), BaseType(BaseType_), + Dimension(Dimension_) {} + + template void match(Fn F) const { F(BaseType, Dimension); } + + void printLeft(OutputStream &S) const override { + BaseType->print(S); + S += " vector["; + if (Dimension) + Dimension->print(S); + S += "]"; + } +}; + +class PixelVectorType final : public Node { + const Node *Dimension; + +public: + PixelVectorType(const Node *Dimension_) + : Node(KPixelVectorType), Dimension(Dimension_) {} + + template void match(Fn F) const { F(Dimension); } + + void printLeft(OutputStream &S) const override { + // FIXME: This should demangle as "vector pixel". + S += "pixel vector["; + Dimension->print(S); + S += "]"; + } +}; + +enum class TemplateParamKind { Type, NonType, Template }; + +/// An invented name for a template parameter for which we don't have a +/// corresponding template argument. +/// +/// This node is created when parsing the for a lambda with +/// explicit template arguments, which might be referenced in the parameter +/// types appearing later in the . +class SyntheticTemplateParamName final : public Node { + TemplateParamKind Kind; + unsigned Index; + +public: + SyntheticTemplateParamName(TemplateParamKind Kind_, unsigned Index_) + : Node(KSyntheticTemplateParamName), Kind(Kind_), Index(Index_) {} + + template void match(Fn F) const { F(Kind, Index); } + + void printLeft(OutputStream &S) const override { + switch (Kind) { + case TemplateParamKind::Type: + S += "$T"; + break; + case TemplateParamKind::NonType: + S += "$N"; + break; + case TemplateParamKind::Template: + S += "$TT"; + break; + } + if (Index > 0) + S << Index - 1; + } +}; + +/// A template type parameter declaration, 'typename T'. +class TypeTemplateParamDecl final : public Node { + Node *Name; + +public: + TypeTemplateParamDecl(Node *Name_) + : Node(KTypeTemplateParamDecl, Cache::Yes), Name(Name_) {} + + template void match(Fn F) const { F(Name); } + + void printLeft(OutputStream &S) const override { + S += "typename "; + } + + void printRight(OutputStream &S) const override { + Name->print(S); + } +}; + +/// A non-type template parameter declaration, 'int N'. +class NonTypeTemplateParamDecl final : public Node { + Node *Name; + Node *Type; + +public: + NonTypeTemplateParamDecl(Node *Name_, Node *Type_) + : Node(KNonTypeTemplateParamDecl, Cache::Yes), Name(Name_), Type(Type_) {} + + template void match(Fn F) const { F(Name, Type); } + + void printLeft(OutputStream &S) const override { + Type->printLeft(S); + if (!Type->hasRHSComponent(S)) + S += " "; + } + + void printRight(OutputStream &S) const override { + Name->print(S); + Type->printRight(S); + } +}; + +/// A template template parameter declaration, +/// 'template typename N'. +class TemplateTemplateParamDecl final : public Node { + Node *Name; + NodeArray Params; + +public: + TemplateTemplateParamDecl(Node *Name_, NodeArray Params_) + : Node(KTemplateTemplateParamDecl, Cache::Yes), Name(Name_), + Params(Params_) {} + + template void match(Fn F) const { F(Name, Params); } + + void printLeft(OutputStream &S) const override { + S += "template<"; + Params.printWithComma(S); + S += "> typename "; + } + + void printRight(OutputStream &S) const override { + Name->print(S); + } +}; + +/// A template parameter pack declaration, 'typename ...T'. +class TemplateParamPackDecl final : public Node { + Node *Param; + +public: + TemplateParamPackDecl(Node *Param_) + : Node(KTemplateParamPackDecl, Cache::Yes), Param(Param_) {} + + template void match(Fn F) const { F(Param); } + + void printLeft(OutputStream &S) const override { + Param->printLeft(S); + S += "..."; + } + + void printRight(OutputStream &S) const override { + Param->printRight(S); + } +}; + +/// An unexpanded parameter pack (either in the expression or type context). If +/// this AST is correct, this node will have a ParameterPackExpansion node above +/// it. +/// +/// This node is created when some are found that apply to an +/// , and is stored in the TemplateParams table. In order for this to +/// appear in the final AST, it has to referenced via a (ie, +/// T_). +class ParameterPack final : public Node { + NodeArray Data; + + // Setup OutputStream for a pack expansion unless we're already expanding one. + void initializePackExpansion(OutputStream &S) const { + if (S.CurrentPackMax == std::numeric_limits::max()) { + S.CurrentPackMax = static_cast(Data.size()); + S.CurrentPackIndex = 0; + } + } + +public: + ParameterPack(NodeArray Data_) : Node(KParameterPack), Data(Data_) { + ArrayCache = FunctionCache = RHSComponentCache = Cache::Unknown; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { + return P->ArrayCache == Cache::No; + })) + ArrayCache = Cache::No; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { + return P->FunctionCache == Cache::No; + })) + FunctionCache = Cache::No; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { + return P->RHSComponentCache == Cache::No; + })) + RHSComponentCache = Cache::No; + } + + template void match(Fn F) const { F(Data); } + + bool hasRHSComponentSlow(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasRHSComponent(S); + } + bool hasArraySlow(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasArray(S); + } + bool hasFunctionSlow(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasFunction(S); + } + const Node *getSyntaxNode(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() ? Data[Idx]->getSyntaxNode(S) : this; + } + + void printLeft(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + if (Idx < Data.size()) + Data[Idx]->printLeft(S); + } + void printRight(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + if (Idx < Data.size()) + Data[Idx]->printRight(S); + } +}; + +/// A variadic template argument. This node represents an occurrence of +/// JE in some . It isn't itself unexpanded, unless +/// one of it's Elements is. The parser inserts a ParameterPack into the +/// TemplateParams table if the this pack belongs to apply to an +/// . +class TemplateArgumentPack final : public Node { + NodeArray Elements; +public: + TemplateArgumentPack(NodeArray Elements_) + : Node(KTemplateArgumentPack), Elements(Elements_) {} + + template void match(Fn F) const { F(Elements); } + + NodeArray getElements() const { return Elements; } + + void printLeft(OutputStream &S) const override { + Elements.printWithComma(S); + } +}; + +/// A pack expansion. Below this node, there are some unexpanded ParameterPacks +/// which each have Child->ParameterPackSize elements. +class ParameterPackExpansion final : public Node { + const Node *Child; + +public: + ParameterPackExpansion(const Node *Child_) + : Node(KParameterPackExpansion), Child(Child_) {} + + template void match(Fn F) const { F(Child); } + + const Node *getChild() const { return Child; } + + void printLeft(OutputStream &S) const override { + constexpr unsigned Max = std::numeric_limits::max(); + SwapAndRestore SavePackIdx(S.CurrentPackIndex, Max); + SwapAndRestore SavePackMax(S.CurrentPackMax, Max); + size_t StreamPos = S.getCurrentPosition(); + + // Print the first element in the pack. If Child contains a ParameterPack, + // it will set up S.CurrentPackMax and print the first element. + Child->print(S); + + // No ParameterPack was found in Child. This can occur if we've found a pack + // expansion on a . + if (S.CurrentPackMax == Max) { + S += "..."; + return; + } + + // We found a ParameterPack, but it has no elements. Erase whatever we may + // of printed. + if (S.CurrentPackMax == 0) { + S.setCurrentPosition(StreamPos); + return; + } + + // Else, iterate through the rest of the elements in the pack. + for (unsigned I = 1, E = S.CurrentPackMax; I < E; ++I) { + S += ", "; + S.CurrentPackIndex = I; + Child->print(S); + } + } +}; + +class TemplateArgs final : public Node { + NodeArray Params; + +public: + TemplateArgs(NodeArray Params_) : Node(KTemplateArgs), Params(Params_) {} + + template void match(Fn F) const { F(Params); } + + NodeArray getParams() { return Params; } + + void printLeft(OutputStream &S) const override { + S += "<"; + Params.printWithComma(S); + if (S.back() == '>') + S += " "; + S += ">"; + } +}; + +/// A forward-reference to a template argument that was not known at the point +/// where the template parameter name was parsed in a mangling. +/// +/// This is created when demangling the name of a specialization of a +/// conversion function template: +/// +/// \code +/// struct A { +/// template operator T*(); +/// }; +/// \endcode +/// +/// When demangling a specialization of the conversion function template, we +/// encounter the name of the template (including the \c T) before we reach +/// the template argument list, so we cannot substitute the parameter name +/// for the corresponding argument while parsing. Instead, we create a +/// \c ForwardTemplateReference node that is resolved after we parse the +/// template arguments. +struct ForwardTemplateReference : Node { + size_t Index; + Node *Ref = nullptr; + + // If we're currently printing this node. It is possible (though invalid) for + // a forward template reference to refer to itself via a substitution. This + // creates a cyclic AST, which will stack overflow printing. To fix this, bail + // out if more than one print* function is active. + mutable bool Printing = false; + + ForwardTemplateReference(size_t Index_) + : Node(KForwardTemplateReference, Cache::Unknown, Cache::Unknown, + Cache::Unknown), + Index(Index_) {} + + // We don't provide a matcher for these, because the value of the node is + // not determined by its construction parameters, and it generally needs + // special handling. + template void match(Fn F) const = delete; + + bool hasRHSComponentSlow(OutputStream &S) const override { + if (Printing) + return false; + SwapAndRestore SavePrinting(Printing, true); + return Ref->hasRHSComponent(S); + } + bool hasArraySlow(OutputStream &S) const override { + if (Printing) + return false; + SwapAndRestore SavePrinting(Printing, true); + return Ref->hasArray(S); + } + bool hasFunctionSlow(OutputStream &S) const override { + if (Printing) + return false; + SwapAndRestore SavePrinting(Printing, true); + return Ref->hasFunction(S); + } + const Node *getSyntaxNode(OutputStream &S) const override { + if (Printing) + return this; + SwapAndRestore SavePrinting(Printing, true); + return Ref->getSyntaxNode(S); + } + + void printLeft(OutputStream &S) const override { + if (Printing) + return; + SwapAndRestore SavePrinting(Printing, true); + Ref->printLeft(S); + } + void printRight(OutputStream &S) const override { + if (Printing) + return; + SwapAndRestore SavePrinting(Printing, true); + Ref->printRight(S); + } +}; + +struct NameWithTemplateArgs : Node { + // name + Node *Name; + Node *TemplateArgs; + + NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_) + : Node(KNameWithTemplateArgs), Name(Name_), TemplateArgs(TemplateArgs_) {} + + template void match(Fn F) const { F(Name, TemplateArgs); } + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputStream &S) const override { + Name->print(S); + TemplateArgs->print(S); + } +}; + +class GlobalQualifiedName final : public Node { + Node *Child; + +public: + GlobalQualifiedName(Node* Child_) + : Node(KGlobalQualifiedName), Child(Child_) {} + + template void match(Fn F) const { F(Child); } + + StringView getBaseName() const override { return Child->getBaseName(); } + + void printLeft(OutputStream &S) const override { + S += "::"; + Child->print(S); + } +}; + +struct StdQualifiedName : Node { + Node *Child; + + StdQualifiedName(Node *Child_) : Node(KStdQualifiedName), Child(Child_) {} + + template void match(Fn F) const { F(Child); } + + StringView getBaseName() const override { return Child->getBaseName(); } + + void printLeft(OutputStream &S) const override { + S += "std::"; + Child->print(S); + } +}; + +enum class SpecialSubKind { + allocator, + basic_string, + string, + istream, + ostream, + iostream, +}; + +class ExpandedSpecialSubstitution final : public Node { + SpecialSubKind SSK; + +public: + ExpandedSpecialSubstitution(SpecialSubKind SSK_) + : Node(KExpandedSpecialSubstitution), SSK(SSK_) {} + + template void match(Fn F) const { F(SSK); } + + StringView getBaseName() const override { + switch (SSK) { + case SpecialSubKind::allocator: + return StringView("allocator"); + case SpecialSubKind::basic_string: + return StringView("basic_string"); + case SpecialSubKind::string: + return StringView("basic_string"); + case SpecialSubKind::istream: + return StringView("basic_istream"); + case SpecialSubKind::ostream: + return StringView("basic_ostream"); + case SpecialSubKind::iostream: + return StringView("basic_iostream"); + } + DEMANGLE_UNREACHABLE; + } + + void printLeft(OutputStream &S) const override { + switch (SSK) { + case SpecialSubKind::allocator: + S += "std::allocator"; + break; + case SpecialSubKind::basic_string: + S += "std::basic_string"; + break; + case SpecialSubKind::string: + S += "std::basic_string, " + "std::allocator >"; + break; + case SpecialSubKind::istream: + S += "std::basic_istream >"; + break; + case SpecialSubKind::ostream: + S += "std::basic_ostream >"; + break; + case SpecialSubKind::iostream: + S += "std::basic_iostream >"; + break; + } + } +}; + +class SpecialSubstitution final : public Node { +public: + SpecialSubKind SSK; + + SpecialSubstitution(SpecialSubKind SSK_) + : Node(KSpecialSubstitution), SSK(SSK_) {} + + template void match(Fn F) const { F(SSK); } + + StringView getBaseName() const override { + switch (SSK) { + case SpecialSubKind::allocator: + return StringView("allocator"); + case SpecialSubKind::basic_string: + return StringView("basic_string"); + case SpecialSubKind::string: + return StringView("string"); + case SpecialSubKind::istream: + return StringView("istream"); + case SpecialSubKind::ostream: + return StringView("ostream"); + case SpecialSubKind::iostream: + return StringView("iostream"); + } + DEMANGLE_UNREACHABLE; + } + + void printLeft(OutputStream &S) const override { + switch (SSK) { + case SpecialSubKind::allocator: + S += "std::allocator"; + break; + case SpecialSubKind::basic_string: + S += "std::basic_string"; + break; + case SpecialSubKind::string: + S += "std::string"; + break; + case SpecialSubKind::istream: + S += "std::istream"; + break; + case SpecialSubKind::ostream: + S += "std::ostream"; + break; + case SpecialSubKind::iostream: + S += "std::iostream"; + break; + } + } +}; + +class CtorDtorName final : public Node { + const Node *Basename; + const bool IsDtor; + const int Variant; + +public: + CtorDtorName(const Node *Basename_, bool IsDtor_, int Variant_) + : Node(KCtorDtorName), Basename(Basename_), IsDtor(IsDtor_), + Variant(Variant_) {} + + template void match(Fn F) const { F(Basename, IsDtor, Variant); } + + void printLeft(OutputStream &S) const override { + if (IsDtor) + S += "~"; + S += Basename->getBaseName(); + } +}; + +class DtorName : public Node { + const Node *Base; + +public: + DtorName(const Node *Base_) : Node(KDtorName), Base(Base_) {} + + template void match(Fn F) const { F(Base); } + + void printLeft(OutputStream &S) const override { + S += "~"; + Base->printLeft(S); + } +}; + +class UnnamedTypeName : public Node { + const StringView Count; + +public: + UnnamedTypeName(StringView Count_) : Node(KUnnamedTypeName), Count(Count_) {} + + template void match(Fn F) const { F(Count); } + + void printLeft(OutputStream &S) const override { + S += "'unnamed"; + S += Count; + S += "\'"; + } +}; + +class ClosureTypeName : public Node { + NodeArray TemplateParams; + NodeArray Params; + StringView Count; + +public: + ClosureTypeName(NodeArray TemplateParams_, NodeArray Params_, + StringView Count_) + : Node(KClosureTypeName), TemplateParams(TemplateParams_), + Params(Params_), Count(Count_) {} + + template void match(Fn F) const { + F(TemplateParams, Params, Count); + } + + void printDeclarator(OutputStream &S) const { + if (!TemplateParams.empty()) { + S += "<"; + TemplateParams.printWithComma(S); + S += ">"; + } + S += "("; + Params.printWithComma(S); + S += ")"; + } + + void printLeft(OutputStream &S) const override { + S += "\'lambda"; + S += Count; + S += "\'"; + printDeclarator(S); + } +}; + +class StructuredBindingName : public Node { + NodeArray Bindings; +public: + StructuredBindingName(NodeArray Bindings_) + : Node(KStructuredBindingName), Bindings(Bindings_) {} + + template void match(Fn F) const { F(Bindings); } + + void printLeft(OutputStream &S) const override { + S += '['; + Bindings.printWithComma(S); + S += ']'; + } +}; + +// -- Expression Nodes -- + +class BinaryExpr : public Node { + const Node *LHS; + const StringView InfixOperator; + const Node *RHS; + +public: + BinaryExpr(const Node *LHS_, StringView InfixOperator_, const Node *RHS_) + : Node(KBinaryExpr), LHS(LHS_), InfixOperator(InfixOperator_), RHS(RHS_) { + } + + template void match(Fn F) const { F(LHS, InfixOperator, RHS); } + + void printLeft(OutputStream &S) const override { + // might be a template argument expression, then we need to disambiguate + // with parens. + if (InfixOperator == ">") + S += "("; + + S += "("; + LHS->print(S); + S += ") "; + S += InfixOperator; + S += " ("; + RHS->print(S); + S += ")"; + + if (InfixOperator == ">") + S += ")"; + } +}; + +class ArraySubscriptExpr : public Node { + const Node *Op1; + const Node *Op2; + +public: + ArraySubscriptExpr(const Node *Op1_, const Node *Op2_) + : Node(KArraySubscriptExpr), Op1(Op1_), Op2(Op2_) {} + + template void match(Fn F) const { F(Op1, Op2); } + + void printLeft(OutputStream &S) const override { + S += "("; + Op1->print(S); + S += ")["; + Op2->print(S); + S += "]"; + } +}; + +class PostfixExpr : public Node { + const Node *Child; + const StringView Operator; + +public: + PostfixExpr(const Node *Child_, StringView Operator_) + : Node(KPostfixExpr), Child(Child_), Operator(Operator_) {} + + template void match(Fn F) const { F(Child, Operator); } + + void printLeft(OutputStream &S) const override { + S += "("; + Child->print(S); + S += ")"; + S += Operator; + } +}; + +class ConditionalExpr : public Node { + const Node *Cond; + const Node *Then; + const Node *Else; + +public: + ConditionalExpr(const Node *Cond_, const Node *Then_, const Node *Else_) + : Node(KConditionalExpr), Cond(Cond_), Then(Then_), Else(Else_) {} + + template void match(Fn F) const { F(Cond, Then, Else); } + + void printLeft(OutputStream &S) const override { + S += "("; + Cond->print(S); + S += ") ? ("; + Then->print(S); + S += ") : ("; + Else->print(S); + S += ")"; + } +}; + +class MemberExpr : public Node { + const Node *LHS; + const StringView Kind; + const Node *RHS; + +public: + MemberExpr(const Node *LHS_, StringView Kind_, const Node *RHS_) + : Node(KMemberExpr), LHS(LHS_), Kind(Kind_), RHS(RHS_) {} + + template void match(Fn F) const { F(LHS, Kind, RHS); } + + void printLeft(OutputStream &S) const override { + LHS->print(S); + S += Kind; + RHS->print(S); + } +}; + +class EnclosingExpr : public Node { + const StringView Prefix; + const Node *Infix; + const StringView Postfix; + +public: + EnclosingExpr(StringView Prefix_, Node *Infix_, StringView Postfix_) + : Node(KEnclosingExpr), Prefix(Prefix_), Infix(Infix_), + Postfix(Postfix_) {} + + template void match(Fn F) const { F(Prefix, Infix, Postfix); } + + void printLeft(OutputStream &S) const override { + S += Prefix; + Infix->print(S); + S += Postfix; + } +}; + +class CastExpr : public Node { + // cast_kind(from) + const StringView CastKind; + const Node *To; + const Node *From; + +public: + CastExpr(StringView CastKind_, const Node *To_, const Node *From_) + : Node(KCastExpr), CastKind(CastKind_), To(To_), From(From_) {} + + template void match(Fn F) const { F(CastKind, To, From); } + + void printLeft(OutputStream &S) const override { + S += CastKind; + S += "<"; + To->printLeft(S); + S += ">("; + From->printLeft(S); + S += ")"; + } +}; + +class SizeofParamPackExpr : public Node { + const Node *Pack; + +public: + SizeofParamPackExpr(const Node *Pack_) + : Node(KSizeofParamPackExpr), Pack(Pack_) {} + + template void match(Fn F) const { F(Pack); } + + void printLeft(OutputStream &S) const override { + S += "sizeof...("; + ParameterPackExpansion PPE(Pack); + PPE.printLeft(S); + S += ")"; + } +}; + +class CallExpr : public Node { + const Node *Callee; + NodeArray Args; + +public: + CallExpr(const Node *Callee_, NodeArray Args_) + : Node(KCallExpr), Callee(Callee_), Args(Args_) {} + + template void match(Fn F) const { F(Callee, Args); } + + void printLeft(OutputStream &S) const override { + Callee->print(S); + S += "("; + Args.printWithComma(S); + S += ")"; + } +}; + +class NewExpr : public Node { + // new (expr_list) type(init_list) + NodeArray ExprList; + Node *Type; + NodeArray InitList; + bool IsGlobal; // ::operator new ? + bool IsArray; // new[] ? +public: + NewExpr(NodeArray ExprList_, Node *Type_, NodeArray InitList_, bool IsGlobal_, + bool IsArray_) + : Node(KNewExpr), ExprList(ExprList_), Type(Type_), InitList(InitList_), + IsGlobal(IsGlobal_), IsArray(IsArray_) {} + + template void match(Fn F) const { + F(ExprList, Type, InitList, IsGlobal, IsArray); + } + + void printLeft(OutputStream &S) const override { + if (IsGlobal) + S += "::operator "; + S += "new"; + if (IsArray) + S += "[]"; + S += ' '; + if (!ExprList.empty()) { + S += "("; + ExprList.printWithComma(S); + S += ")"; + } + Type->print(S); + if (!InitList.empty()) { + S += "("; + InitList.printWithComma(S); + S += ")"; + } + + } +}; + +class DeleteExpr : public Node { + Node *Op; + bool IsGlobal; + bool IsArray; + +public: + DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_) + : Node(KDeleteExpr), Op(Op_), IsGlobal(IsGlobal_), IsArray(IsArray_) {} + + template void match(Fn F) const { F(Op, IsGlobal, IsArray); } + + void printLeft(OutputStream &S) const override { + if (IsGlobal) + S += "::"; + S += "delete"; + if (IsArray) + S += "[] "; + Op->print(S); + } +}; + +class PrefixExpr : public Node { + StringView Prefix; + Node *Child; + +public: + PrefixExpr(StringView Prefix_, Node *Child_) + : Node(KPrefixExpr), Prefix(Prefix_), Child(Child_) {} + + template void match(Fn F) const { F(Prefix, Child); } + + void printLeft(OutputStream &S) const override { + S += Prefix; + S += "("; + Child->print(S); + S += ")"; + } +}; + +class FunctionParam : public Node { + StringView Number; + +public: + FunctionParam(StringView Number_) : Node(KFunctionParam), Number(Number_) {} + + template void match(Fn F) const { F(Number); } + + void printLeft(OutputStream &S) const override { + S += "fp"; + S += Number; + } +}; + +class ConversionExpr : public Node { + const Node *Type; + NodeArray Expressions; + +public: + ConversionExpr(const Node *Type_, NodeArray Expressions_) + : Node(KConversionExpr), Type(Type_), Expressions(Expressions_) {} + + template void match(Fn F) const { F(Type, Expressions); } + + void printLeft(OutputStream &S) const override { + S += "("; + Type->print(S); + S += ")("; + Expressions.printWithComma(S); + S += ")"; + } +}; + +class InitListExpr : public Node { + const Node *Ty; + NodeArray Inits; +public: + InitListExpr(const Node *Ty_, NodeArray Inits_) + : Node(KInitListExpr), Ty(Ty_), Inits(Inits_) {} + + template void match(Fn F) const { F(Ty, Inits); } + + void printLeft(OutputStream &S) const override { + if (Ty) + Ty->print(S); + S += '{'; + Inits.printWithComma(S); + S += '}'; + } +}; + +class BracedExpr : public Node { + const Node *Elem; + const Node *Init; + bool IsArray; +public: + BracedExpr(const Node *Elem_, const Node *Init_, bool IsArray_) + : Node(KBracedExpr), Elem(Elem_), Init(Init_), IsArray(IsArray_) {} + + template void match(Fn F) const { F(Elem, Init, IsArray); } + + void printLeft(OutputStream &S) const override { + if (IsArray) { + S += '['; + Elem->print(S); + S += ']'; + } else { + S += '.'; + Elem->print(S); + } + if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) + S += " = "; + Init->print(S); + } +}; + +class BracedRangeExpr : public Node { + const Node *First; + const Node *Last; + const Node *Init; +public: + BracedRangeExpr(const Node *First_, const Node *Last_, const Node *Init_) + : Node(KBracedRangeExpr), First(First_), Last(Last_), Init(Init_) {} + + template void match(Fn F) const { F(First, Last, Init); } + + void printLeft(OutputStream &S) const override { + S += '['; + First->print(S); + S += " ... "; + Last->print(S); + S += ']'; + if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) + S += " = "; + Init->print(S); + } +}; + +class FoldExpr : public Node { + const Node *Pack, *Init; + StringView OperatorName; + bool IsLeftFold; + +public: + FoldExpr(bool IsLeftFold_, StringView OperatorName_, const Node *Pack_, + const Node *Init_) + : Node(KFoldExpr), Pack(Pack_), Init(Init_), OperatorName(OperatorName_), + IsLeftFold(IsLeftFold_) {} + + template void match(Fn F) const { + F(IsLeftFold, OperatorName, Pack, Init); + } + + void printLeft(OutputStream &S) const override { + auto PrintPack = [&] { + S += '('; + ParameterPackExpansion(Pack).print(S); + S += ')'; + }; + + S += '('; + + if (IsLeftFold) { + // init op ... op pack + if (Init != nullptr) { + Init->print(S); + S += ' '; + S += OperatorName; + S += ' '; + } + // ... op pack + S += "... "; + S += OperatorName; + S += ' '; + PrintPack(); + } else { // !IsLeftFold + // pack op ... + PrintPack(); + S += ' '; + S += OperatorName; + S += " ..."; + // pack op ... op init + if (Init != nullptr) { + S += ' '; + S += OperatorName; + S += ' '; + Init->print(S); + } + } + S += ')'; + } +}; + +class ThrowExpr : public Node { + const Node *Op; + +public: + ThrowExpr(const Node *Op_) : Node(KThrowExpr), Op(Op_) {} + + template void match(Fn F) const { F(Op); } + + void printLeft(OutputStream &S) const override { + S += "throw "; + Op->print(S); + } +}; + +// MSVC __uuidof extension, generated by clang in -fms-extensions mode. +class UUIDOfExpr : public Node { + Node *Operand; +public: + UUIDOfExpr(Node *Operand_) : Node(KUUIDOfExpr), Operand(Operand_) {} + + template void match(Fn F) const { F(Operand); } + + void printLeft(OutputStream &S) const override { + S << "__uuidof("; + Operand->print(S); + S << ")"; + } +}; + +class BoolExpr : public Node { + bool Value; + +public: + BoolExpr(bool Value_) : Node(KBoolExpr), Value(Value_) {} + + template void match(Fn F) const { F(Value); } + + void printLeft(OutputStream &S) const override { + S += Value ? StringView("true") : StringView("false"); + } +}; + +class StringLiteral : public Node { + const Node *Type; + +public: + StringLiteral(const Node *Type_) : Node(KStringLiteral), Type(Type_) {} + + template void match(Fn F) const { F(Type); } + + void printLeft(OutputStream &S) const override { + S += "\"<"; + Type->print(S); + S += ">\""; + } +}; + +class LambdaExpr : public Node { + const Node *Type; + +public: + LambdaExpr(const Node *Type_) : Node(KLambdaExpr), Type(Type_) {} + + template void match(Fn F) const { F(Type); } + + void printLeft(OutputStream &S) const override { + S += "[]"; + if (Type->getKind() == KClosureTypeName) + static_cast(Type)->printDeclarator(S); + S += "{...}"; + } +}; + +class EnumLiteral : public Node { + // ty(integer) + const Node *Ty; + StringView Integer; + +public: + EnumLiteral(const Node *Ty_, StringView Integer_) + : Node(KEnumLiteral), Ty(Ty_), Integer(Integer_) {} + + template void match(Fn F) const { F(Ty, Integer); } + + void printLeft(OutputStream &S) const override { + S << "("; + Ty->print(S); + S << ")"; + + if (Integer[0] == 'n') + S << "-" << Integer.dropFront(1); + else + S << Integer; + } +}; + +class IntegerLiteral : public Node { + StringView Type; + StringView Value; + +public: + IntegerLiteral(StringView Type_, StringView Value_) + : Node(KIntegerLiteral), Type(Type_), Value(Value_) {} + + template void match(Fn F) const { F(Type, Value); } + + void printLeft(OutputStream &S) const override { + if (Type.size() > 3) { + S += "("; + S += Type; + S += ")"; + } + + if (Value[0] == 'n') { + S += "-"; + S += Value.dropFront(1); + } else + S += Value; + + if (Type.size() <= 3) + S += Type; + } +}; + +template struct FloatData; + +namespace float_literal_impl { +constexpr Node::Kind getFloatLiteralKind(float *) { + return Node::KFloatLiteral; +} +constexpr Node::Kind getFloatLiteralKind(double *) { + return Node::KDoubleLiteral; +} +constexpr Node::Kind getFloatLiteralKind(long double *) { + return Node::KLongDoubleLiteral; +} +} + +template class FloatLiteralImpl : public Node { + const StringView Contents; + + static constexpr Kind KindForClass = + float_literal_impl::getFloatLiteralKind((Float *)nullptr); + +public: + FloatLiteralImpl(StringView Contents_) + : Node(KindForClass), Contents(Contents_) {} + + template void match(Fn F) const { F(Contents); } + + void printLeft(OutputStream &s) const override { + const char *first = Contents.begin(); + const char *last = Contents.end() + 1; + + const size_t N = FloatData::mangled_size; + if (static_cast(last - first) > N) { + last = first + N; + union { + Float value; + char buf[sizeof(Float)]; + }; + const char *t = first; + char *e = buf; + for (; t != last; ++t, ++e) { + unsigned d1 = isdigit(*t) ? static_cast(*t - '0') + : static_cast(*t - 'a' + 10); + ++t; + unsigned d0 = isdigit(*t) ? static_cast(*t - '0') + : static_cast(*t - 'a' + 10); + *e = static_cast((d1 << 4) + d0); + } +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + std::reverse(buf, e); +#endif + char num[FloatData::max_demangled_size] = {0}; + int n = snprintf(num, sizeof(num), FloatData::spec, value); + s += StringView(num, num + n); + } + } +}; + +using FloatLiteral = FloatLiteralImpl; +using DoubleLiteral = FloatLiteralImpl; +using LongDoubleLiteral = FloatLiteralImpl; + +/// Visit the node. Calls \c F(P), where \c P is the node cast to the +/// appropriate derived class. +template +void Node::visit(Fn F) const { + switch (K) { +#define CASE(X) case K ## X: return F(static_cast(this)); + FOR_EACH_NODE_KIND(CASE) +#undef CASE + } + assert(0 && "unknown mangling node kind"); +} + +/// Determine the kind of a node from its type. +template struct NodeKind; +#define SPECIALIZATION(X) \ + template<> struct NodeKind { \ + static constexpr Node::Kind Kind = Node::K##X; \ + static constexpr const char *name() { return #X; } \ + }; +FOR_EACH_NODE_KIND(SPECIALIZATION) +#undef SPECIALIZATION + +#undef FOR_EACH_NODE_KIND + +template +class PODSmallVector { + static_assert(std::is_pod::value, + "T is required to be a plain old data type"); + + T* First = nullptr; + T* Last = nullptr; + T* Cap = nullptr; + T Inline[N] = {0}; + + bool isInline() const { return First == Inline; } + + void clearInline() { + First = Inline; + Last = Inline; + Cap = Inline + N; + } + + void reserve(size_t NewCap) { + size_t S = size(); + if (isInline()) { + auto* Tmp = static_cast(std::malloc(NewCap * sizeof(T))); + if (Tmp == nullptr) + std::terminate(); + std::copy(First, Last, Tmp); + First = Tmp; + } else { + First = static_cast(std::realloc(First, NewCap * sizeof(T))); + if (First == nullptr) + std::terminate(); + } + Last = First + S; + Cap = First + NewCap; + } + +public: + PODSmallVector() : First(Inline), Last(First), Cap(Inline + N) {} + + PODSmallVector(const PODSmallVector&) = delete; + PODSmallVector& operator=(const PODSmallVector&) = delete; + + PODSmallVector(PODSmallVector&& Other) : PODSmallVector() { + if (Other.isInline()) { + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return; + } + + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + } + + PODSmallVector& operator=(PODSmallVector&& Other) { + if (Other.isInline()) { + if (!isInline()) { + std::free(First); + clearInline(); + } + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return *this; + } + + if (isInline()) { + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + return *this; + } + + std::swap(First, Other.First); + std::swap(Last, Other.Last); + std::swap(Cap, Other.Cap); + Other.clear(); + return *this; + } + + void push_back(const T& Elem) { + if (Last == Cap) + reserve(size() * 2); + *Last++ = Elem; + } + + void pop_back() { + assert(Last != First && "Popping empty vector!"); + --Last; + } + + void dropBack(size_t Index) { + assert(Index <= size() && "dropBack() can't expand!"); + Last = First + Index; + } + + T* begin() { return First; } + T* end() { return Last; } + + bool empty() const { return First == Last; } + size_t size() const { return static_cast(Last - First); } + T& back() { + assert(Last != First && "Calling back() on empty vector!"); + return *(Last - 1); + } + T& operator[](size_t Index) { + assert(Index < size() && "Invalid access!"); + return *(begin() + Index); + } + void clear() { Last = First; } + + ~PODSmallVector() { + if (!isInline()) + std::free(First); + } +}; + +template struct AbstractManglingParser { + const char *First; + const char *Last; + + // Name stack, this is used by the parser to hold temporary names that were + // parsed. The parser collapses multiple names into new nodes to construct + // the AST. Once the parser is finished, names.size() == 1. + PODSmallVector Names; + + // Substitution table. Itanium supports name substitutions as a means of + // compression. The string "S42_" refers to the 44nd entry (base-36) in this + // table. + PODSmallVector Subs; + + using TemplateParamList = PODSmallVector; + + class ScopedTemplateParamList { + AbstractManglingParser *Parser; + size_t OldNumTemplateParamLists; + TemplateParamList Params; + + public: + ScopedTemplateParamList(AbstractManglingParser *Parser) + : Parser(Parser), + OldNumTemplateParamLists(Parser->TemplateParams.size()) { + Parser->TemplateParams.push_back(&Params); + } + ~ScopedTemplateParamList() { + assert(Parser->TemplateParams.size() >= OldNumTemplateParamLists); + Parser->TemplateParams.dropBack(OldNumTemplateParamLists); + } + }; + + // Template parameter table. Like the above, but referenced like "T42_". + // This has a smaller size compared to Subs and Names because it can be + // stored on the stack. + TemplateParamList OuterTemplateParams; + + // Lists of template parameters indexed by template parameter depth, + // referenced like "TL2_4_". If nonempty, element 0 is always + // OuterTemplateParams; inner elements are always template parameter lists of + // lambda expressions. For a generic lambda with no explicit template + // parameter list, the corresponding parameter list pointer will be null. + PODSmallVector TemplateParams; + + // Set of unresolved forward references. These can occur in a + // conversion operator's type, and are resolved in the enclosing . + PODSmallVector ForwardTemplateRefs; + + bool TryToParseTemplateArgs = true; + bool PermitForwardTemplateReferences = false; + size_t ParsingLambdaParamsAtLevel = (size_t)-1; + + unsigned NumSyntheticTemplateParameters[3] = {}; + + Alloc ASTAllocator; + + AbstractManglingParser(const char *First_, const char *Last_) + : First(First_), Last(Last_) {} + + Derived &getDerived() { return static_cast(*this); } + + void reset(const char *First_, const char *Last_) { + First = First_; + Last = Last_; + Names.clear(); + Subs.clear(); + TemplateParams.clear(); + ParsingLambdaParamsAtLevel = (size_t)-1; + TryToParseTemplateArgs = true; + PermitForwardTemplateReferences = false; + for (int I = 0; I != 3; ++I) + NumSyntheticTemplateParameters[I] = 0; + ASTAllocator.reset(); + } + + template Node *make(Args &&... args) { + return ASTAllocator.template makeNode(std::forward(args)...); + } + + template NodeArray makeNodeArray(It begin, It end) { + size_t sz = static_cast(end - begin); + void *mem = ASTAllocator.allocateNodeArray(sz); + Node **data = new (mem) Node *[sz]; + std::copy(begin, end, data); + return NodeArray(data, sz); + } + + NodeArray popTrailingNodeArray(size_t FromPosition) { + assert(FromPosition <= Names.size()); + NodeArray res = + makeNodeArray(Names.begin() + (long)FromPosition, Names.end()); + Names.dropBack(FromPosition); + return res; + } + + bool consumeIf(StringView S) { + if (StringView(First, Last).startsWith(S)) { + First += S.size(); + return true; + } + return false; + } + + bool consumeIf(char C) { + if (First != Last && *First == C) { + ++First; + return true; + } + return false; + } + + char consume() { return First != Last ? *First++ : '\0'; } + + char look(unsigned Lookahead = 0) { + if (static_cast(Last - First) <= Lookahead) + return '\0'; + return First[Lookahead]; + } + + size_t numLeft() const { return static_cast(Last - First); } + + StringView parseNumber(bool AllowNegative = false); + Qualifiers parseCVQualifiers(); + bool parsePositiveInteger(size_t *Out); + StringView parseBareSourceName(); + + bool parseSeqId(size_t *Out); + Node *parseSubstitution(); + Node *parseTemplateParam(); + Node *parseTemplateParamDecl(); + Node *parseTemplateArgs(bool TagTemplates = false); + Node *parseTemplateArg(); + + /// Parse the production. + Node *parseExpr(); + Node *parsePrefixExpr(StringView Kind); + Node *parseBinaryExpr(StringView Kind); + Node *parseIntegerLiteral(StringView Lit); + Node *parseExprPrimary(); + template Node *parseFloatingLiteral(); + Node *parseFunctionParam(); + Node *parseNewExpr(); + Node *parseConversionExpr(); + Node *parseBracedExpr(); + Node *parseFoldExpr(); + + /// Parse the production. + Node *parseType(); + Node *parseFunctionType(); + Node *parseVectorType(); + Node *parseDecltype(); + Node *parseArrayType(); + Node *parsePointerToMemberType(); + Node *parseClassEnumType(); + Node *parseQualifiedType(); + + Node *parseEncoding(); + bool parseCallOffset(); + Node *parseSpecialName(); + + /// Holds some extra information about a that is being parsed. This + /// information is only pertinent if the refers to an . + struct NameState { + bool CtorDtorConversion = false; + bool EndsWithTemplateArgs = false; + Qualifiers CVQualifiers = QualNone; + FunctionRefQual ReferenceQualifier = FrefQualNone; + size_t ForwardTemplateRefsBegin; + + NameState(AbstractManglingParser *Enclosing) + : ForwardTemplateRefsBegin(Enclosing->ForwardTemplateRefs.size()) {} + }; + + bool resolveForwardTemplateRefs(NameState &State) { + size_t I = State.ForwardTemplateRefsBegin; + size_t E = ForwardTemplateRefs.size(); + for (; I < E; ++I) { + size_t Idx = ForwardTemplateRefs[I]->Index; + if (TemplateParams.empty() || !TemplateParams[0] || + Idx >= TemplateParams[0]->size()) + return true; + ForwardTemplateRefs[I]->Ref = (*TemplateParams[0])[Idx]; + } + ForwardTemplateRefs.dropBack(State.ForwardTemplateRefsBegin); + return false; + } + + /// Parse the production> + Node *parseName(NameState *State = nullptr); + Node *parseLocalName(NameState *State); + Node *parseOperatorName(NameState *State); + Node *parseUnqualifiedName(NameState *State); + Node *parseUnnamedTypeName(NameState *State); + Node *parseSourceName(NameState *State); + Node *parseUnscopedName(NameState *State); + Node *parseNestedName(NameState *State); + Node *parseCtorDtorName(Node *&SoFar, NameState *State); + + Node *parseAbiTags(Node *N); + + /// Parse the production. + Node *parseUnresolvedName(); + Node *parseSimpleId(); + Node *parseBaseUnresolvedName(); + Node *parseUnresolvedType(); + Node *parseDestructorName(); + + /// Top-level entry point into the parser. + Node *parse(); +}; + +const char* parse_discriminator(const char* first, const char* last); + +// ::= // N +// ::= # See Scope Encoding below // Z +// ::= +// ::= +// +// ::= +// ::= +template +Node *AbstractManglingParser::parseName(NameState *State) { + consumeIf('L'); // extension + + if (look() == 'N') + return getDerived().parseNestedName(State); + if (look() == 'Z') + return getDerived().parseLocalName(State); + + // ::= + if (look() == 'S' && look(1) != 't') { + Node *S = getDerived().parseSubstitution(); + if (S == nullptr) + return nullptr; + if (look() != 'I') + return nullptr; + Node *TA = getDerived().parseTemplateArgs(State != nullptr); + if (TA == nullptr) + return nullptr; + if (State) State->EndsWithTemplateArgs = true; + return make(S, TA); + } + + Node *N = getDerived().parseUnscopedName(State); + if (N == nullptr) + return nullptr; + // ::= + if (look() == 'I') { + Subs.push_back(N); + Node *TA = getDerived().parseTemplateArgs(State != nullptr); + if (TA == nullptr) + return nullptr; + if (State) State->EndsWithTemplateArgs = true; + return make(N, TA); + } + // ::= + return N; +} + +// := Z E [] +// := Z E s [] +// := Z Ed [ ] _ +template +Node *AbstractManglingParser::parseLocalName(NameState *State) { + if (!consumeIf('Z')) + return nullptr; + Node *Encoding = getDerived().parseEncoding(); + if (Encoding == nullptr || !consumeIf('E')) + return nullptr; + + if (consumeIf('s')) { + First = parse_discriminator(First, Last); + auto *StringLitName = make("string literal"); + if (!StringLitName) + return nullptr; + return make(Encoding, StringLitName); + } + + if (consumeIf('d')) { + parseNumber(true); + if (!consumeIf('_')) + return nullptr; + Node *N = getDerived().parseName(State); + if (N == nullptr) + return nullptr; + return make(Encoding, N); + } + + Node *Entity = getDerived().parseName(State); + if (Entity == nullptr) + return nullptr; + First = parse_discriminator(First, Last); + return make(Encoding, Entity); +} + +// ::= +// ::= St # ::std:: +// extension ::= StL +template +Node * +AbstractManglingParser::parseUnscopedName(NameState *State) { + if (consumeIf("StL") || consumeIf("St")) { + Node *R = getDerived().parseUnqualifiedName(State); + if (R == nullptr) + return nullptr; + return make(R); + } + return getDerived().parseUnqualifiedName(State); +} + +// ::= [abi-tags] +// ::= +// ::= +// ::= +// ::= DC + E # structured binding declaration +template +Node * +AbstractManglingParser::parseUnqualifiedName(NameState *State) { + // s are special-cased in parseNestedName(). + Node *Result; + if (look() == 'U') + Result = getDerived().parseUnnamedTypeName(State); + else if (look() >= '1' && look() <= '9') + Result = getDerived().parseSourceName(State); + else if (consumeIf("DC")) { + size_t BindingsBegin = Names.size(); + do { + Node *Binding = getDerived().parseSourceName(State); + if (Binding == nullptr) + return nullptr; + Names.push_back(Binding); + } while (!consumeIf('E')); + Result = make(popTrailingNodeArray(BindingsBegin)); + } else + Result = getDerived().parseOperatorName(State); + if (Result != nullptr) + Result = getDerived().parseAbiTags(Result); + return Result; +} + +// ::= Ut [] _ +// ::= +// +// ::= Ul E [ ] _ +// +// ::= + # Parameter types or "v" if the lambda has no parameters +template +Node * +AbstractManglingParser::parseUnnamedTypeName(NameState *State) { + // refer to the innermost . Clear out any + // outer args that we may have inserted into TemplateParams. + if (State != nullptr) + TemplateParams.clear(); + + if (consumeIf("Ut")) { + StringView Count = parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make(Count); + } + if (consumeIf("Ul")) { + SwapAndRestore SwapParams(ParsingLambdaParamsAtLevel, + TemplateParams.size()); + ScopedTemplateParamList LambdaTemplateParams(this); + + size_t ParamsBegin = Names.size(); + while (look() == 'T' && + StringView("yptn").find(look(1)) != StringView::npos) { + Node *T = parseTemplateParamDecl(); + if (!T) + return nullptr; + Names.push_back(T); + } + NodeArray TempParams = popTrailingNodeArray(ParamsBegin); + + // FIXME: If TempParams is empty and none of the function parameters + // includes 'auto', we should remove LambdaTemplateParams from the + // TemplateParams list. Unfortunately, we don't find out whether there are + // any 'auto' parameters until too late in an example such as: + // + // template void f( + // decltype([](decltype([](T v) {}), + // auto) {})) {} + // template void f( + // decltype([](decltype([](T w) {}), + // int) {})) {} + // + // Here, the type of v is at level 2 but the type of w is at level 1. We + // don't find this out until we encounter the type of the next parameter. + // + // However, compilers can't actually cope with the former example in + // practice, and it's likely to be made ill-formed in future, so we don't + // need to support it here. + // + // If we encounter an 'auto' in the function parameter types, we will + // recreate a template parameter scope for it, but any intervening lambdas + // will be parsed in the 'wrong' template parameter depth. + if (TempParams.empty()) + TemplateParams.pop_back(); + + if (!consumeIf("vE")) { + do { + Node *P = getDerived().parseType(); + if (P == nullptr) + return nullptr; + Names.push_back(P); + } while (!consumeIf('E')); + } + NodeArray Params = popTrailingNodeArray(ParamsBegin); + + StringView Count = parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make(TempParams, Params, Count); + } + if (consumeIf("Ub")) { + (void)parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make("'block-literal'"); + } + return nullptr; +} + +// ::= +template +Node *AbstractManglingParser::parseSourceName(NameState *) { + size_t Length = 0; + if (parsePositiveInteger(&Length)) + return nullptr; + if (numLeft() < Length || Length == 0) + return nullptr; + StringView Name(First, First + Length); + First += Length; + if (Name.startsWith("_GLOBAL__N")) + return make("(anonymous namespace)"); + return make(Name); +} + +// ::= aa # && +// ::= ad # & (unary) +// ::= an # & +// ::= aN # &= +// ::= aS # = +// ::= cl # () +// ::= cm # , +// ::= co # ~ +// ::= cv # (cast) +// ::= da # delete[] +// ::= de # * (unary) +// ::= dl # delete +// ::= dv # / +// ::= dV # /= +// ::= eo # ^ +// ::= eO # ^= +// ::= eq # == +// ::= ge # >= +// ::= gt # > +// ::= ix # [] +// ::= le # <= +// ::= li # operator "" +// ::= ls # << +// ::= lS # <<= +// ::= lt # < +// ::= mi # - +// ::= mI # -= +// ::= ml # * +// ::= mL # *= +// ::= mm # -- (postfix in context) +// ::= na # new[] +// ::= ne # != +// ::= ng # - (unary) +// ::= nt # ! +// ::= nw # new +// ::= oo # || +// ::= or # | +// ::= oR # |= +// ::= pm # ->* +// ::= pl # + +// ::= pL # += +// ::= pp # ++ (postfix in context) +// ::= ps # + (unary) +// ::= pt # -> +// ::= qu # ? +// ::= rm # % +// ::= rM # %= +// ::= rs # >> +// ::= rS # >>= +// ::= ss # <=> C++2a +// ::= v # vendor extended operator +template +Node * +AbstractManglingParser::parseOperatorName(NameState *State) { + switch (look()) { + case 'a': + switch (look(1)) { + case 'a': + First += 2; + return make("operator&&"); + case 'd': + case 'n': + First += 2; + return make("operator&"); + case 'N': + First += 2; + return make("operator&="); + case 'S': + First += 2; + return make("operator="); + } + return nullptr; + case 'c': + switch (look(1)) { + case 'l': + First += 2; + return make("operator()"); + case 'm': + First += 2; + return make("operator,"); + case 'o': + First += 2; + return make("operator~"); + // ::= cv # (cast) + case 'v': { + First += 2; + SwapAndRestore SaveTemplate(TryToParseTemplateArgs, false); + // If we're parsing an encoding, State != nullptr and the conversion + // operators' could have a that refers to some + // s further ahead in the mangled name. + SwapAndRestore SavePermit(PermitForwardTemplateReferences, + PermitForwardTemplateReferences || + State != nullptr); + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + if (State) State->CtorDtorConversion = true; + return make(Ty); + } + } + return nullptr; + case 'd': + switch (look(1)) { + case 'a': + First += 2; + return make("operator delete[]"); + case 'e': + First += 2; + return make("operator*"); + case 'l': + First += 2; + return make("operator delete"); + case 'v': + First += 2; + return make("operator/"); + case 'V': + First += 2; + return make("operator/="); + } + return nullptr; + case 'e': + switch (look(1)) { + case 'o': + First += 2; + return make("operator^"); + case 'O': + First += 2; + return make("operator^="); + case 'q': + First += 2; + return make("operator=="); + } + return nullptr; + case 'g': + switch (look(1)) { + case 'e': + First += 2; + return make("operator>="); + case 't': + First += 2; + return make("operator>"); + } + return nullptr; + case 'i': + if (look(1) == 'x') { + First += 2; + return make("operator[]"); + } + return nullptr; + case 'l': + switch (look(1)) { + case 'e': + First += 2; + return make("operator<="); + // ::= li # operator "" + case 'i': { + First += 2; + Node *SN = getDerived().parseSourceName(State); + if (SN == nullptr) + return nullptr; + return make(SN); + } + case 's': + First += 2; + return make("operator<<"); + case 'S': + First += 2; + return make("operator<<="); + case 't': + First += 2; + return make("operator<"); + } + return nullptr; + case 'm': + switch (look(1)) { + case 'i': + First += 2; + return make("operator-"); + case 'I': + First += 2; + return make("operator-="); + case 'l': + First += 2; + return make("operator*"); + case 'L': + First += 2; + return make("operator*="); + case 'm': + First += 2; + return make("operator--"); + } + return nullptr; + case 'n': + switch (look(1)) { + case 'a': + First += 2; + return make("operator new[]"); + case 'e': + First += 2; + return make("operator!="); + case 'g': + First += 2; + return make("operator-"); + case 't': + First += 2; + return make("operator!"); + case 'w': + First += 2; + return make("operator new"); + } + return nullptr; + case 'o': + switch (look(1)) { + case 'o': + First += 2; + return make("operator||"); + case 'r': + First += 2; + return make("operator|"); + case 'R': + First += 2; + return make("operator|="); + } + return nullptr; + case 'p': + switch (look(1)) { + case 'm': + First += 2; + return make("operator->*"); + case 'l': + First += 2; + return make("operator+"); + case 'L': + First += 2; + return make("operator+="); + case 'p': + First += 2; + return make("operator++"); + case 's': + First += 2; + return make("operator+"); + case 't': + First += 2; + return make("operator->"); + } + return nullptr; + case 'q': + if (look(1) == 'u') { + First += 2; + return make("operator?"); + } + return nullptr; + case 'r': + switch (look(1)) { + case 'm': + First += 2; + return make("operator%"); + case 'M': + First += 2; + return make("operator%="); + case 's': + First += 2; + return make("operator>>"); + case 'S': + First += 2; + return make("operator>>="); + } + return nullptr; + case 's': + if (look(1) == 's') { + First += 2; + return make("operator<=>"); + } + return nullptr; + // ::= v # vendor extended operator + case 'v': + if (std::isdigit(look(1))) { + First += 2; + Node *SN = getDerived().parseSourceName(State); + if (SN == nullptr) + return nullptr; + return make(SN); + } + return nullptr; + } + return nullptr; +} + +// ::= C1 # complete object constructor +// ::= C2 # base object constructor +// ::= C3 # complete object allocating constructor +// extension ::= C4 # gcc old-style "[unified]" constructor +// extension ::= C5 # the COMDAT used for ctors +// ::= D0 # deleting destructor +// ::= D1 # complete object destructor +// ::= D2 # base object destructor +// extension ::= D4 # gcc old-style "[unified]" destructor +// extension ::= D5 # the COMDAT used for dtors +template +Node * +AbstractManglingParser::parseCtorDtorName(Node *&SoFar, + NameState *State) { + if (SoFar->getKind() == Node::KSpecialSubstitution) { + auto SSK = static_cast(SoFar)->SSK; + switch (SSK) { + case SpecialSubKind::string: + case SpecialSubKind::istream: + case SpecialSubKind::ostream: + case SpecialSubKind::iostream: + SoFar = make(SSK); + if (!SoFar) + return nullptr; + break; + default: + break; + } + } + + if (consumeIf('C')) { + bool IsInherited = consumeIf('I'); + if (look() != '1' && look() != '2' && look() != '3' && look() != '4' && + look() != '5') + return nullptr; + int Variant = look() - '0'; + ++First; + if (State) State->CtorDtorConversion = true; + if (IsInherited) { + if (getDerived().parseName(State) == nullptr) + return nullptr; + } + return make(SoFar, /*IsDtor=*/false, Variant); + } + + if (look() == 'D' && (look(1) == '0' || look(1) == '1' || look(1) == '2' || + look(1) == '4' || look(1) == '5')) { + int Variant = look(1) - '0'; + First += 2; + if (State) State->CtorDtorConversion = true; + return make(SoFar, /*IsDtor=*/true, Variant); + } + + return nullptr; +} + +// ::= N [] [] E +// ::= N [] [] E +// +// ::= +// ::= +// ::= +// ::= +// ::= # empty +// ::= +// ::= +// extension ::= L +// +// := [] M +// +// ::=