diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2022-11-30 09:15:25 +0100 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2022-11-30 09:17:24 +0100 |
commit | d5ce1eda907df019cd3b26fc8db0246eac804c76 (patch) | |
tree | e785841998ded00ea03686e05cf70e48dcb10014 | |
parent | adc3fba76fbf1ccfd0863d39702d57f247b5b109 (diff) |
include demumble without submodule
20 files changed, 11635 insertions, 4 deletions
diff --git a/.gitmodules b/.gitmodules index 2e2520dd..fd4fc9fd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,6 +25,3 @@ [submodule "luajit"] path = external/luajit url = https://github.com/WohlSoft/LuaJIT.git -[submodule "demumble"] - path = demumble - url = https://github.com/sthalik/demumble diff --git a/CMakeLists.txt b/CMakeLists.txt index 793c6dc5..8d56aa45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,7 +180,7 @@ include_directories(SYSTEM "${_fm-json-include-dirs}" ) -add_subdirectory(demumble) +add_subdirectory(demangle) add_subdirectory(src) add_subdirectory(serialize) add_subdirectory(loader) diff --git a/demangle/CMakeLists.txt b/demangle/CMakeLists.txt new file mode 100644 index 00000000..ce3222a4 --- /dev/null +++ b/demangle/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) +project(demumble CXX) + +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) +set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_DEFAULT 23) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS FALSE) + +if (NOT MSVC) + add_compile_options(-Wall -fno-exceptions -fno-rtti) + # 10.9 chosen somewhat arbitrary; it's the first target where clang defaults + # to libc++ and ld64 defaults to stripping __TEXT,__eh_frame. + if (APPLE) + add_compile_options(-mmacosx-version-min=10.9) + else() + add_compile_options(-fno-plt) + endif() + add_compile_options(-Wno-error -Wno-sign-conversion -Wno-unused -Wno-unused-parameter) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang$") + add_compile_options(-Wno-shorten-64-to-32 -Wno-implicit-int-conversion -Wno-class-varargs -Wno-extra-semi-stmt) + endif() +else() + add_compile_options(-Zc:inline -permissive- -GR-) + add_compile_options(-wd4100 -wd4244 -wd4267) + add_definitions(-D_HAS_EXCEPTIONS=0 -DNOMINMAX -D_USE_MATH_DEFINES=1) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) # The LLVM build sets this. +endif() + +include_directories(SYSTEM third_party/llvm/include) +add_library(demumble STATIC + third_party/llvm/lib/Demangle/Demangle.cpp + third_party/llvm/lib/Demangle/ItaniumDemangle.cpp + third_party/llvm/lib/Demangle/MicrosoftDemangle.cpp + third_party/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp + demangle.cpp) +target_link_libraries(demumble PUBLIC Corrade::Containers) diff --git a/demangle/demangle.cpp b/demangle/demangle.cpp new file mode 100644 index 00000000..f3cfd231 --- /dev/null +++ b/demangle/demangle.cpp @@ -0,0 +1,15 @@ +#include "demangle.h" +#include <cstdlib> +#include <string> +#include "llvm/Demangle/Demangle.h" + +using namespace Corrade::Containers; + +String demangle_symbol(const char* symbol) +{ + if (!symbol || !*symbol) + return {}; + if (const auto str = llvm::demangle(symbol); !str.empty()) + return String{str.data(), str.size()}; + return {}; +} diff --git a/demangle/demangle.h b/demangle/demangle.h new file mode 100644 index 00000000..632f9c14 --- /dev/null +++ b/demangle/demangle.h @@ -0,0 +1,4 @@ +#pragma once +#include <Corrade/Containers/String.h> + +Corrade::Containers::String demangle_symbol(const char* symbol); diff --git a/demangle/third_party/llvm/LICENSE.txt b/demangle/third_party/llvm/LICENSE.txt new file mode 100644 index 00000000..fa6ac540 --- /dev/null +++ b/demangle/third_party/llvm/LICENSE.txt @@ -0,0 +1,279 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + diff --git a/demangle/third_party/llvm/include/llvm/Demangle/Compiler.h b/demangle/third_party/llvm/include/llvm/Demangle/Compiler.h new file mode 100644 index 00000000..c17ff4ca --- /dev/null +++ b/demangle/third_party/llvm/include/llvm/Demangle/Compiler.h @@ -0,0 +1,549 @@ +//===--- Compiler.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// +// 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 + +#ifdef __cplusplus +#include <new> +#endif +#include <stddef.h> + +#if defined(_MSC_VER) +#include <sal.h> +#endif + +#ifndef __has_feature +# define __has_feature(x) 0 +#endif + +#ifndef __has_extension +# define __has_extension(x) 0 +#endif + +#ifndef __has_attribute +# define __has_attribute(x) 0 +#endif + +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +// Only use __has_cpp_attribute in C++ mode. GCC defines __has_cpp_attribute in +// C mode, but the :: in __has_cpp_attribute(scoped::attribute) is invalid. +#ifndef LLVM_HAS_CPP_ATTRIBUTE +#if defined(__cplusplus) && defined(__has_cpp_attribute) +# define LLVM_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define LLVM_HAS_CPP_ATTRIBUTE(x) 0 +#endif +#endif + +/// \macro LLVM_GNUC_PREREQ +/// Extend the default __GNUC_PREREQ even if glibc's features.h isn't +/// available. +#ifndef LLVM_GNUC_PREREQ +# if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) +# define LLVM_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \ + ((maj) << 20) + ((min) << 10) + (patch)) +# elif defined(__GNUC__) && defined(__GNUC_MINOR__) +# define LLVM_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10)) +# else +# define LLVM_GNUC_PREREQ(maj, min, patch) 0 +# endif +#endif + +/// \macro LLVM_MSC_PREREQ +/// Is the compiler MSVC of at least the specified version? +/// The common \param version values to check for are: +/// * 1910: VS2017, version 15.1 & 15.2 +/// * 1911: VS2017, version 15.3 & 15.4 +/// * 1912: VS2017, version 15.5 +/// * 1913: VS2017, version 15.6 +/// * 1914: VS2017, version 15.7 +/// * 1915: VS2017, version 15.8 +/// * 1916: VS2017, version 15.9 +/// * 1920: VS2019, version 16.0 +/// * 1921: VS2019, version 16.1 +#ifdef _MSC_VER +#define LLVM_MSC_PREREQ(version) (_MSC_VER >= (version)) + +// We require at least MSVC 2017. +#if !LLVM_MSC_PREREQ(1910) +#error LLVM requires at least MSVC 2017. +#endif + +#else +#define LLVM_MSC_PREREQ(version) 0 +#endif + +/// Does the compiler support ref-qualifiers for *this? +/// +/// Sadly, this is separate from just rvalue reference support because GCC +/// and MSVC implemented this later than everything else. This appears to be +/// corrected in MSVC 2019 but not MSVC 2017. +#if __has_feature(cxx_rvalue_references) || LLVM_GNUC_PREREQ(4, 8, 1) || \ + LLVM_MSC_PREREQ(1920) +#define LLVM_HAS_RVALUE_REFERENCE_THIS 1 +#else +#define LLVM_HAS_RVALUE_REFERENCE_THIS 0 +#endif + +/// Expands to '&' if ref-qualifiers for *this are supported. +/// +/// This can be used to provide lvalue/rvalue overrides of member functions. +/// The rvalue override should be guarded by LLVM_HAS_RVALUE_REFERENCE_THIS +#if LLVM_HAS_RVALUE_REFERENCE_THIS +#define LLVM_LVALUE_FUNCTION & +#else +#define LLVM_LVALUE_FUNCTION +#endif + +/// LLVM_LIBRARY_VISIBILITY - If a class marked with this attribute is linked +/// into a shared library, then the class should be private to the library and +/// not accessible from outside it. Can also be used to mark variables and +/// functions, making them private to any shared library they are linked into. +/// On PE/COFF targets, library visibility is the default, so this isn't needed. +/// +/// LLVM_EXTERNAL_VISIBILITY - classes, functions, and variables marked with +/// this attribute will be made public and visible outside of any shared library +/// they are linked in to. +#if (__has_attribute(visibility) || LLVM_GNUC_PREREQ(4, 0, 0)) && \ + !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(_WIN32) +#define LLVM_LIBRARY_VISIBILITY __attribute__ ((visibility("hidden"))) +#define LLVM_EXTERNAL_VISIBILITY __attribute__ ((visibility("default"))) +#else +#define LLVM_LIBRARY_VISIBILITY +#define LLVM_EXTERNAL_VISIBILITY +#endif + +#if defined(__GNUC__) +#define LLVM_PREFETCH(addr, rw, locality) __builtin_prefetch(addr, rw, locality) +#else +#define LLVM_PREFETCH(addr, rw, locality) +#endif + +#if __has_attribute(used) || LLVM_GNUC_PREREQ(3, 1, 0) +#define LLVM_ATTRIBUTE_USED __attribute__((__used__)) +#else +#define LLVM_ATTRIBUTE_USED +#endif + +/// LLVM_NODISCARD - Warn if a type or return value is discarded. + +// Use the 'nodiscard' attribute in C++17 or newer mode. +#if defined(__cplusplus) && __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(nodiscard) +#define LLVM_NODISCARD [[nodiscard]] +#elif LLVM_HAS_CPP_ATTRIBUTE(clang::warn_unused_result) +#define LLVM_NODISCARD [[clang::warn_unused_result]] +// Clang in C++14 mode claims that it has the 'nodiscard' attribute, but also +// warns in the pedantic mode that 'nodiscard' is a C++17 extension (PR33518). +// Use the 'nodiscard' attribute in C++14 mode only with GCC. +// TODO: remove this workaround when PR33518 is resolved. +#elif defined(__GNUC__) && LLVM_HAS_CPP_ATTRIBUTE(nodiscard) +#define LLVM_NODISCARD [[nodiscard]] +#else +#define LLVM_NODISCARD +#endif + +// Indicate that a non-static, non-const C++ member function reinitializes +// the entire object to a known state, independent of the previous state of +// the object. +// +// The clang-tidy check bugprone-use-after-move recognizes this attribute as a +// marker that a moved-from object has left the indeterminate state and can be +// reused. +#if LLVM_HAS_CPP_ATTRIBUTE(clang::reinitializes) +#define LLVM_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]] +#else +#define LLVM_ATTRIBUTE_REINITIALIZES +#endif + +// Some compilers warn about unused functions. When a function is sometimes +// used or not depending on build settings (e.g. a function only called from +// within "assert"), this attribute can be used to suppress such warnings. +// +// However, it shouldn't be used for unused *variables*, as those have a much +// more portable solution: +// (void)unused_var_name; +// Prefer cast-to-void wherever it is sufficient. +#if __has_attribute(unused) || LLVM_GNUC_PREREQ(3, 1, 0) +#define LLVM_ATTRIBUTE_UNUSED __attribute__((__unused__)) +#else +#define LLVM_ATTRIBUTE_UNUSED +#endif + +// FIXME: Provide this for PE/COFF targets. +#if (__has_attribute(weak) || LLVM_GNUC_PREREQ(4, 0, 0)) && \ + (!defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(_WIN32)) +#define LLVM_ATTRIBUTE_WEAK __attribute__((__weak__)) +#else +#define LLVM_ATTRIBUTE_WEAK +#endif + +// Prior to clang 3.2, clang did not accept any spelling of +// __has_attribute(const), so assume it is supported. +#if defined(__clang__) || defined(__GNUC__) +// aka 'CONST' but following LLVM Conventions. +#define LLVM_READNONE __attribute__((__const__)) +#else +#define LLVM_READNONE +#endif + +#if __has_attribute(pure) || defined(__GNUC__) +// aka 'PURE' but following LLVM Conventions. +#define LLVM_READONLY __attribute__((__pure__)) +#else +#define LLVM_READONLY +#endif + +#if __has_builtin(__builtin_expect) || LLVM_GNUC_PREREQ(4, 0, 0) +#define LLVM_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true) +#define LLVM_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false) +#else +#define LLVM_LIKELY(EXPR) (EXPR) +#define LLVM_UNLIKELY(EXPR) (EXPR) +#endif + +/// LLVM_ATTRIBUTE_NOINLINE - On compilers where we have a directive to do so, +/// mark a method "not for inlining". +#if __has_attribute(noinline) || LLVM_GNUC_PREREQ(3, 4, 0) +#define LLVM_ATTRIBUTE_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_NOINLINE __declspec(noinline) +#else +#define LLVM_ATTRIBUTE_NOINLINE +#endif + +/// LLVM_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do +/// so, mark a method "always inline" because it is performance sensitive. GCC +/// 3.4 supported this but is buggy in various cases and produces unimplemented +/// errors, just use it in GCC 4.0 and later. +#if __has_attribute(always_inline) || LLVM_GNUC_PREREQ(4, 0, 0) +#define LLVM_ATTRIBUTE_ALWAYS_INLINE inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_ALWAYS_INLINE __forceinline +#else +#define LLVM_ATTRIBUTE_ALWAYS_INLINE inline +#endif + +#ifdef __GNUC__ +#define LLVM_ATTRIBUTE_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_NORETURN __declspec(noreturn) +#else +#define LLVM_ATTRIBUTE_NORETURN +#endif + +#if __has_attribute(returns_nonnull) || LLVM_GNUC_PREREQ(4, 9, 0) +#define LLVM_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_RETURNS_NONNULL _Ret_notnull_ +#else +#define LLVM_ATTRIBUTE_RETURNS_NONNULL +#endif + +/// \macro LLVM_ATTRIBUTE_RETURNS_NOALIAS Used to mark a function as returning a +/// pointer that does not alias any other valid pointer. +#ifdef __GNUC__ +#define LLVM_ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__)) +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_RETURNS_NOALIAS __declspec(restrict) +#else +#define LLVM_ATTRIBUTE_RETURNS_NOALIAS +#endif + +/// LLVM_FALLTHROUGH - Mark fallthrough cases in switch statements. +#if defined(__cplusplus) && __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(fallthrough) +#define LLVM_FALLTHROUGH [[fallthrough]] +#elif LLVM_HAS_CPP_ATTRIBUTE(gnu::fallthrough) +#define LLVM_FALLTHROUGH [[gnu::fallthrough]] +#elif __has_attribute(fallthrough) +#define LLVM_FALLTHROUGH __attribute__((fallthrough)) +#elif LLVM_HAS_CPP_ATTRIBUTE(clang::fallthrough) +#define LLVM_FALLTHROUGH [[clang::fallthrough]] +#else +#define LLVM_FALLTHROUGH +#endif + +/// LLVM_REQUIRE_CONSTANT_INITIALIZATION - Apply this to globals to ensure that +/// they are constant initialized. +#if LLVM_HAS_CPP_ATTRIBUTE(clang::require_constant_initialization) +#define LLVM_REQUIRE_CONSTANT_INITIALIZATION \ + [[clang::require_constant_initialization]] +#else +#define LLVM_REQUIRE_CONSTANT_INITIALIZATION +#endif + +/// LLVM_GSL_OWNER - Apply this to owning classes like SmallVector to enable +/// lifetime warnings. +#if LLVM_HAS_CPP_ATTRIBUTE(gsl::Owner) +#define LLVM_GSL_OWNER [[gsl::Owner]] +#else +#define LLVM_GSL_OWNER +#endif + +/// LLVM_GSL_POINTER - Apply this to non-owning classes like +/// StringRef to enable lifetime warnings. +#if LLVM_HAS_CPP_ATTRIBUTE(gsl::Pointer) +#define LLVM_GSL_POINTER [[gsl::Pointer]] +#else +#define LLVM_GSL_POINTER +#endif + +/// LLVM_EXTENSION - Support compilers where we have a keyword to suppress +/// pedantic diagnostics. +#ifdef __GNUC__ +#define LLVM_EXTENSION __extension__ +#else +#define LLVM_EXTENSION +#endif + +// LLVM_ATTRIBUTE_DEPRECATED(decl, "message") +// This macro will be removed. +// Use C++14's attribute instead: [[deprecated("message")]] +#define LLVM_ATTRIBUTE_DEPRECATED(decl, message) [[deprecated(message)]] decl + +/// LLVM_BUILTIN_UNREACHABLE - On compilers which support it, expands +/// to an expression which states that it is undefined behavior for the +/// compiler to reach this point. Otherwise is not defined. +#if __has_builtin(__builtin_unreachable) || LLVM_GNUC_PREREQ(4, 5, 0) +# define LLVM_BUILTIN_UNREACHABLE __builtin_unreachable() +#elif defined(_MSC_VER) +# define LLVM_BUILTIN_UNREACHABLE __assume(false) +#endif + +/// LLVM_BUILTIN_TRAP - On compilers which support it, expands to an expression +/// which causes the program to exit abnormally. +#if __has_builtin(__builtin_trap) || LLVM_GNUC_PREREQ(4, 3, 0) +# define LLVM_BUILTIN_TRAP __builtin_trap() +#elif defined(_MSC_VER) +// The __debugbreak intrinsic is supported by MSVC, does not require forward +// declarations involving platform-specific typedefs (unlike RaiseException), +// results in a call to vectored exception handlers, and encodes to a short +// instruction that still causes the trapping behavior we want. +# define LLVM_BUILTIN_TRAP __debugbreak() +#else +# define LLVM_BUILTIN_TRAP *(volatile int*)0x11 = 0 +#endif + +/// LLVM_BUILTIN_DEBUGTRAP - On compilers which support it, expands to +/// an expression which causes the program to break while running +/// under a debugger. +#if __has_builtin(__builtin_debugtrap) +# define LLVM_BUILTIN_DEBUGTRAP __builtin_debugtrap() +#elif defined(_MSC_VER) +// The __debugbreak intrinsic is supported by MSVC and breaks while +// running under the debugger, and also supports invoking a debugger +// when the OS is configured appropriately. +# define LLVM_BUILTIN_DEBUGTRAP __debugbreak() +#else +// Just continue execution when built with compilers that have no +// support. This is a debugging aid and not intended to force the +// program to abort if encountered. +# define LLVM_BUILTIN_DEBUGTRAP +#endif + +/// \macro LLVM_ASSUME_ALIGNED +/// Returns a pointer with an assumed alignment. +#if __has_builtin(__builtin_assume_aligned) || LLVM_GNUC_PREREQ(4, 7, 0) +# define LLVM_ASSUME_ALIGNED(p, a) __builtin_assume_aligned(p, a) +#elif defined(LLVM_BUILTIN_UNREACHABLE) +# define LLVM_ASSUME_ALIGNED(p, a) \ + (((uintptr_t(p) % (a)) == 0) ? (p) : (LLVM_BUILTIN_UNREACHABLE, (p))) +#else +# define LLVM_ASSUME_ALIGNED(p, a) (p) +#endif + +/// \macro LLVM_PACKED +/// Used to specify a packed structure. +/// LLVM_PACKED( +/// struct A { +/// int i; +/// int j; +/// int k; +/// long long l; +/// }); +/// +/// LLVM_PACKED_START +/// struct B { +/// int i; +/// int j; +/// int k; +/// long long l; +/// }; +/// LLVM_PACKED_END +#ifdef _MSC_VER +# define LLVM_PACKED(d) __pragma(pack(push, 1)) d __pragma(pack(pop)) +# define LLVM_PACKED_START __pragma(pack(push, 1)) +# define LLVM_PACKED_END __pragma(pack(pop)) +#else +# define LLVM_PACKED(d) d __attribute__((packed)) +# define LLVM_PACKED_START _Pragma("pack(push, 1)") +# define LLVM_PACKED_END _Pragma("pack(pop)") +#endif + +/// \macro LLVM_PTR_SIZE +/// A constant integer equivalent to the value of sizeof(void*). +/// Generally used in combination with alignas or when doing computation in the +/// preprocessor. +#ifdef __SIZEOF_POINTER__ +# define LLVM_PTR_SIZE __SIZEOF_POINTER__ +#elif defined(_WIN64) +# define LLVM_PTR_SIZE 8 +#elif defined(_WIN32) +# define LLVM_PTR_SIZE 4 +#elif defined(_MSC_VER) +# error "could not determine LLVM_PTR_SIZE as a constant int for MSVC" +#else +# define LLVM_PTR_SIZE sizeof(void *) +#endif + +/// \macro LLVM_MEMORY_SANITIZER_BUILD +/// Whether LLVM itself is built with MemorySanitizer instrumentation. +#if __has_feature(memory_sanitizer) +# define LLVM_MEMORY_SANITIZER_BUILD 1 +# include <sanitizer/msan_interface.h> +# define LLVM_NO_SANITIZE_MEMORY_ATTRIBUTE __attribute__((no_sanitize_memory)) +#else +# define LLVM_MEMORY_SANITIZER_BUILD 0 +# define __msan_allocated_memory(p, size) +# define __msan_unpoison(p, size) +# define LLVM_NO_SANITIZE_MEMORY_ATTRIBUTE +#endif + +/// \macro LLVM_ADDRESS_SANITIZER_BUILD +/// Whether LLVM itself is built with AddressSanitizer instrumentation. +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) +# define LLVM_ADDRESS_SANITIZER_BUILD 1 +# include <sanitizer/asan_interface.h> +#else +# define LLVM_ADDRESS_SANITIZER_BUILD 0 +# define __asan_poison_memory_region(p, size) +# define __asan_unpoison_memory_region(p, size) +#endif + +/// \macro LLVM_THREAD_SANITIZER_BUILD +/// Whether LLVM itself is built with ThreadSanitizer instrumentation. +#if __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__) +# define LLVM_THREAD_SANITIZER_BUILD 1 +#else +# define LLVM_THREAD_SANITIZER_BUILD 0 +#endif + +#if LLVM_THREAD_SANITIZER_BUILD +// Thread Sanitizer is a tool that finds races in code. +// See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations . +// tsan detects these exact functions by name. +#ifdef __cplusplus +extern "C" { +#endif +void AnnotateHappensAfter(const char *file, int line, const volatile void *cv); +void AnnotateHappensBefore(const char *file, int line, const volatile void *cv); +void AnnotateIgnoreWritesBegin(const char *file, int line); +void AnnotateIgnoreWritesEnd(const char *file, int line); +#ifdef __cplusplus +} +#endif + +// This marker is used to define a happens-before arc. The race detector will +// infer an arc from the begin to the end when they share the same pointer +// argument. +# define TsanHappensBefore(cv) AnnotateHappensBefore(__FILE__, __LINE__, cv) + +// This marker defines the destination of a happens-before arc. +# define TsanHappensAfter(cv) AnnotateHappensAfter(__FILE__, __LINE__, cv) + +// Ignore any races on writes between here and the next TsanIgnoreWritesEnd. +# define TsanIgnoreWritesBegin() AnnotateIgnoreWritesBegin(__FILE__, __LINE__) + +// Resume checking for racy writes. +# define TsanIgnoreWritesEnd() AnnotateIgnoreWritesEnd(__FILE__, __LINE__) +#else +# define TsanHappensBefore(cv) +# define TsanHappensAfter(cv) +# define TsanIgnoreWritesBegin() +# define TsanIgnoreWritesEnd() +#endif + +/// \macro LLVM_NO_SANITIZE +/// Disable a particular sanitizer for a function. +#if __has_attribute(no_sanitize) +#define LLVM_NO_SANITIZE(KIND) __attribute__((no_sanitize(KIND))) +#else +#define LLVM_NO_SANITIZE(KIND) +#endif + +/// Mark debug helper function definitions like dump() that should not be +/// stripped from debug builds. +/// Note that you should also surround dump() functions with +/// `#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)` so they do always +/// get stripped in release builds. +// FIXME: Move this to a private config.h as it's not usable in public headers. +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED +#else +#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE +#endif + +/// \macro LLVM_PRETTY_FUNCTION +/// Gets a user-friendly looking function signature for the current scope +/// using the best available method on each platform. The exact format of the +/// resulting string is implementation specific and non-portable, so this should +/// only be used, for example, for logging or diagnostics. +#if defined(_MSC_VER) +#define LLVM_PRETTY_FUNCTION __FUNCSIG__ +#elif defined(__GNUC__) || defined(__clang__) +#define LLVM_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define LLVM_PRETTY_FUNCTION __func__ +#endif + +/// \macro LLVM_THREAD_LOCAL +/// A thread-local storage specifier which can be used with globals, +/// extern globals, and static globals. +/// +/// This is essentially an extremely restricted analog to C++11's thread_local +/// support. It uses thread_local if available, falling back on gcc __thread +/// if not. __thread doesn't support many of the C++11 thread_local's +/// features. You should only use this for PODs that you can statically +/// initialize to some constant value. In almost all circumstances this is most +/// appropriate for use with a pointer, integer, or small aggregation of +/// pointers and integers. +#if LLVM_ENABLE_THREADS +#if __has_feature(cxx_thread_local) || defined(_MSC_VER) +#define LLVM_THREAD_LOCAL thread_local +#else +// Clang, GCC, and other compatible compilers used __thread prior to C++11 and +// we only need the restricted functionality that provides. +#define LLVM_THREAD_LOCAL __thread +#endif +#else // !LLVM_ENABLE_THREADS +// If threading is disabled entirely, this compiles to nothing and you get +// a normal global variable. +#define LLVM_THREAD_LOCAL +#endif + +/// \macro LLVM_ENABLE_EXCEPTIONS +/// Whether LLVM is built with exception support. +#if __has_feature(cxx_exceptions) +#define LLVM_ENABLE_EXCEPTIONS 1 +#elif defined(__GNUC__) && defined(__EXCEPTIONS) +#define LLVM_ENABLE_EXCEPTIONS 1 +#elif defined(_MSC_VER) && defined(_CPPUNWIND) +#define LLVM_ENABLE_EXCEPTIONS 1 +#endif + +#endif diff --git a/demangle/third_party/llvm/include/llvm/Demangle/Demangle.h b/demangle/third_party/llvm/include/llvm/Demangle/Demangle.h new file mode 100644 index 00000000..6133d0b9 --- /dev/null +++ b/demangle/third_party/llvm/include/llvm/Demangle/Demangle.h @@ -0,0 +1,133 @@ +//===--- 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 <cstddef> +#include <string> + +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, + MSDF_NoVariableType = 1 << 5, +}; + +/// 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); + +// Demangles a Rust v0 mangled symbol. +char *rustDemangle(const char *MangledName); + +// Demangles a D mangled symbol. +char *dlangDemangle(const char *MangledName); + +/// 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); + +bool nonMicrosoftDemangle(const char *MangledName, std::string &Result); + +/// "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<int>" 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 <special-name>. 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/demangle/third_party/llvm/include/llvm/Demangle/DemangleConfig.h b/demangle/third_party/llvm/include/llvm/Demangle/DemangleConfig.h new file mode 100644 index 00000000..2ff95dd8 --- /dev/null +++ b/demangle/third_party/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_DEMANGLECONFIG_H +#define LLVM_DEMANGLE_DEMANGLECONFIG_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/demangle/third_party/llvm/include/llvm/Demangle/ItaniumDemangle.h b/demangle/third_party/llvm/include/llvm/Demangle/ItaniumDemangle.h new file mode 100644 index 00000000..ea55f4a6 --- /dev/null +++ b/demangle/third_party/llvm/include/llvm/Demangle/ItaniumDemangle.h @@ -0,0 +1,5500 @@ +//===--- ItaniumDemangle.h -----------*- mode:c++;eval:(read-only-mode) -*-===// +// Do not edit! See README.txt. +// 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. +// There are two copies of this file in the source tree. The one under +// libcxxabi is the original and the one under llvm is the copy. Use +// cp-to-llvm.sh to update the copy. See README.txt for more details. +// +//===----------------------------------------------------------------------===// + +#ifndef DEMANGLE_ITANIUMDEMANGLE_H +#define DEMANGLE_ITANIUMDEMANGLE_H + +#include "DemangleConfig.h" +#include "StringView.h" +#include "Utility.h" +#include <algorithm> +#include <cassert> +#include <cctype> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <limits> +#include <new> +#include <utility> + +DEMANGLE_NAMESPACE_BEGIN + +template <class T, size_t N> class PODSmallVector { + static_assert(std::is_pod<T>::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<T *>(std::malloc(NewCap * sizeof(T))); + if (Tmp == nullptr) + std::terminate(); + std::copy(First, Last, Tmp); + First = Tmp; + } else { + First = static_cast<T *>(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; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void push_back(const T &Elem) { + if (Last == Cap) + reserve(size() * 2); + *Last++ = Elem; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + 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<size_t>(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); + } +}; + +// 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 NODE(NodeKind) K##NodeKind, +#include "ItaniumNodes.def" + }; + + /// 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, }; + + /// Operator precedence for expression nodes. Used to determine required + /// parens in expression emission. + enum class Prec { + Primary, + Postfix, + Unary, + Cast, + PtrMem, + Multiplicative, + Additive, + Shift, + Spaceship, + Relational, + Equality, + And, + Xor, + Ior, + AndIf, + OrIf, + Conditional, + Assign, + Comma, + Default, + }; + +private: + Kind K; + + Prec Precedence : 6; + + // 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 : 2; + + /// Track if this node is a (possibly qualified) array type. This can affect + /// how we format the output string. + Cache ArrayCache : 2; + + /// Track if this node is a (possibly qualified) function type. This can + /// affect how we format the output string. + Cache FunctionCache : 2; + +public: + Node(Kind K_, Prec Precedence_ = Prec::Primary, + Cache RHSComponentCache_ = Cache::No, Cache ArrayCache_ = Cache::No, + Cache FunctionCache_ = Cache::No) + : K(K_), Precedence(Precedence_), RHSComponentCache(RHSComponentCache_), + ArrayCache(ArrayCache_), FunctionCache(FunctionCache_) {} + Node(Kind K_, Cache RHSComponentCache_, Cache ArrayCache_ = Cache::No, + Cache FunctionCache_ = Cache::No) + : Node(K_, Prec::Primary, RHSComponentCache_, ArrayCache_, + FunctionCache_) {} + + /// Visit the most-derived object corresponding to this object. + template<typename Fn> 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<typename Fn> void match(Fn F) const; + + bool hasRHSComponent(OutputBuffer &OB) const { + if (RHSComponentCache != Cache::Unknown) + return RHSComponentCache == Cache::Yes; + return hasRHSComponentSlow(OB); + } + + bool hasArray(OutputBuffer &OB) const { + if (ArrayCache != Cache::Unknown) + return ArrayCache == Cache::Yes; + return hasArraySlow(OB); + } + + bool hasFunction(OutputBuffer &OB) const { + if (FunctionCache != Cache::Unknown) + return FunctionCache == Cache::Yes; + return hasFunctionSlow(OB); + } + + Kind getKind() const { return K; } + + Prec getPrecedence() const { return Precedence; } + + virtual bool hasRHSComponentSlow(OutputBuffer &) const { return false; } + virtual bool hasArraySlow(OutputBuffer &) const { return false; } + virtual bool hasFunctionSlow(OutputBuffer &) 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(OutputBuffer &) const { return this; } + + // Print this node as an expression operand, surrounding it in parentheses if + // its precedence is [Strictly] weaker than P. + void printAsOperand(OutputBuffer &OB, Prec P = Prec::Default, + bool StrictlyWorse = false) const { + bool Paren = + unsigned(getPrecedence()) >= unsigned(P) + unsigned(StrictlyWorse); + if (Paren) + OB.printOpen(); + print(OB); + if (Paren) + OB.printClose(); + } + + void print(OutputBuffer &OB) const { + printLeft(OB); + if (RHSComponentCache != Cache::No) + printRight(OB); + } + + // Print the "left" side of this Node into OutputBuffer. + virtual void printLeft(OutputBuffer &) 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(OutputBuffer &) 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(OutputBuffer &OB) const { + bool FirstElement = true; + for (size_t Idx = 0; Idx != NumElements; ++Idx) { + size_t BeforeComma = OB.getCurrentPosition(); + if (!FirstElement) + OB += ", "; + size_t AfterComma = OB.getCurrentPosition(); + Elements[Idx]->printAsOperand(OB, Node::Prec::Comma); + + // Elements[Idx] is an empty parameter pack expansion, we should erase the + // comma we just printed. + if (AfterComma == OB.getCurrentPosition()) { + OB.setCurrentPosition(BeforeComma); + continue; + } + + FirstElement = false; + } + } +}; + +struct NodeArrayNode : Node { + NodeArray Array; + NodeArrayNode(NodeArray Array_) : Node(KNodeArrayNode), Array(Array_) {} + + template<typename Fn> void match(Fn F) const { F(Array); } + + void printLeft(OutputBuffer &OB) const override { Array.printWithComma(OB); } +}; + +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<typename Fn> void match(Fn F) const { F(Prefix, Suffix); } + + void printLeft(OutputBuffer &OB) const override { + Prefix->print(OB); + OB += " ("; + OB += Suffix; + OB += ")"; + } +}; + +class VendorExtQualType final : public Node { + const Node *Ty; + StringView Ext; + const Node *TA; + +public: + VendorExtQualType(const Node *Ty_, StringView Ext_, const Node *TA_) + : Node(KVendorExtQualType), Ty(Ty_), Ext(Ext_), TA(TA_) {} + + const Node *getTy() const { return Ty; } + StringView getExt() const { return Ext; } + const Node *getTA() const { return TA; } + + template <typename Fn> void match(Fn F) const { F(Ty, Ext, TA); } + + void printLeft(OutputBuffer &OB) const override { + Ty->print(OB); + OB += " "; + OB += Ext; + if (TA != nullptr) + TA->print(OB); + } +}; + +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<Qualifiers>(Q1 | Q2); +} + +class QualType final : public Node { +protected: + const Qualifiers Quals; + const Node *Child; + + void printQuals(OutputBuffer &OB) const { + if (Quals & QualConst) + OB += " const"; + if (Quals & QualVolatile) + OB += " volatile"; + if (Quals & QualRestrict) + OB += " restrict"; + } + +public: + QualType(const Node *Child_, Qualifiers Quals_) + : Node(KQualType, Child_->RHSComponentCache, + Child_->ArrayCache, Child_->FunctionCache), + Quals(Quals_), Child(Child_) {} + + Qualifiers getQuals() const { return Quals; } + const Node *getChild() const { return Child; } + + template<typename Fn> void match(Fn F) const { F(Child, Quals); } + + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return Child->hasRHSComponent(OB); + } + bool hasArraySlow(OutputBuffer &OB) const override { + return Child->hasArray(OB); + } + bool hasFunctionSlow(OutputBuffer &OB) const override { + return Child->hasFunction(OB); + } + + void printLeft(OutputBuffer &OB) const override { + Child->printLeft(OB); + printQuals(OB); + } + + void printRight(OutputBuffer &OB) const override { Child->printRight(OB); } +}; + +class ConversionOperatorType final : public Node { + const Node *Ty; + +public: + ConversionOperatorType(const Node *Ty_) + : Node(KConversionOperatorType), Ty(Ty_) {} + + template<typename Fn> void match(Fn F) const { F(Ty); } + + void printLeft(OutputBuffer &OB) const override { + OB += "operator "; + Ty->print(OB); + } +}; + +class PostfixQualifiedType final : public Node { + const Node *Ty; + const StringView Postfix; + +public: + PostfixQualifiedType(const Node *Ty_, StringView Postfix_) + : Node(KPostfixQualifiedType), Ty(Ty_), Postfix(Postfix_) {} + + template<typename Fn> void match(Fn F) const { F(Ty, Postfix); } + + void printLeft(OutputBuffer &OB) const override { + Ty->printLeft(OB); + OB += Postfix; + } +}; + +class NameType final : public Node { + const StringView Name; + +public: + NameType(StringView Name_) : Node(KNameType), Name(Name_) {} + + template<typename Fn> void match(Fn F) const { F(Name); } + + StringView getName() const { return Name; } + StringView getBaseName() const override { return Name; } + + void printLeft(OutputBuffer &OB) const override { OB += Name; } +}; + +class BitIntType final : public Node { + const Node *Size; + bool Signed; + +public: + BitIntType(const Node *Size_, bool Signed_) + : Node(KBitIntType), Size(Size_), Signed(Signed_) {} + + template <typename Fn> void match(Fn F) const { F(Size, Signed); } + + void printLeft(OutputBuffer &OB) const override { + if (!Signed) + OB += "unsigned "; + OB += "_BitInt"; + OB.printOpen(); + Size->printAsOperand(OB); + OB.printClose(); + } +}; + +class ElaboratedTypeSpefType : public Node { + StringView Kind; + Node *Child; +public: + ElaboratedTypeSpefType(StringView Kind_, Node *Child_) + : Node(KElaboratedTypeSpefType), Kind(Kind_), Child(Child_) {} + + template<typename Fn> void match(Fn F) const { F(Kind, Child); } + + void printLeft(OutputBuffer &OB) const override { + OB += Kind; + OB += ' '; + Child->print(OB); + } +}; + +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<typename Fn> void match(Fn F) const { F(Base, Tag); } + + void printLeft(OutputBuffer &OB) const override { + Base->printLeft(OB); + OB += "[abi:"; + OB += Tag; + OB += "]"; + } +}; + +class EnableIfAttr : public Node { + NodeArray Conditions; +public: + EnableIfAttr(NodeArray Conditions_) + : Node(KEnableIfAttr), Conditions(Conditions_) {} + + template<typename Fn> void match(Fn F) const { F(Conditions); } + + void printLeft(OutputBuffer &OB) const override { + OB += " [enable_if:"; + Conditions.printWithComma(OB); + OB += ']'; + } +}; + +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<typename Fn> void match(Fn F) const { F(Ty, Protocol); } + + bool isObjCObject() const { + return Ty->getKind() == KNameType && + static_cast<const NameType *>(Ty)->getName() == "objc_object"; + } + + void printLeft(OutputBuffer &OB) const override { + Ty->print(OB); + OB += "<"; + OB += Protocol; + OB += ">"; + } +}; + +class PointerType final : public Node { + const Node *Pointee; + +public: + PointerType(const Node *Pointee_) + : Node(KPointerType, Pointee_->RHSComponentCache), + Pointee(Pointee_) {} + + const Node *getPointee() const { return Pointee; } + + template<typename Fn> void match(Fn F) const { F(Pointee); } + + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return Pointee->hasRHSComponent(OB); + } + + void printLeft(OutputBuffer &OB) const override { + // We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>. + if (Pointee->getKind() != KObjCProtoName || + !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) { + Pointee->printLeft(OB); + if (Pointee->hasArray(OB)) + OB += " "; + if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) + OB += "("; + OB += "*"; + } else { + const auto *objcProto = static_cast<const ObjCProtoName *>(Pointee); + OB += "id<"; + OB += objcProto->Protocol; + OB += ">"; + } + } + + void printRight(OutputBuffer &OB) const override { + if (Pointee->getKind() != KObjCProtoName || + !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) { + if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) + OB += ")"; + Pointee->printRight(OB); + } + } +}; + +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. + // + // A combination of a TemplateForwardReference and a back-ref Substitution + // from an ill-formed string may have created a cycle; use cycle detection to + // avoid looping forever. + std::pair<ReferenceKind, const Node *> collapse(OutputBuffer &OB) const { + auto SoFar = std::make_pair(RK, Pointee); + // Track the chain of nodes for the Floyd's 'tortoise and hare' + // cycle-detection algorithm, since getSyntaxNode(S) is impure + PODSmallVector<const Node *, 8> Prev; + for (;;) { + const Node *SN = SoFar.second->getSyntaxNode(OB); + if (SN->getKind() != KReferenceType) + break; + auto *RT = static_cast<const ReferenceType *>(SN); + SoFar.second = RT->Pointee; + SoFar.first = std::min(SoFar.first, RT->RK); + + // The middle of Prev is the 'slow' pointer moving at half speed + Prev.push_back(SoFar.second); + if (Prev.size() > 1 && SoFar.second == Prev[(Prev.size() - 1) / 2]) { + // Cycle detected + SoFar.second = nullptr; + break; + } + } + return SoFar; + } + +public: + ReferenceType(const Node *Pointee_, ReferenceKind RK_) + : Node(KReferenceType, Pointee_->RHSComponentCache), + Pointee(Pointee_), RK(RK_) {} + + template<typename Fn> void match(Fn F) const { F(Pointee, RK); } + + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return Pointee->hasRHSComponent(OB); + } + + void printLeft(OutputBuffer &OB) const override { + if (Printing) + return; + ScopedOverride<bool> SavePrinting(Printing, true); + std::pair<ReferenceKind, const Node *> Collapsed = collapse(OB); + if (!Collapsed.second) + return; + Collapsed.second->printLeft(OB); + if (Collapsed.second->hasArray(OB)) + OB += " "; + if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) + OB += "("; + + OB += (Collapsed.first == ReferenceKind::LValue ? "&" : "&&"); + } + void printRight(OutputBuffer &OB) const override { + if (Printing) + return; + ScopedOverride<bool> SavePrinting(Printing, true); + std::pair<ReferenceKind, const Node *> Collapsed = collapse(OB); + if (!Collapsed.second) + return; + if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) + OB += ")"; + Collapsed.second->printRight(OB); + } +}; + +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<typename Fn> void match(Fn F) const { F(ClassType, MemberType); } + + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return MemberType->hasRHSComponent(OB); + } + + void printLeft(OutputBuffer &OB) const override { + MemberType->printLeft(OB); + if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) + OB += "("; + else + OB += " "; + ClassType->print(OB); + OB += "::*"; + } + + void printRight(OutputBuffer &OB) const override { + if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) + OB += ")"; + MemberType->printRight(OB); + } +}; + +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<typename Fn> void match(Fn F) const { F(Base, Dimension); } + + bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } + bool hasArraySlow(OutputBuffer &) const override { return true; } + + void printLeft(OutputBuffer &OB) const override { Base->printLeft(OB); } + + void printRight(OutputBuffer &OB) const override { + if (OB.back() != ']') + OB += " "; + OB += "["; + if (Dimension) + Dimension->print(OB); + OB += "]"; + Base->printRight(OB); + } +}; + +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<typename Fn> void match(Fn F) const { + F(Ret, Params, CVQuals, RefQual, ExceptionSpec); + } + + bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } + bool hasFunctionSlow(OutputBuffer &) 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(OutputBuffer &OB) const override { + Ret->printLeft(OB); + OB += " "; + } + + void printRight(OutputBuffer &OB) const override { + OB.printOpen(); + Params.printWithComma(OB); + OB.printClose(); + Ret->printRight(OB); + + if (CVQuals & QualConst) + OB += " const"; + if (CVQuals & QualVolatile) + OB += " volatile"; + if (CVQuals & QualRestrict) + OB += " restrict"; + + if (RefQual == FrefQualLValue) + OB += " &"; + else if (RefQual == FrefQualRValue) + OB += " &&"; + + if (ExceptionSpec != nullptr) { + OB += ' '; + ExceptionSpec->print(OB); + } + } +}; + +class NoexceptSpec : public Node { + const Node *E; +public: + NoexceptSpec(const Node *E_) : Node(KNoexceptSpec), E(E_) {} + + template<typename Fn> void match(Fn F) const { F(E); } + + void printLeft(OutputBuffer &OB) const override { + OB += "noexcept"; + OB.printOpen(); + E->printAsOperand(OB); + OB.printClose(); + } +}; + +class DynamicExceptionSpec : public Node { + NodeArray Types; +public: + DynamicExceptionSpec(NodeArray Types_) + : Node(KDynamicExceptionSpec), Types(Types_) {} + + template<typename Fn> void match(Fn F) const { F(Types); } + + void printLeft(OutputBuffer &OB) const override { + OB += "throw"; + OB.printOpen(); + Types.printWithComma(OB); + OB.printClose(); + } +}; + +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<typename Fn> 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(OutputBuffer &) const override { return true; } + bool hasFunctionSlow(OutputBuffer &) const override { return true; } + + const Node *getName() const { return Name; } + + void printLeft(OutputBuffer &OB) const override { + if (Ret) { + Ret->printLeft(OB); + if (!Ret->hasRHSComponent(OB)) + OB += " "; + } + Name->print(OB); + } + + void printRight(OutputBuffer &OB) const override { + OB.printOpen(); + Params.printWithComma(OB); + OB.printClose(); + if (Ret) + Ret->printRight(OB); + + if (CVQuals & QualConst) + OB += " const"; + if (CVQuals & QualVolatile) + OB += " volatile"; + if (CVQuals & QualRestrict) + OB += " restrict"; + + if (RefQual == FrefQualLValue) + OB += " &"; + else if (RefQual == FrefQualRValue) + OB += " &&"; + + if (Attrs != nullptr) + Attrs->print(OB); + } +}; + +class LiteralOperator : public Node { + const Node *OpName; + +public: + LiteralOperator(const Node *OpName_) + : Node(KLiteralOperator), OpName(OpName_) {} + + template<typename Fn> void match(Fn F) const { F(OpName); } + + void printLeft(OutputBuffer &OB) const override { + OB += "operator\"\" "; + OpName->print(OB); + } +}; + +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<typename Fn> void match(Fn F) const { F(Special, Child); } + + void printLeft(OutputBuffer &OB) const override { + OB += Special; + Child->print(OB); + } +}; + +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<typename Fn> void match(Fn F) const { F(FirstType, SecondType); } + + void printLeft(OutputBuffer &OB) const override { + OB += "construction vtable for "; + FirstType->print(OB); + OB += "-in-"; + SecondType->print(OB); + } +}; + +struct NestedName : Node { + Node *Qual; + Node *Name; + + NestedName(Node *Qual_, Node *Name_) + : Node(KNestedName), Qual(Qual_), Name(Name_) {} + + template<typename Fn> void match(Fn F) const { F(Qual, Name); } + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputBuffer &OB) const override { + Qual->print(OB); + OB += "::"; + Name->print(OB); + } +}; + +struct ModuleName : Node { + ModuleName *Parent; + Node *Name; + bool IsPartition; + + ModuleName(ModuleName *Parent_, Node *Name_, bool IsPartition_ = false) + : Node(KModuleName), Parent(Parent_), Name(Name_), + IsPartition(IsPartition_) {} + + template <typename Fn> void match(Fn F) const { + F(Parent, Name, IsPartition); + } + + void printLeft(OutputBuffer &OB) const override { + if (Parent) + Parent->print(OB); + if (Parent || IsPartition) + OB += IsPartition ? ':' : '.'; + Name->print(OB); + } +}; + +struct ModuleEntity : Node { + ModuleName *Module; + Node *Name; + + ModuleEntity(ModuleName *Module_, Node *Name_) + : Node(KModuleEntity), Module(Module_), Name(Name_) {} + + template <typename Fn> void match(Fn F) const { F(Module, Name); } + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputBuffer &OB) const override { + Name->print(OB); + OB += '@'; + Module->print(OB); + } +}; + +struct LocalName : Node { + Node *Encoding; + Node *Entity; + + LocalName(Node *Encoding_, Node *Entity_) + : Node(KLocalName), Encoding(Encoding_), Entity(Entity_) {} + + template<typename Fn> void match(Fn F) const { F(Encoding, Entity); } + + void printLeft(OutputBuffer &OB) const override { + Encoding->print(OB); + OB += "::"; + Entity->print(OB); + } +}; + +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<typename Fn> void match(Fn F) const { F(Qualifier, Name); } + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputBuffer &OB) const override { + Qualifier->print(OB); + OB += "::"; + Name->print(OB); + } +}; + +class VectorType final : public Node { + const Node *BaseType; + const Node *Dimension; + +public: + VectorType(const Node *BaseType_, const Node *Dimension_) + : Node(KVectorType), BaseType(BaseType_), Dimension(Dimension_) {} + + const Node *getBaseType() const { return BaseType; } + const Node *getDimension() const { return Dimension; } + + template<typename Fn> void match(Fn F) const { F(BaseType, Dimension); } + + void printLeft(OutputBuffer &OB) const override { + BaseType->print(OB); + OB += " vector["; + if (Dimension) + Dimension->print(OB); + OB += "]"; + } +}; + +class PixelVectorType final : public Node { + const Node *Dimension; + +public: + PixelVectorType(const Node *Dimension_) + : Node(KPixelVectorType), Dimension(Dimension_) {} + + template<typename Fn> void match(Fn F) const { F(Dimension); } + + void printLeft(OutputBuffer &OB) const override { + // FIXME: This should demangle as "vector pixel". + OB += "pixel vector["; + Dimension->print(OB); + OB += "]"; + } +}; + +class BinaryFPType final : public Node { + const Node *Dimension; + +public: + BinaryFPType(const Node *Dimension_) + : Node(KBinaryFPType), Dimension(Dimension_) {} + + template<typename Fn> void match(Fn F) const { F(Dimension); } + + void printLeft(OutputBuffer &OB) const override { + OB += "_Float"; + Dimension->print(OB); + } +}; + +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 <lambda-sig> for a lambda with +/// explicit template arguments, which might be referenced in the parameter +/// types appearing later in the <lambda-sig>. +class SyntheticTemplateParamName final : public Node { + TemplateParamKind Kind; + unsigned Index; + +public: + SyntheticTemplateParamName(TemplateParamKind Kind_, unsigned Index_) + : Node(KSyntheticTemplateParamName), Kind(Kind_), Index(Index_) {} + + template<typename Fn> void match(Fn F) const { F(Kind, Index); } + + void printLeft(OutputBuffer &OB) const override { + switch (Kind) { + case TemplateParamKind::Type: + OB += "$T"; + break; + case TemplateParamKind::NonType: + OB += "$N"; + break; + case TemplateParamKind::Template: + OB += "$TT"; + break; + } + if (Index > 0) + OB << 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<typename Fn> void match(Fn F) const { F(Name); } + + void printLeft(OutputBuffer &OB) const override { OB += "typename "; } + + void printRight(OutputBuffer &OB) const override { Name->print(OB); } +}; + +/// 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<typename Fn> void match(Fn F) const { F(Name, Type); } + + void printLeft(OutputBuffer &OB) const override { + Type->printLeft(OB); + if (!Type->hasRHSComponent(OB)) + OB += " "; + } + + void printRight(OutputBuffer &OB) const override { + Name->print(OB); + Type->printRight(OB); + } +}; + +/// A template template parameter declaration, +/// 'template<typename T> 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<typename Fn> void match(Fn F) const { F(Name, Params); } + + void printLeft(OutputBuffer &OB) const override { + ScopedOverride<unsigned> LT(OB.GtIsGt, 0); + OB += "template<"; + Params.printWithComma(OB); + OB += "> typename "; + } + + void printRight(OutputBuffer &OB) const override { Name->print(OB); } +}; + +/// 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<typename Fn> void match(Fn F) const { F(Param); } + + void printLeft(OutputBuffer &OB) const override { + Param->printLeft(OB); + OB += "..."; + } + + void printRight(OutputBuffer &OB) const override { Param->printRight(OB); } +}; + +/// 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 <template-args> are found that apply to an +/// <encoding>, and is stored in the TemplateParams table. In order for this to +/// appear in the final AST, it has to referenced via a <template-param> (ie, +/// T_). +class ParameterPack final : public Node { + NodeArray Data; + + // Setup OutputBuffer for a pack expansion, unless we're already expanding + // one. + void initializePackExpansion(OutputBuffer &OB) const { + if (OB.CurrentPackMax == std::numeric_limits<unsigned>::max()) { + OB.CurrentPackMax = static_cast<unsigned>(Data.size()); + OB.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<typename Fn> void match(Fn F) const { F(Data); } + + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasRHSComponent(OB); + } + bool hasArraySlow(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasArray(OB); + } + bool hasFunctionSlow(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasFunction(OB); + } + const Node *getSyntaxNode(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() ? Data[Idx]->getSyntaxNode(OB) : this; + } + + void printLeft(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + if (Idx < Data.size()) + Data[Idx]->printLeft(OB); + } + void printRight(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + if (Idx < Data.size()) + Data[Idx]->printRight(OB); + } +}; + +/// A variadic template argument. This node represents an occurrence of +/// J<something>E in some <template-args>. It isn't itself unexpanded, unless +/// one of it's Elements is. The parser inserts a ParameterPack into the +/// TemplateParams table if the <template-args> this pack belongs to apply to an +/// <encoding>. +class TemplateArgumentPack final : public Node { + NodeArray Elements; +public: + TemplateArgumentPack(NodeArray Elements_) + : Node(KTemplateArgumentPack), Elements(Elements_) {} + + template<typename Fn> void match(Fn F) const { F(Elements); } + + NodeArray getElements() const { return Elements; } + + void printLeft(OutputBuffer &OB) const override { + Elements.printWithComma(OB); + } +}; + +/// 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<typename Fn> void match(Fn F) const { F(Child); } + + const Node *getChild() const { return Child; } + + void printLeft(OutputBuffer &OB) const override { + constexpr unsigned Max = std::numeric_limits<unsigned>::max(); + ScopedOverride<unsigned> SavePackIdx(OB.CurrentPackIndex, Max); + ScopedOverride<unsigned> SavePackMax(OB.CurrentPackMax, Max); + size_t StreamPos = OB.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(OB); + + // No ParameterPack was found in Child. This can occur if we've found a pack + // expansion on a <function-param>. + if (OB.CurrentPackMax == Max) { + OB += "..."; + return; + } + + // We found a ParameterPack, but it has no elements. Erase whatever we may + // of printed. + if (OB.CurrentPackMax == 0) { + OB.setCurrentPosition(StreamPos); + return; + } + + // Else, iterate through the rest of the elements in the pack. + for (unsigned I = 1, E = OB.CurrentPackMax; I < E; ++I) { + OB += ", "; + OB.CurrentPackIndex = I; + Child->print(OB); + } + } +}; + +class TemplateArgs final : public Node { + NodeArray Params; + +public: + TemplateArgs(NodeArray Params_) : Node(KTemplateArgs), Params(Params_) {} + + template<typename Fn> void match(Fn F) const { F(Params); } + + NodeArray getParams() { return Params; } + + void printLeft(OutputBuffer &OB) const override { + ScopedOverride<unsigned> LT(OB.GtIsGt, 0); + OB += "<"; + Params.printWithComma(OB); + OB += ">"; + } +}; + +/// 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<typename T> 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<typename Fn> void match(Fn F) const = delete; + + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + if (Printing) + return false; + ScopedOverride<bool> SavePrinting(Printing, true); + return Ref->hasRHSComponent(OB); + } + bool hasArraySlow(OutputBuffer &OB) const override { + if (Printing) + return false; + ScopedOverride<bool> SavePrinting(Printing, true); + return Ref->hasArray(OB); + } + bool hasFunctionSlow(OutputBuffer &OB) const override { + if (Printing) + return false; + ScopedOverride<bool> SavePrinting(Printing, true); + return Ref->hasFunction(OB); + } + const Node *getSyntaxNode(OutputBuffer &OB) const override { + if (Printing) + return this; + ScopedOverride<bool> SavePrinting(Printing, true); + return Ref->getSyntaxNode(OB); + } + + void printLeft(OutputBuffer &OB) const override { + if (Printing) + return; + ScopedOverride<bool> SavePrinting(Printing, true); + Ref->printLeft(OB); + } + void printRight(OutputBuffer &OB) const override { + if (Printing) + return; + ScopedOverride<bool> SavePrinting(Printing, true); + Ref->printRight(OB); + } +}; + +struct NameWithTemplateArgs : Node { + // name<template_args> + Node *Name; + Node *TemplateArgs; + + NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_) + : Node(KNameWithTemplateArgs), Name(Name_), TemplateArgs(TemplateArgs_) {} + + template<typename Fn> void match(Fn F) const { F(Name, TemplateArgs); } + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputBuffer &OB) const override { + Name->print(OB); + TemplateArgs->print(OB); + } +}; + +class GlobalQualifiedName final : public Node { + Node *Child; + +public: + GlobalQualifiedName(Node* Child_) + : Node(KGlobalQualifiedName), Child(Child_) {} + + template<typename Fn> void match(Fn F) const { F(Child); } + + StringView getBaseName() const override { return Child->getBaseName(); } + + void printLeft(OutputBuffer &OB) const override { + OB += "::"; + Child->print(OB); + } +}; + +enum class SpecialSubKind { + allocator, + basic_string, + string, + istream, + ostream, + iostream, +}; + +class SpecialSubstitution; +class ExpandedSpecialSubstitution : public Node { +protected: + SpecialSubKind SSK; + + ExpandedSpecialSubstitution(SpecialSubKind SSK_, Kind K_) + : Node(K_), SSK(SSK_) {} +public: + ExpandedSpecialSubstitution(SpecialSubKind SSK_) + : ExpandedSpecialSubstitution(SSK_, KExpandedSpecialSubstitution) {} + inline ExpandedSpecialSubstitution(SpecialSubstitution const *); + + template<typename Fn> void match(Fn F) const { F(SSK); } + +protected: + bool isInstantiation() const { + return unsigned(SSK) >= unsigned(SpecialSubKind::string); + } + + 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; + } + +private: + void printLeft(OutputBuffer &OB) const override { + OB << "std::" << getBaseName(); + if (isInstantiation()) { + OB << "<char, std::char_traits<char>"; + if (SSK == SpecialSubKind::string) + OB << ", std::allocator<char>"; + OB << ">"; + } + } +}; + +class SpecialSubstitution final : public ExpandedSpecialSubstitution { +public: + SpecialSubstitution(SpecialSubKind SSK_) + : ExpandedSpecialSubstitution(SSK_, KSpecialSubstitution) {} + + template<typename Fn> void match(Fn F) const { F(SSK); } + + StringView getBaseName() const override { + auto SV = ExpandedSpecialSubstitution::getBaseName (); + if (isInstantiation()) { + // The instantiations are typedefs that drop the "basic_" prefix. + assert(SV.startsWith("basic_")); + SV = SV.dropFront(sizeof("basic_") - 1); + } + return SV; + } + + void printLeft(OutputBuffer &OB) const override { + OB << "std::" << getBaseName(); + } +}; + +inline ExpandedSpecialSubstitution::ExpandedSpecialSubstitution( + SpecialSubstitution const *SS) + : ExpandedSpecialSubstitution(SS->SSK) {} + +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<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); } + + void printLeft(OutputBuffer &OB) const override { + if (IsDtor) + OB += "~"; + OB += Basename->getBaseName(); + } +}; + +class DtorName : public Node { + const Node *Base; + +public: + DtorName(const Node *Base_) : Node(KDtorName), Base(Base_) {} + + template<typename Fn> void match(Fn F) const { F(Base); } + + void printLeft(OutputBuffer &OB) const override { + OB += "~"; + Base->printLeft(OB); + } +}; + +class UnnamedTypeName : public Node { + const StringView Count; + +public: + UnnamedTypeName(StringView Count_) : Node(KUnnamedTypeName), Count(Count_) {} + + template<typename Fn> void match(Fn F) const { F(Count); } + + void printLeft(OutputBuffer &OB) const override { + OB += "'unnamed"; + OB += Count; + OB += "\'"; + } +}; + +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<typename Fn> void match(Fn F) const { + F(TemplateParams, Params, Count); + } + + void printDeclarator(OutputBuffer &OB) const { + if (!TemplateParams.empty()) { + ScopedOverride<unsigned> LT(OB.GtIsGt, 0); + OB += "<"; + TemplateParams.printWithComma(OB); + OB += ">"; + } + OB.printOpen(); + Params.printWithComma(OB); + OB.printClose(); + } + + void printLeft(OutputBuffer &OB) const override { + OB += "\'lambda"; + OB += Count; + OB += "\'"; + printDeclarator(OB); + } +}; + +class StructuredBindingName : public Node { + NodeArray Bindings; +public: + StructuredBindingName(NodeArray Bindings_) + : Node(KStructuredBindingName), Bindings(Bindings_) {} + + template<typename Fn> void match(Fn F) const { F(Bindings); } + + void printLeft(OutputBuffer &OB) const override { + OB.printOpen('['); + Bindings.printWithComma(OB); + OB.printClose(']'); + } +}; + +// -- 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_, + Prec Prec_) + : Node(KBinaryExpr, Prec_), LHS(LHS_), InfixOperator(InfixOperator_), + RHS(RHS_) {} + + template <typename Fn> void match(Fn F) const { + F(LHS, InfixOperator, RHS, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + bool ParenAll = OB.isGtInsideTemplateArgs() && + (InfixOperator == ">" || InfixOperator == ">>"); + if (ParenAll) + OB.printOpen(); + // Assignment is right associative, with special LHS precedence. + bool IsAssign = getPrecedence() == Prec::Assign; + LHS->printAsOperand(OB, IsAssign ? Prec::OrIf : getPrecedence(), !IsAssign); + // No space before comma operator + if (!(InfixOperator == ",")) + OB += " "; + OB += InfixOperator; + OB += " "; + RHS->printAsOperand(OB, getPrecedence(), IsAssign); + if (ParenAll) + OB.printClose(); + } +}; + +class ArraySubscriptExpr : public Node { + const Node *Op1; + const Node *Op2; + +public: + ArraySubscriptExpr(const Node *Op1_, const Node *Op2_, Prec Prec_) + : Node(KArraySubscriptExpr, Prec_), Op1(Op1_), Op2(Op2_) {} + + template <typename Fn> void match(Fn F) const { + F(Op1, Op2, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + Op1->printAsOperand(OB, getPrecedence()); + OB.printOpen('['); + Op2->printAsOperand(OB); + OB.printClose(']'); + } +}; + +class PostfixExpr : public Node { + const Node *Child; + const StringView Operator; + +public: + PostfixExpr(const Node *Child_, StringView Operator_, Prec Prec_) + : Node(KPostfixExpr, Prec_), Child(Child_), Operator(Operator_) {} + + template <typename Fn> void match(Fn F) const { + F(Child, Operator, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + Child->printAsOperand(OB, getPrecedence(), true); + OB += 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_, + Prec Prec_) + : Node(KConditionalExpr, Prec_), Cond(Cond_), Then(Then_), Else(Else_) {} + + template <typename Fn> void match(Fn F) const { + F(Cond, Then, Else, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + Cond->printAsOperand(OB, getPrecedence()); + OB += " ? "; + Then->printAsOperand(OB); + OB += " : "; + Else->printAsOperand(OB, Prec::Assign, true); + } +}; + +class MemberExpr : public Node { + const Node *LHS; + const StringView Kind; + const Node *RHS; + +public: + MemberExpr(const Node *LHS_, StringView Kind_, const Node *RHS_, Prec Prec_) + : Node(KMemberExpr, Prec_), LHS(LHS_), Kind(Kind_), RHS(RHS_) {} + + template <typename Fn> void match(Fn F) const { + F(LHS, Kind, RHS, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + LHS->printAsOperand(OB, getPrecedence(), true); + OB += Kind; + RHS->printAsOperand(OB, getPrecedence(), false); + } +}; + +class SubobjectExpr : public Node { + const Node *Type; + const Node *SubExpr; + StringView Offset; + NodeArray UnionSelectors; + bool OnePastTheEnd; + +public: + SubobjectExpr(const Node *Type_, const Node *SubExpr_, StringView Offset_, + NodeArray UnionSelectors_, bool OnePastTheEnd_) + : Node(KSubobjectExpr), Type(Type_), SubExpr(SubExpr_), Offset(Offset_), + UnionSelectors(UnionSelectors_), OnePastTheEnd(OnePastTheEnd_) {} + + template<typename Fn> void match(Fn F) const { + F(Type, SubExpr, Offset, UnionSelectors, OnePastTheEnd); + } + + void printLeft(OutputBuffer &OB) const override { + SubExpr->print(OB); + OB += ".<"; + Type->print(OB); + OB += " at offset "; + if (Offset.empty()) { + OB += "0"; + } else if (Offset[0] == 'n') { + OB += "-"; + OB += Offset.dropFront(); + } else { + OB += Offset; + } + OB += ">"; + } +}; + +class EnclosingExpr : public Node { + const StringView Prefix; + const Node *Infix; + const StringView Postfix; + +public: + EnclosingExpr(StringView Prefix_, const Node *Infix_, + Prec Prec_ = Prec::Primary) + : Node(KEnclosingExpr, Prec_), Prefix(Prefix_), Infix(Infix_) {} + + template <typename Fn> void match(Fn F) const { + F(Prefix, Infix, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + OB += Prefix; + OB.printOpen(); + Infix->print(OB); + OB.printClose(); + OB += Postfix; + } +}; + +class CastExpr : public Node { + // cast_kind<to>(from) + const StringView CastKind; + const Node *To; + const Node *From; + +public: + CastExpr(StringView CastKind_, const Node *To_, const Node *From_, Prec Prec_) + : Node(KCastExpr, Prec_), CastKind(CastKind_), To(To_), From(From_) {} + + template <typename Fn> void match(Fn F) const { + F(CastKind, To, From, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + OB += CastKind; + { + ScopedOverride<unsigned> LT(OB.GtIsGt, 0); + OB += "<"; + To->printLeft(OB); + OB += ">"; + } + OB.printOpen(); + From->printAsOperand(OB); + OB.printClose(); + } +}; + +class SizeofParamPackExpr : public Node { + const Node *Pack; + +public: + SizeofParamPackExpr(const Node *Pack_) + : Node(KSizeofParamPackExpr), Pack(Pack_) {} + + template<typename Fn> void match(Fn F) const { F(Pack); } + + void printLeft(OutputBuffer &OB) const override { + OB += "sizeof..."; + OB.printOpen(); + ParameterPackExpansion PPE(Pack); + PPE.printLeft(OB); + OB.printClose(); + } +}; + +class CallExpr : public Node { + const Node *Callee; + NodeArray Args; + +public: + CallExpr(const Node *Callee_, NodeArray Args_, Prec Prec_) + : Node(KCallExpr, Prec_), Callee(Callee_), Args(Args_) {} + + template <typename Fn> void match(Fn F) const { + F(Callee, Args, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + Callee->print(OB); + OB.printOpen(); + Args.printWithComma(OB); + OB.printClose(); + } +}; + +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_, Prec Prec_) + : Node(KNewExpr, Prec_), ExprList(ExprList_), Type(Type_), + InitList(InitList_), IsGlobal(IsGlobal_), IsArray(IsArray_) {} + + template<typename Fn> void match(Fn F) const { + F(ExprList, Type, InitList, IsGlobal, IsArray, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + if (IsGlobal) + OB += "::"; + OB += "new"; + if (IsArray) + OB += "[]"; + if (!ExprList.empty()) { + OB.printOpen(); + ExprList.printWithComma(OB); + OB.printClose(); + } + OB += " "; + Type->print(OB); + if (!InitList.empty()) { + OB.printOpen(); + InitList.printWithComma(OB); + OB.printClose(); + } + } +}; + +class DeleteExpr : public Node { + Node *Op; + bool IsGlobal; + bool IsArray; + +public: + DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_, Prec Prec_) + : Node(KDeleteExpr, Prec_), Op(Op_), IsGlobal(IsGlobal_), + IsArray(IsArray_) {} + + template <typename Fn> void match(Fn F) const { + F(Op, IsGlobal, IsArray, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + if (IsGlobal) + OB += "::"; + OB += "delete"; + if (IsArray) + OB += "[]"; + OB += ' '; + Op->print(OB); + } +}; + +class PrefixExpr : public Node { + StringView Prefix; + Node *Child; + +public: + PrefixExpr(StringView Prefix_, Node *Child_, Prec Prec_) + : Node(KPrefixExpr, Prec_), Prefix(Prefix_), Child(Child_) {} + + template <typename Fn> void match(Fn F) const { + F(Prefix, Child, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + OB += Prefix; + Child->printAsOperand(OB, getPrecedence()); + } +}; + +class FunctionParam : public Node { + StringView Number; + +public: + FunctionParam(StringView Number_) : Node(KFunctionParam), Number(Number_) {} + + template<typename Fn> void match(Fn F) const { F(Number); } + + void printLeft(OutputBuffer &OB) const override { + OB += "fp"; + OB += Number; + } +}; + +class ConversionExpr : public Node { + const Node *Type; + NodeArray Expressions; + +public: + ConversionExpr(const Node *Type_, NodeArray Expressions_, Prec Prec_) + : Node(KConversionExpr, Prec_), Type(Type_), Expressions(Expressions_) {} + + template <typename Fn> void match(Fn F) const { + F(Type, Expressions, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + OB.printOpen(); + Type->print(OB); + OB.printClose(); + OB.printOpen(); + Expressions.printWithComma(OB); + OB.printClose(); + } +}; + +class PointerToMemberConversionExpr : public Node { + const Node *Type; + const Node *SubExpr; + StringView Offset; + +public: + PointerToMemberConversionExpr(const Node *Type_, const Node *SubExpr_, + StringView Offset_, Prec Prec_) + : Node(KPointerToMemberConversionExpr, Prec_), Type(Type_), + SubExpr(SubExpr_), Offset(Offset_) {} + + template <typename Fn> void match(Fn F) const { + F(Type, SubExpr, Offset, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + OB.printOpen(); + Type->print(OB); + OB.printClose(); + OB.printOpen(); + SubExpr->print(OB); + OB.printClose(); + } +}; + +class InitListExpr : public Node { + const Node *Ty; + NodeArray Inits; +public: + InitListExpr(const Node *Ty_, NodeArray Inits_) + : Node(KInitListExpr), Ty(Ty_), Inits(Inits_) {} + + template<typename Fn> void match(Fn F) const { F(Ty, Inits); } + + void printLeft(OutputBuffer &OB) const override { + if (Ty) + Ty->print(OB); + OB += '{'; + Inits.printWithComma(OB); + OB += '}'; + } +}; + +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<typename Fn> void match(Fn F) const { F(Elem, Init, IsArray); } + + void printLeft(OutputBuffer &OB) const override { + if (IsArray) { + OB += '['; + Elem->print(OB); + OB += ']'; + } else { + OB += '.'; + Elem->print(OB); + } + if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) + OB += " = "; + Init->print(OB); + } +}; + +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<typename Fn> void match(Fn F) const { F(First, Last, Init); } + + void printLeft(OutputBuffer &OB) const override { + OB += '['; + First->print(OB); + OB += " ... "; + Last->print(OB); + OB += ']'; + if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) + OB += " = "; + Init->print(OB); + } +}; + +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<typename Fn> void match(Fn F) const { + F(IsLeftFold, OperatorName, Pack, Init); + } + + void printLeft(OutputBuffer &OB) const override { + auto PrintPack = [&] { + OB.printOpen(); + ParameterPackExpansion(Pack).print(OB); + OB.printClose(); + }; + + OB.printOpen(); + // Either '[init op ]... op pack' or 'pack op ...[ op init]' + // Refactored to '[(init|pack) op ]...[ op (pack|init)]' + // Fold expr operands are cast-expressions + if (!IsLeftFold || Init != nullptr) { + // '(init|pack) op ' + if (IsLeftFold) + Init->printAsOperand(OB, Prec::Cast, true); + else + PrintPack(); + OB << " " << OperatorName << " "; + } + OB << "..."; + if (IsLeftFold || Init != nullptr) { + // ' op (init|pack)' + OB << " " << OperatorName << " "; + if (IsLeftFold) + PrintPack(); + else + Init->printAsOperand(OB, Prec::Cast, true); + } + OB.printClose(); + } +}; + +class ThrowExpr : public Node { + const Node *Op; + +public: + ThrowExpr(const Node *Op_) : Node(KThrowExpr), Op(Op_) {} + + template<typename Fn> void match(Fn F) const { F(Op); } + + void printLeft(OutputBuffer &OB) const override { + OB += "throw "; + Op->print(OB); + } +}; + +class BoolExpr : public Node { + bool Value; + +public: + BoolExpr(bool Value_) : Node(KBoolExpr), Value(Value_) {} + + template<typename Fn> void match(Fn F) const { F(Value); } + + void printLeft(OutputBuffer &OB) const override { + OB += Value ? StringView("true") : StringView("false"); + } +}; + +class StringLiteral : public Node { + const Node *Type; + +public: + StringLiteral(const Node *Type_) : Node(KStringLiteral), Type(Type_) {} + + template<typename Fn> void match(Fn F) const { F(Type); } + + void printLeft(OutputBuffer &OB) const override { + OB += "\"<"; + Type->print(OB); + OB += ">\""; + } +}; + +class LambdaExpr : public Node { + const Node *Type; + +public: + LambdaExpr(const Node *Type_) : Node(KLambdaExpr), Type(Type_) {} + + template<typename Fn> void match(Fn F) const { F(Type); } + + void printLeft(OutputBuffer &OB) const override { + OB += "[]"; + if (Type->getKind() == KClosureTypeName) + static_cast<const ClosureTypeName *>(Type)->printDeclarator(OB); + OB += "{...}"; + } +}; + +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<typename Fn> void match(Fn F) const { F(Ty, Integer); } + + void printLeft(OutputBuffer &OB) const override { + OB.printOpen(); + Ty->print(OB); + OB.printClose(); + + if (Integer[0] == 'n') + OB << "-" << Integer.dropFront(1); + else + OB << Integer; + } +}; + +class IntegerLiteral : public Node { + StringView Type; + StringView Value; + +public: + IntegerLiteral(StringView Type_, StringView Value_) + : Node(KIntegerLiteral), Type(Type_), Value(Value_) {} + + template<typename Fn> void match(Fn F) const { F(Type, Value); } + + void printLeft(OutputBuffer &OB) const override { + if (Type.size() > 3) { + OB.printOpen(); + OB += Type; + OB.printClose(); + } + + if (Value[0] == 'n') { + OB += '-'; + OB += Value.dropFront(1); + } else + OB += Value; + + if (Type.size() <= 3) + OB += Type; + } +}; + +template <class Float> 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 Float> 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<typename Fn> void match(Fn F) const { F(Contents); } + + void printLeft(OutputBuffer &OB) const override { + const char *first = Contents.begin(); + const char *last = Contents.end() + 1; + + const size_t N = FloatData<Float>::mangled_size; + if (static_cast<std::size_t>(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<unsigned>(*t - '0') + : static_cast<unsigned>(*t - 'a' + 10); + ++t; + unsigned d0 = isdigit(*t) ? static_cast<unsigned>(*t - '0') + : static_cast<unsigned>(*t - 'a' + 10); + *e = static_cast<char>((d1 << 4) + d0); + } +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + std::reverse(buf, e); +#endif + char num[FloatData<Float>::max_demangled_size] = {0}; + int n = snprintf(num, sizeof(num), FloatData<Float>::spec, value); + OB += StringView(num, num + n); + } + } +}; + +using FloatLiteral = FloatLiteralImpl<float>; +using DoubleLiteral = FloatLiteralImpl<double>; +using LongDoubleLiteral = FloatLiteralImpl<long double>; + +/// Visit the node. Calls \c F(P), where \c P is the node cast to the +/// appropriate derived class. +template<typename Fn> +void Node::visit(Fn F) const { + switch (K) { +#define NODE(X) \ + case K##X: \ + return F(static_cast<const X *>(this)); +#include "ItaniumNodes.def" + } + assert(0 && "unknown mangling node kind"); +} + +/// Determine the kind of a node from its type. +template<typename NodeT> struct NodeKind; +#define NODE(X) \ + template <> struct NodeKind<X> { \ + static constexpr Node::Kind Kind = Node::K##X; \ + static constexpr const char *name() { return #X; } \ + }; +#include "ItaniumNodes.def" + +template <typename Derived, typename Alloc> 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<Node *, 32> 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<Node *, 32> Subs; + + using TemplateParamList = PODSmallVector<Node *, 8>; + + class ScopedTemplateParamList { + AbstractManglingParser *Parser; + size_t OldNumTemplateParamLists; + TemplateParamList Params; + + public: + ScopedTemplateParamList(AbstractManglingParser *TheParser) + : Parser(TheParser), + OldNumTemplateParamLists(TheParser->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<TemplateParamList *, 4> TemplateParams; + + // Set of unresolved forward <template-param> references. These can occur in a + // conversion operator's type, and are resolved in the enclosing <encoding>. + PODSmallVector<ForwardTemplateReference *, 4> 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<Derived &>(*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 <class T, class... Args> Node *make(Args &&... args) { + return ASTAllocator.template makeNode<T>(std::forward<Args>(args)...); + } + + template <class It> NodeArray makeNodeArray(It begin, It end) { + size_t sz = static_cast<size_t>(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) const { + if (static_cast<size_t>(Last - First) <= Lookahead) + return '\0'; + return First[Lookahead]; + } + + size_t numLeft() const { return static_cast<size_t>(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 <expr> production. + Node *parseExpr(); + Node *parsePrefixExpr(StringView Kind, Node::Prec Prec); + Node *parseBinaryExpr(StringView Kind, Node::Prec Prec); + Node *parseIntegerLiteral(StringView Lit); + Node *parseExprPrimary(); + template <class Float> Node *parseFloatingLiteral(); + Node *parseFunctionParam(); + Node *parseConversionExpr(); + Node *parseBracedExpr(); + Node *parseFoldExpr(); + Node *parsePointerToMemberConversionExpr(Node::Prec Prec); + Node *parseSubobjectExpr(); + + /// Parse the <type> 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 <name> that is being parsed. This + /// information is only pertinent if the <name> refers to an <encoding>. + 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 <name> production> + Node *parseName(NameState *State = nullptr); + Node *parseLocalName(NameState *State); + Node *parseOperatorName(NameState *State); + bool parseModuleNameOpt(ModuleName *&Module); + Node *parseUnqualifiedName(NameState *State, Node *Scope, ModuleName *Module); + Node *parseUnnamedTypeName(NameState *State); + Node *parseSourceName(NameState *State); + Node *parseUnscopedName(NameState *State, bool *isSubstName); + Node *parseNestedName(NameState *State); + Node *parseCtorDtorName(Node *&SoFar, NameState *State); + + Node *parseAbiTags(Node *N); + + struct OperatorInfo { + enum OIKind : unsigned char { + Prefix, // Prefix unary: @ expr + Postfix, // Postfix unary: expr @ + Binary, // Binary: lhs @ rhs + Array, // Array index: lhs [ rhs ] + Member, // Member access: lhs @ rhs + New, // New + Del, // Delete + Call, // Function call: expr (expr*) + CCast, // C cast: (type)expr + Conditional, // Conditional: expr ? expr : expr + NameOnly, // Overload only, not allowed in expression. + // Below do not have operator names + NamedCast, // Named cast, @<type>(expr) + OfIdOp, // alignof, sizeof, typeid + + Unnameable = NamedCast, + }; + char Enc[2]; // Encoding + OIKind Kind; // Kind of operator + bool Flag : 1; // Entry-specific flag + Node::Prec Prec : 7; // Precedence + const char *Name; // Spelling + + public: + constexpr OperatorInfo(const char (&E)[3], OIKind K, bool F, Node::Prec P, + const char *N) + : Enc{E[0], E[1]}, Kind{K}, Flag{F}, Prec{P}, Name{N} {} + + public: + bool operator<(const OperatorInfo &Other) const { + return *this < Other.Enc; + } + bool operator<(const char *Peek) const { + return Enc[0] < Peek[0] || (Enc[0] == Peek[0] && Enc[1] < Peek[1]); + } + bool operator==(const char *Peek) const { + return Enc[0] == Peek[0] && Enc[1] == Peek[1]; + } + bool operator!=(const char *Peek) const { return !this->operator==(Peek); } + + public: + StringView getSymbol() const { + StringView Res = Name; + if (Kind < Unnameable) { + assert(Res.startsWith("operator") && + "operator name does not start with 'operator'"); + Res = Res.dropFront(sizeof("operator") - 1); + Res.consumeFront(' '); + } + return Res; + } + StringView getName() const { return Name; } + OIKind getKind() const { return Kind; } + bool getFlag() const { return Flag; } + Node::Prec getPrecedence() const { return Prec; } + }; + static const OperatorInfo Ops[]; + static const size_t NumOps; + const OperatorInfo *parseOperatorEncoding(); + + /// Parse the <unresolved-name> production. + Node *parseUnresolvedName(bool Global); + 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); + +// <name> ::= <nested-name> // N +// ::= <local-name> # See Scope Encoding below // Z +// ::= <unscoped-template-name> <template-args> +// ::= <unscoped-name> +// +// <unscoped-template-name> ::= <unscoped-name> +// ::= <substitution> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseName(NameState *State) { + if (look() == 'N') + return getDerived().parseNestedName(State); + if (look() == 'Z') + return getDerived().parseLocalName(State); + + Node *Result = nullptr; + bool IsSubst = false; + + Result = getDerived().parseUnscopedName(State, &IsSubst); + if (!Result) + return nullptr; + + if (look() == 'I') { + // ::= <unscoped-template-name> <template-args> + if (!IsSubst) + // An unscoped-template-name is substitutable. + Subs.push_back(Result); + Node *TA = getDerived().parseTemplateArgs(State != nullptr); + if (TA == nullptr) + return nullptr; + if (State) + State->EndsWithTemplateArgs = true; + Result = make<NameWithTemplateArgs>(Result, TA); + } else if (IsSubst) { + // The substitution case must be followed by <template-args>. + return nullptr; + } + + return Result; +} + +// <local-name> := Z <function encoding> E <entity name> [<discriminator>] +// := Z <function encoding> E s [<discriminator>] +// := Z <function encoding> Ed [ <parameter number> ] _ <entity name> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::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<NameType>("string literal"); + if (!StringLitName) + return nullptr; + return make<LocalName>(Encoding, StringLitName); + } + + if (consumeIf('d')) { + parseNumber(true); + if (!consumeIf('_')) + return nullptr; + Node *N = getDerived().parseName(State); + if (N == nullptr) + return nullptr; + return make<LocalName>(Encoding, N); + } + + Node *Entity = getDerived().parseName(State); + if (Entity == nullptr) + return nullptr; + First = parse_discriminator(First, Last); + return make<LocalName>(Encoding, Entity); +} + +// <unscoped-name> ::= <unqualified-name> +// ::= St <unqualified-name> # ::std:: +// [*] extension +template <typename Derived, typename Alloc> +Node * +AbstractManglingParser<Derived, Alloc>::parseUnscopedName(NameState *State, + bool *IsSubst) { + + Node *Std = nullptr; + if (consumeIf("St")) { + Std = make<NameType>("std"); + if (Std == nullptr) + return nullptr; + } + + Node *Res = nullptr; + ModuleName *Module = nullptr; + if (look() == 'S') { + Node *S = getDerived().parseSubstitution(); + if (!S) + return nullptr; + if (S->getKind() == Node::KModuleName) + Module = static_cast<ModuleName *>(S); + else if (IsSubst && Std == nullptr) { + Res = S; + *IsSubst = true; + } else { + return nullptr; + } + } + + if (Res == nullptr || Std != nullptr) { + Res = getDerived().parseUnqualifiedName(State, Std, Module); + } + + return Res; +} + +// <unqualified-name> ::= [<module-name>] L? <operator-name> [<abi-tags>] +// ::= [<module-name>] <ctor-dtor-name> [<abi-tags>] +// ::= [<module-name>] L? <source-name> [<abi-tags>] +// ::= [<module-name>] L? <unnamed-type-name> [<abi-tags>] +// # structured binding declaration +// ::= [<module-name>] L? DC <source-name>+ E +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseUnqualifiedName( + NameState *State, Node *Scope, ModuleName *Module) { + if (getDerived().parseModuleNameOpt(Module)) + return nullptr; + + consumeIf('L'); + + Node *Result; + if (look() >= '1' && look() <= '9') { + Result = getDerived().parseSourceName(State); + } else if (look() == 'U') { + Result = getDerived().parseUnnamedTypeName(State); + } else if (consumeIf("DC")) { + // Structured binding + 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<StructuredBindingName>(popTrailingNodeArray(BindingsBegin)); + } else if (look() == 'C' || look() == 'D') { + // A <ctor-dtor-name>. + if (Scope == nullptr || Module != nullptr) + return nullptr; + Result = getDerived().parseCtorDtorName(Scope, State); + } else { + Result = getDerived().parseOperatorName(State); + } + + if (Result != nullptr && Module != nullptr) + Result = make<ModuleEntity>(Module, Result); + if (Result != nullptr) + Result = getDerived().parseAbiTags(Result); + if (Result != nullptr && Scope != nullptr) + Result = make<NestedName>(Scope, Result); + + return Result; +} + +// <module-name> ::= <module-subname> +// ::= <module-name> <module-subname> +// ::= <substitution> # passed in by caller +// <module-subname> ::= W <source-name> +// ::= W P <source-name> +template <typename Derived, typename Alloc> +bool AbstractManglingParser<Derived, Alloc>::parseModuleNameOpt( + ModuleName *&Module) { + while (consumeIf('W')) { + bool IsPartition = consumeIf('P'); + Node *Sub = getDerived().parseSourceName(nullptr); + if (!Sub) + return true; + Module = + static_cast<ModuleName *>(make<ModuleName>(Module, Sub, IsPartition)); + Subs.push_back(Module); + } + + return false; +} + +// <unnamed-type-name> ::= Ut [<nonnegative number>] _ +// ::= <closure-type-name> +// +// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ +// +// <lambda-sig> ::= <parameter type>+ # Parameter types or "v" if the lambda has no parameters +template <typename Derived, typename Alloc> +Node * +AbstractManglingParser<Derived, Alloc>::parseUnnamedTypeName(NameState *State) { + // <template-params> refer to the innermost <template-args>. 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<UnnamedTypeName>(Count); + } + if (consumeIf("Ul")) { + ScopedOverride<size_t> 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<typename T> void f( + // decltype([](decltype([]<typename T>(T v) {}), + // auto) {})) {} + // template<typename T> void f( + // decltype([](decltype([]<typename T>(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<ClosureTypeName>(TempParams, Params, Count); + } + if (consumeIf("Ub")) { + (void)parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make<NameType>("'block-literal'"); + } + return nullptr; +} + +// <source-name> ::= <positive length number> <identifier> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::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<NameType>("(anonymous namespace)"); + return make<NameType>(Name); +} + +// Operator encodings +template <typename Derived, typename Alloc> +const typename AbstractManglingParser< + Derived, Alloc>::OperatorInfo AbstractManglingParser<Derived, + Alloc>::Ops[] = { + // Keep ordered by encoding + {"aN", OperatorInfo::Binary, false, Node::Prec::Assign, "operator&="}, + {"aS", OperatorInfo::Binary, false, Node::Prec::Assign, "operator="}, + {"aa", OperatorInfo::Binary, false, Node::Prec::AndIf, "operator&&"}, + {"ad", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator&"}, + {"an", OperatorInfo::Binary, false, Node::Prec::And, "operator&"}, + {"at", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Unary, "alignof "}, + {"aw", OperatorInfo::NameOnly, false, Node::Prec::Primary, + "operator co_await"}, + {"az", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Unary, "alignof "}, + {"cc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, "const_cast"}, + {"cl", OperatorInfo::Call, false, Node::Prec::Postfix, "operator()"}, + {"cm", OperatorInfo::Binary, false, Node::Prec::Comma, "operator,"}, + {"co", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator~"}, + {"cv", OperatorInfo::CCast, false, Node::Prec::Cast, "operator"}, // C Cast + {"dV", OperatorInfo::Binary, false, Node::Prec::Assign, "operator/="}, + {"da", OperatorInfo::Del, /*Ary*/ true, Node::Prec::Unary, + "operator delete[]"}, + {"dc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, "dynamic_cast"}, + {"de", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator*"}, + {"dl", OperatorInfo::Del, /*Ary*/ false, Node::Prec::Unary, + "operator delete"}, + {"ds", OperatorInfo::Member, /*Named*/ false, Node::Prec::PtrMem, + "operator.*"}, + {"dt", OperatorInfo::Member, /*Named*/ false, Node::Prec::Postfix, + "operator."}, + {"dv", OperatorInfo::Binary, false, Node::Prec::Assign, "operator/"}, + {"eO", OperatorInfo::Binary, false, Node::Prec::Assign, "operator^="}, + {"eo", OperatorInfo::Binary, false, Node::Prec::Xor, "operator^"}, + {"eq", OperatorInfo::Binary, false, Node::Prec::Equality, "operator=="}, + {"ge", OperatorInfo::Binary, false, Node::Prec::Relational, "operator>="}, + {"gt", OperatorInfo::Binary, false, Node::Prec::Relational, "operator>"}, + {"ix", OperatorInfo::Array, false, Node::Prec::Postfix, "operator[]"}, + {"lS", OperatorInfo::Binary, false, Node::Prec::Assign, "operator<<="}, + {"le", OperatorInfo::Binary, false, Node::Prec::Relational, "operator<="}, + {"ls", OperatorInfo::Binary, false, Node::Prec::Shift, "operator<<"}, + {"lt", OperatorInfo::Binary, false, Node::Prec::Relational, "operator<"}, + {"mI", OperatorInfo::Binary, false, Node::Prec::Assign, "operator-="}, + {"mL", OperatorInfo::Binary, false, Node::Prec::Assign, "operator*="}, + {"mi", OperatorInfo::Binary, false, Node::Prec::Additive, "operator-"}, + {"ml", OperatorInfo::Binary, false, Node::Prec::Multiplicative, + "operator*"}, + {"mm", OperatorInfo::Postfix, false, Node::Prec::Postfix, "operator--"}, + {"na", OperatorInfo::New, /*Ary*/ true, Node::Prec::Unary, + "operator new[]"}, + {"ne", OperatorInfo::Binary, false, Node::Prec::Equality, "operator!="}, + {"ng", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator-"}, + {"nt", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator!"}, + {"nw", OperatorInfo::New, /*Ary*/ false, Node::Prec::Unary, "operator new"}, + {"oR", OperatorInfo::Binary, false, Node::Prec::Assign, "operator|="}, + {"oo", OperatorInfo::Binary, false, Node::Prec::OrIf, "operator||"}, + {"or", OperatorInfo::Binary, false, Node::Prec::Ior, "operator|"}, + {"pL", OperatorInfo::Binary, false, Node::Prec::Assign, "operator+="}, + {"pl", OperatorInfo::Binary, false, Node::Prec::Additive, "operator+"}, + {"pm", OperatorInfo::Member, /*Named*/ false, Node::Prec::PtrMem, + "operator->*"}, + {"pp", OperatorInfo::Postfix, false, Node::Prec::Postfix, "operator++"}, + {"ps", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator+"}, + {"pt", OperatorInfo::Member, /*Named*/ true, Node::Prec::Postfix, + "operator->"}, + {"qu", OperatorInfo::Conditional, false, Node::Prec::Conditional, + "operator?"}, + {"rM", OperatorInfo::Binary, false, Node::Prec::Assign, "operator%="}, + {"rS", OperatorInfo::Binary, false, Node::Prec::Assign, "operator>>="}, + {"rc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, + "reinterpret_cast"}, + {"rm", OperatorInfo::Binary, false, Node::Prec::Multiplicative, + "operator%"}, + {"rs", OperatorInfo::Binary, false, Node::Prec::Shift, "operator>>"}, + {"sc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, "static_cast"}, + {"ss", OperatorInfo::Binary, false, Node::Prec::Spaceship, "operator<=>"}, + {"st", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Unary, "sizeof "}, + {"sz", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Unary, "sizeof "}, + {"te", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Postfix, + "typeid "}, + {"ti", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Postfix, "typeid "}, +}; +template <typename Derived, typename Alloc> +const size_t AbstractManglingParser<Derived, Alloc>::NumOps = sizeof(Ops) / + sizeof(Ops[0]); + +// If the next 2 chars are an operator encoding, consume them and return their +// OperatorInfo. Otherwise return nullptr. +template <typename Derived, typename Alloc> +const typename AbstractManglingParser<Derived, Alloc>::OperatorInfo * +AbstractManglingParser<Derived, Alloc>::parseOperatorEncoding() { + if (numLeft() < 2) + return nullptr; + + // We can't use lower_bound as that can link to symbols in the C++ library, + // and this must remain independant of that. + size_t lower = 0u, upper = NumOps - 1; // Inclusive bounds. + while (upper != lower) { + size_t middle = (upper + lower) / 2; + if (Ops[middle] < First) + lower = middle + 1; + else + upper = middle; + } + if (Ops[lower] != First) + return nullptr; + + First += 2; + return &Ops[lower]; +} + +// <operator-name> ::= See parseOperatorEncoding() +// ::= li <source-name> # operator "" +// ::= v <digit> <source-name> # vendor extended operator +template <typename Derived, typename Alloc> +Node * +AbstractManglingParser<Derived, Alloc>::parseOperatorName(NameState *State) { + if (const auto *Op = parseOperatorEncoding()) { + if (Op->getKind() == OperatorInfo::CCast) { + // ::= cv <type> # (cast) + ScopedOverride<bool> SaveTemplate(TryToParseTemplateArgs, false); + // If we're parsing an encoding, State != nullptr and the conversion + // operators' <type> could have a <template-param> that refers to some + // <template-arg>s further ahead in the mangled name. + ScopedOverride<bool> SavePermit(PermitForwardTemplateReferences, + PermitForwardTemplateReferences || + State != nullptr); + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + if (State) State->CtorDtorConversion = true; + return make<ConversionOperatorType>(Ty); + } + + if (Op->getKind() >= OperatorInfo::Unnameable) + /* Not a nameable operator. */ + return nullptr; + if (Op->getKind() == OperatorInfo::Member && !Op->getFlag()) + /* Not a nameable MemberExpr */ + return nullptr; + + return make<NameType>(Op->getName()); + } + + if (consumeIf("li")) { + // ::= li <source-name> # operator "" + Node *SN = getDerived().parseSourceName(State); + if (SN == nullptr) + return nullptr; + return make<LiteralOperator>(SN); + } + + if (consumeIf('v')) { + // ::= v <digit> <source-name> # vendor extended operator + if (look() >= '0' && look() <= '9') { + First++; + Node *SN = getDerived().parseSourceName(State); + if (SN == nullptr) + return nullptr; + return make<ConversionOperatorType>(SN); + } + return nullptr; + } + + return nullptr; +} + +// <ctor-dtor-name> ::= 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 <typename Derived, typename Alloc> +Node * +AbstractManglingParser<Derived, Alloc>::parseCtorDtorName(Node *&SoFar, + NameState *State) { + if (SoFar->getKind() == Node::KSpecialSubstitution) { + // Expand the special substitution. + SoFar = make<ExpandedSpecialSubstitution>( + static_cast<SpecialSubstitution *>(SoFar)); + if (!SoFar) + return nullptr; + } + + 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<CtorDtorName>(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<CtorDtorName>(SoFar, /*IsDtor=*/true, Variant); + } + + return nullptr; +} + +// <nested-name> ::= N [<CV-Qualifiers>] [<ref-qualifier>] <prefix> +// <unqualified-name> E +// ::= N [<CV-Qualifiers>] [<ref-qualifier>] <template-prefix> +// <template-args> E +// +// <prefix> ::= <prefix> <unqualified-name> +// ::= <template-prefix> <template-args> +// ::= <template-param> +// ::= <decltype> +// ::= # empty +// ::= <substitution> +// ::= <prefix> <data-member-prefix> +// [*] extension +// +// <data-member-prefix> := <member source-name> [<template-args>] M +// +// <template-prefix> ::= <prefix> <template unqualified-name> +// ::= <template-param> +// ::= <substitution> +template <typename Derived, typename Alloc> +Node * +AbstractManglingParser<Derived, Alloc>::parseNestedName(NameState *State) { + if (!consumeIf('N')) + return nullptr; + + Qualifiers CVTmp = parseCVQualifiers(); + if (State) State->CVQualifiers = CVTmp; + + if (consumeIf('O')) { + if (State) State->ReferenceQualifier = FrefQualRValue; + } else if (consumeIf('R')) { + if (State) State->ReferenceQualifier = FrefQualLValue; + } else { + if (State) State->ReferenceQualifier = FrefQualNone; + } + + Node *SoFar = nullptr; + while (!consumeIf('E')) { + if (State) + // Only set end-with-template on the case that does that. + State->EndsWithTemplateArgs = false; + + if (look() == 'T') { + // ::= <template-param> + if (SoFar != nullptr) + return nullptr; // Cannot have a prefix. + SoFar = getDerived().parseTemplateParam(); + } else if (look() == 'I') { + // ::= <template-prefix> <template-args> + if (SoFar == nullptr) + return nullptr; // Must have a prefix. + Node *TA = getDerived().parseTemplateArgs(State != nullptr); + if (TA == nullptr) + return nullptr; + if (SoFar->getKind() == Node::KNameWithTemplateArgs) + // Semantically <template-args> <template-args> cannot be generated by a + // C++ entity. There will always be [something like] a name between + // them. + return nullptr; + if (State) + State->EndsWithTemplateArgs = true; + SoFar = make<NameWithTemplateArgs>(SoFar, TA); + } else if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) { + // ::= <decltype> + if (SoFar != nullptr) + return nullptr; // Cannot have a prefix. + SoFar = getDerived().parseDecltype(); + } else { + ModuleName *Module = nullptr; + + if (look() == 'S') { + // ::= <substitution> + Node *S = nullptr; + if (look(1) == 't') { + First += 2; + S = make<NameType>("std"); + } else { + S = getDerived().parseSubstitution(); + } + if (!S) + return nullptr; + if (S->getKind() == Node::KModuleName) { + Module = static_cast<ModuleName *>(S); + } else if (SoFar != nullptr) { + return nullptr; // Cannot have a prefix. + } else { + SoFar = S; + continue; // Do not push a new substitution. + } + } + + // ::= [<prefix>] <unqualified-name> + SoFar = getDerived().parseUnqualifiedName(State, SoFar, Module); + } + + if (SoFar == nullptr) + return nullptr; + Subs.push_back(SoFar); + + // No longer used. + // <data-member-prefix> := <member source-name> [<template-args>] M + consumeIf('M'); + } + + if (SoFar == nullptr || Subs.empty()) + return nullptr; + + Subs.pop_back(); + return SoFar; +} + +// <simple-id> ::= <source-name> [ <template-args> ] +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseSimpleId() { + Node *SN = getDerived().parseSourceName(/*NameState=*/nullptr); + if (SN == nullptr) + return nullptr; + if (look() == 'I') { + Node *TA = getDerived().parseTemplateArgs(); + if (TA == nullptr) + return nullptr; + return make<NameWithTemplateArgs>(SN, TA); + } + return SN; +} + +// <destructor-name> ::= <unresolved-type> # e.g., ~T or ~decltype(f()) +// ::= <simple-id> # e.g., ~A<2*N> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseDestructorName() { + Node *Result; + if (std::isdigit(look())) + Result = getDerived().parseSimpleId(); + else + Result = getDerived().parseUnresolvedType(); + if (Result == nullptr) + return nullptr; + return make<DtorName>(Result); +} + +// <unresolved-type> ::= <template-param> +// ::= <decltype> +// ::= <substitution> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseUnresolvedType() { + if (look() == 'T') { + Node *TP = getDerived().parseTemplateParam(); + if (TP == nullptr) + return nullptr; + Subs.push_back(TP); + return TP; + } + if (look() == 'D') { + Node *DT = getDerived().parseDecltype(); + if (DT == nullptr) + return nullptr; + Subs.push_back(DT); + return DT; + } + return getDerived().parseSubstitution(); +} + +// <base-unresolved-name> ::= <simple-id> # unresolved name +// extension ::= <operator-name> # unresolved operator-function-id +// extension ::= <operator-name> <template-args> # unresolved operator template-id +// ::= on <operator-name> # unresolved operator-function-id +// ::= on <operator-name> <template-args> # unresolved operator template-id +// ::= dn <destructor-name> # destructor or pseudo-destructor; +// # e.g. ~X or ~X<N-1> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseBaseUnresolvedName() { + if (std::isdigit(look())) + return getDerived().parseSimpleId(); + + if (consumeIf("dn")) + return getDerived().parseDestructorName(); + + consumeIf("on"); + + Node *Oper = getDerived().parseOperatorName(/*NameState=*/nullptr); + if (Oper == nullptr) + return nullptr; + if (look() == 'I') { + Node *TA = getDerived().parseTemplateArgs(); + if (TA == nullptr) + return nullptr; + return make<NameWithTemplateArgs>(Oper, TA); + } + return Oper; +} + +// <unresolved-name> +// extension ::= srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name> +// ::= [gs] <base-unresolved-name> # x or (with "gs") ::x +// ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name> +// # A::x, N::y, A<T>::z; "gs" means leading "::" +// [gs] has been parsed by caller. +// ::= sr <unresolved-type> <base-unresolved-name> # T::x / decltype(p)::x +// extension ::= sr <unresolved-type> <template-args> <base-unresolved-name> +// # T::N::x /decltype(p)::N::x +// (ignored) ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name> +// +// <unresolved-qualifier-level> ::= <simple-id> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseUnresolvedName(bool Global) { + Node *SoFar = nullptr; + + // srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name> + // srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name> + if (consumeIf("srN")) { + SoFar = getDerived().parseUnresolvedType(); + if (SoFar == nullptr) + return nullptr; + + if (look() == 'I') { + Node *TA = getDerived().parseTemplateArgs(); + if (TA == nullptr) + return nullptr; + SoFar = make<NameWithTemplateArgs>(SoFar, TA); + if (!SoFar) + return nullptr; + } + + while (!consumeIf('E')) { + Node *Qual = getDerived().parseSimpleId(); + if (Qual == nullptr) + return nullptr; + SoFar = make<QualifiedName>(SoFar, Qual); + if (!SoFar) + return nullptr; + } + + Node *Base = getDerived().parseBaseUnresolvedName(); + if (Base == nullptr) + return nullptr; + return make<QualifiedName>(SoFar, Base); + } + + // [gs] <base-unresolved-name> # x or (with "gs") ::x + if (!consumeIf("sr")) { + SoFar = getDerived().parseBaseUnresolvedName(); + if (SoFar == nullptr) + return nullptr; + if (Global) + SoFar = make<GlobalQualifiedName>(SoFar); + return SoFar; + } + + // [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name> + if (std::isdigit(look())) { + do { + Node *Qual = getDerived().parseSimpleId(); + if (Qual == nullptr) + return nullptr; + if (SoFar) + SoFar = make<QualifiedName>(SoFar, Qual); + else if (Global) + SoFar = make<GlobalQualifiedName>(Qual); + else + SoFar = Qual; + if (!SoFar) + return nullptr; + } while (!consumeIf('E')); + } + // sr <unresolved-type> <base-unresolved-name> + // sr <unresolved-type> <template-args> <base-unresolved-name> + else { + SoFar = getDerived().parseUnresolvedType(); + if (SoFar == nullptr) + return nullptr; + + if (look() == 'I') { + Node *TA = getDerived().parseTemplateArgs(); + if (TA == nullptr) + return nullptr; + SoFar = make<NameWithTemplateArgs>(SoFar, TA); + if (!SoFar) + return nullptr; + } + } + + assert(SoFar != nullptr); + + Node *Base = getDerived().parseBaseUnresolvedName(); + if (Base == nullptr) + return nullptr; + return make<QualifiedName>(SoFar, Base); +} + +// <abi-tags> ::= <abi-tag> [<abi-tags>] +// <abi-tag> ::= B <source-name> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseAbiTags(Node *N) { + while (consumeIf('B')) { + StringView SN = parseBareSourceName(); + if (SN.empty()) + return nullptr; + N = make<AbiTagAttr>(N, SN); + if (!N) + return nullptr; + } + return N; +} + +// <number> ::= [n] <non-negative decimal integer> +template <typename Alloc, typename Derived> +StringView +AbstractManglingParser<Alloc, Derived>::parseNumber(bool AllowNegative) { + const char *Tmp = First; + if (AllowNegative) + consumeIf('n'); + if (numLeft() == 0 || !std::isdigit(*First)) + return StringView(); + while (numLeft() != 0 && std::isdigit(*First)) + ++First; + return StringView(Tmp, First); +} + +// <positive length number> ::= [0-9]* +template <typename Alloc, typename Derived> +bool AbstractManglingParser<Alloc, Derived>::parsePositiveInteger(size_t *Out) { + *Out = 0; + if (look() < '0' || look() > '9') + return true; + while (look() >= '0' && look() <= '9') { + *Out *= 10; + *Out += static_cast<size_t>(consume() - '0'); + } + return false; +} + +template <typename Alloc, typename Derived> +StringView AbstractManglingParser<Alloc, Derived>::parseBareSourceName() { + size_t Int = 0; + if (parsePositiveInteger(&Int) || numLeft() < Int) + return StringView(); + StringView R(First, First + Int); + First += Int; + return R; +} + +// <function-type> ::= [<CV-qualifiers>] [<exception-spec>] [Dx] F [Y] <bare-function-type> [<ref-qualifier>] E +// +// <exception-spec> ::= Do # non-throwing exception-specification (e.g., noexcept, throw()) +// ::= DO <expression> E # computed (instantiation-dependent) noexcept +// ::= Dw <type>+ E # dynamic exception specification with instantiation-dependent types +// +// <ref-qualifier> ::= R # & ref-qualifier +// <ref-qualifier> ::= O # && ref-qualifier +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseFunctionType() { + Qualifiers CVQuals = parseCVQualifiers(); + + Node *ExceptionSpec = nullptr; + if (consumeIf("Do")) { + ExceptionSpec = make<NameType>("noexcept"); + if (!ExceptionSpec) + return nullptr; + } else if (consumeIf("DO")) { + Node *E = getDerived().parseExpr(); + if (E == nullptr || !consumeIf('E')) + return nullptr; + ExceptionSpec = make<NoexceptSpec>(E); + if (!ExceptionSpec) + return nullptr; + } else if (consumeIf("Dw")) { + size_t SpecsBegin = Names.size(); + while (!consumeIf('E')) { + Node *T = getDerived().parseType(); + if (T == nullptr) + return nullptr; + Names.push_back(T); + } + ExceptionSpec = + make<DynamicExceptionSpec>(popTrailingNodeArray(SpecsBegin)); + if (!ExceptionSpec) + return nullptr; + } + + consumeIf("Dx"); // transaction safe + + if (!consumeIf('F')) + return nullptr; + consumeIf('Y'); // extern "C" + Node *ReturnType = getDerived().parseType(); + if (ReturnType == nullptr) + return nullptr; + + FunctionRefQual ReferenceQualifier = FrefQualNone; + size_t ParamsBegin = Names.size(); + while (true) { + if (consumeIf('E')) + break; + if (consumeIf('v')) + continue; + if (consumeIf("RE")) { + ReferenceQualifier = FrefQualLValue; + break; + } + if (consumeIf("OE")) { + ReferenceQualifier = FrefQualRValue; + break; + } + Node *T = getDerived().parseType(); + if (T == nullptr) + return nullptr; + Names.push_back(T); + } + + NodeArray Params = popTrailingNodeArray(ParamsBegin); + return make<FunctionType>(ReturnType, Params, CVQuals, + ReferenceQualifier, ExceptionSpec); +} + +// extension: +// <vector-type> ::= Dv <positive dimension number> _ <extended element type> +// ::= Dv [<dimension expression>] _ <element type> +// <extended element type> ::= <element type> +// ::= p # AltiVec vector pixel +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseVectorType() { + if (!consumeIf("Dv")) + return nullptr; + if (look() >= '1' && look() <= '9') { + Node *DimensionNumber = make<NameType>(parseNumber()); + if (!DimensionNumber) + return nullptr; + if (!consumeIf('_')) + return nullptr; + if (consumeIf('p')) + return make<PixelVectorType>(DimensionNumber); + Node *ElemType = getDerived().parseType(); + if (ElemType == nullptr) + return nullptr; + return make<VectorType>(ElemType, DimensionNumber); + } + + if (!consumeIf('_')) { + Node *DimExpr = getDerived().parseExpr(); + if (!DimExpr) + return nullptr; + if (!consumeIf('_')) + return nullptr; + Node *ElemType = getDerived().parseType(); + if (!ElemType) + return nullptr; + return make<VectorType>(ElemType, DimExpr); + } + Node *ElemType = getDerived().parseType(); + if (!ElemType) + return nullptr; + return make<VectorType>(ElemType, /*Dimension=*/nullptr); +} + +// <decltype> ::= Dt <expression> E # decltype of an id-expression or class member access (C++0x) +// ::= DT <expression> E # decltype of an expression (C++0x) +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseDecltype() { + if (!consumeIf('D')) + return nullptr; + if (!consumeIf('t') && !consumeIf('T')) + return nullptr; + Node *E = getDerived().parseExpr(); + if (E == nullptr) + return nullptr; + if (!consumeIf('E')) + return nullptr; + return make<EnclosingExpr>("decltype", E); +} + +// <array-type> ::= A <positive dimension number> _ <element type> +// ::= A [<dimension expression>] _ <element type> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseArrayType() { + if (!consumeIf('A')) + return nullptr; + + Node *Dimension = nullptr; + + if (std::isdigit(look())) { + Dimension = make<NameType>(parseNumber()); + if (!Dimension) + return nullptr; + if (!consumeIf('_')) + return nullptr; + } else if (!consumeIf('_')) { + Node *DimExpr = getDerived().parseExpr(); + if (DimExpr == nullptr) + return nullptr; + if (!consumeIf('_')) + return nullptr; + Dimension = DimExpr; + } + + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + return make<ArrayType>(Ty, Dimension); +} + +// <pointer-to-member-type> ::= M <class type> <member type> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parsePointerToMemberType() { + if (!consumeIf('M')) + return nullptr; + Node *ClassType = getDerived().parseType(); + if (ClassType == nullptr) + return nullptr; + Node *MemberType = getDerived().parseType(); + if (MemberType == nullptr) + return nullptr; + return make<PointerToMemberType>(ClassType, MemberType); +} + +// <class-enum-type> ::= <name> # non-dependent type name, dependent type name, or dependent typename-specifier +// ::= Ts <name> # dependent elaborated type specifier using 'struct' or 'class' +// ::= Tu <name> # dependent elaborated type specifier using 'union' +// ::= Te <name> # dependent elaborated type specifier using 'enum' +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseClassEnumType() { + StringView ElabSpef; + if (consumeIf("Ts")) + ElabSpef = "struct"; + else if (consumeIf("Tu")) + ElabSpef = "union"; + else if (consumeIf("Te")) + ElabSpef = "enum"; + + Node *Name = getDerived().parseName(); + if (Name == nullptr) + return nullptr; + + if (!ElabSpef.empty()) + return make<ElaboratedTypeSpefType>(ElabSpef, Name); + + return Name; +} + +// <qualified-type> ::= <qualifiers> <type> +// <qualifiers> ::= <extended-qualifier>* <CV-qualifiers> +// <extended-qualifier> ::= U <source-name> [<template-args>] # vendor extended type qualifier +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseQualifiedType() { + if (consumeIf('U')) { + StringView Qual = parseBareSourceName(); + if (Qual.empty()) + return nullptr; + + // extension ::= U <objc-name> <objc-type> # objc-type<identifier> + if (Qual.startsWith("objcproto")) { + StringView ProtoSourceName = Qual.dropFront(std::strlen("objcproto")); + StringView Proto; + { + ScopedOverride<const char *> SaveFirst(First, ProtoSourceName.begin()), + SaveLast(Last, ProtoSourceName.end()); + Proto = parseBareSourceName(); + } + if (Proto.empty()) + return nullptr; + Node *Child = getDerived().parseQualifiedType(); + if (Child == nullptr) + return nullptr; + return make<ObjCProtoName>(Child, Proto); + } + + Node *TA = nullptr; + if (look() == 'I') { + TA = getDerived().parseTemplateArgs(); + if (TA == nullptr) + return nullptr; + } + + Node *Child = getDerived().parseQualifiedType(); + if (Child == nullptr) + return nullptr; + return make<VendorExtQualType>(Child, Qual, TA); + } + + Qualifiers Quals = parseCVQualifiers(); + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + if (Quals != QualNone) + Ty = make<QualType>(Ty, Quals); + return Ty; +} + +// <type> ::= <builtin-type> +// ::= <qualified-type> +// ::= <function-type> +// ::= <class-enum-type> +// ::= <array-type> +// ::= <pointer-to-member-type> +// ::= <template-param> +// ::= <template-template-param> <template-args> +// ::= <decltype> +// ::= P <type> # pointer +// ::= R <type> # l-value reference +// ::= O <type> # r-value reference (C++11) +// ::= C <type> # complex pair (C99) +// ::= G <type> # imaginary (C99) +// ::= <substitution> # See Compression below +// extension ::= U <objc-name> <objc-type> # objc-type<identifier> +// extension ::= <vector-type> # <vector-type> starts with Dv +// +// <objc-name> ::= <k0 number> objcproto <k1 number> <identifier> # k0 = 9 + <number of digits in k1> + k1 +// <objc-type> ::= <source-name> # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseType() { + Node *Result = nullptr; + + switch (look()) { + // ::= <qualified-type> + case 'r': + case 'V': + case 'K': { + unsigned AfterQuals = 0; + if (look(AfterQuals) == 'r') ++AfterQuals; + if (look(AfterQuals) == 'V') ++AfterQuals; + if (look(AfterQuals) == 'K') ++AfterQuals; + + if (look(AfterQuals) == 'F' || + (look(AfterQuals) == 'D' && + (look(AfterQuals + 1) == 'o' || look(AfterQuals + 1) == 'O' || + look(AfterQuals + 1) == 'w' || look(AfterQuals + 1) == 'x'))) { + Result = getDerived().parseFunctionType(); + break; + } + DEMANGLE_FALLTHROUGH; + } + case 'U': { + Result = getDerived().parseQualifiedType(); + break; + } + // <builtin-type> ::= v # void + case 'v': + ++First; + return make<NameType>("void"); + // ::= w # wchar_t + case 'w': + ++First; + return make<NameType>("wchar_t"); + // ::= b # bool + case 'b': + ++First; + return make<NameType>("bool"); + // ::= c # char + case 'c': + ++First; + return make<NameType>("char"); + // ::= a # signed char + case 'a': + ++First; + return make<NameType>("signed char"); + // ::= h # unsigned char + case 'h': + ++First; + return make<NameType>("unsigned char"); + // ::= s # short + case 's': + ++First; + return make<NameType>("short"); + // ::= t # unsigned short + case 't': + ++First; + return make<NameType>("unsigned short"); + // ::= i # int + case 'i': + ++First; + return make<NameType>("int"); + // ::= j # unsigned int + case 'j': + ++First; + return make<NameType>("unsigned int"); + // ::= l # long + case 'l': + ++First; + return make<NameType>("long"); + // ::= m # unsigned long + case 'm': + ++First; + return make<NameType>("unsigned long"); + // ::= x # long long, __int64 + case 'x': + ++First; + return make<NameType>("long long"); + // ::= y # unsigned long long, __int64 + case 'y': + ++First; + return make<NameType>("unsigned long long"); + // ::= n # __int128 + case 'n': + ++First; + return make<NameType>("__int128"); + // ::= o # unsigned __int128 + case 'o': + ++First; + return make<NameType>("unsigned __int128"); + // ::= f # float + case 'f': + ++First; + return make<NameType>("float"); + // ::= d # double + case 'd': + ++First; + return make<NameType>("double"); + // ::= e # long double, __float80 + case 'e': + ++First; + return make<NameType>("long double"); + // ::= g # __float128 + case 'g': + ++First; + return make<NameType>("__float128"); + // ::= z # ellipsis + case 'z': + ++First; + return make<NameType>("..."); + + // <builtin-type> ::= u <source-name> # vendor extended type + case 'u': { + ++First; + StringView Res = parseBareSourceName(); + if (Res.empty()) + return nullptr; + // Typically, <builtin-type>s are not considered substitution candidates, + // but the exception to that exception is vendor extended types (Itanium C++ + // ABI 5.9.1). + Result = make<NameType>(Res); + break; + } + case 'D': + switch (look(1)) { + // ::= Dd # IEEE 754r decimal floating point (64 bits) + case 'd': + First += 2; + return make<NameType>("decimal64"); + // ::= De # IEEE 754r decimal floating point (128 bits) + case 'e': + First += 2; + return make<NameType>("decimal128"); + // ::= Df # IEEE 754r decimal floating point (32 bits) + case 'f': + First += 2; + return make<NameType>("decimal32"); + // ::= Dh # IEEE 754r half-precision floating point (16 bits) + case 'h': + First += 2; + return make<NameType>("half"); + // ::= DF <number> _ # ISO/IEC TS 18661 binary floating point (N bits) + case 'F': { + First += 2; + Node *DimensionNumber = make<NameType>(parseNumber()); + if (!DimensionNumber) + return nullptr; + if (!consumeIf('_')) + return nullptr; + return make<BinaryFPType>(DimensionNumber); + } + // ::= DB <number> _ # C23 signed _BitInt(N) + // ::= DB <instantiation-dependent expression> _ # C23 signed _BitInt(N) + // ::= DU <number> _ # C23 unsigned _BitInt(N) + // ::= DU <instantiation-dependent expression> _ # C23 unsigned _BitInt(N) + case 'B': + case 'U': { + bool Signed = look(1) == 'B'; + First += 2; + Node *Size = std::isdigit(look()) ? make<NameType>(parseNumber()) + : getDerived().parseExpr(); + if (!Size) + return nullptr; + if (!consumeIf('_')) + return nullptr; + return make<BitIntType>(Size, Signed); + } + // ::= Di # char32_t + case 'i': + First += 2; + return make<NameType>("char32_t"); + // ::= Ds # char16_t + case 's': + First += 2; + return make<NameType>("char16_t"); + // ::= Du # char8_t (C++2a, not yet in the Itanium spec) + case 'u': + First += 2; + return make<NameType>("char8_t"); + // ::= Da # auto (in dependent new-expressions) + case 'a': + First += 2; + return make<NameType>("auto"); + // ::= Dc # decltype(auto) + case 'c': + First += 2; + return make<NameType>("decltype(auto)"); + // ::= Dn # std::nullptr_t (i.e., decltype(nullptr)) + case 'n': + First += 2; + return make<NameType>("std::nullptr_t"); + + // ::= <decltype> + case 't': + case 'T': { + Result = getDerived().parseDecltype(); + break; + } + // extension ::= <vector-type> # <vector-type> starts with Dv + case 'v': { + Result = getDerived().parseVectorType(); + break; + } + // ::= Dp <type> # pack expansion (C++0x) + case 'p': { + First += 2; + Node *Child = getDerived().parseType(); + if (!Child) + return nullptr; + Result = make<ParameterPackExpansion>(Child); + break; + } + // Exception specifier on a function type. + case 'o': + case 'O': + case 'w': + // Transaction safe function type. + case 'x': + Result = getDerived().parseFunctionType(); + break; + } + break; + // ::= <function-type> + case 'F': { + Result = getDerived().parseFunctionType(); + break; + } + // ::= <array-type> + case 'A': { + Result = getDerived().parseArrayType(); + break; + } + // ::= <pointer-to-member-type> + case 'M': { + Result = getDerived().parsePointerToMemberType(); + break; + } + // ::= <template-param> + case 'T': { + // This could be an elaborate type specifier on a <class-enum-type>. + if (look(1) == 's' || look(1) == 'u' || look(1) == 'e') { + Result = getDerived().parseClassEnumType(); + break; + } + + Result = getDerived().parseTemplateParam(); + if (Result == nullptr) + return nullptr; + + // Result could be either of: + // <type> ::= <template-param> + // <type> ::= <template-template-param> <template-args> + // + // <template-template-param> ::= <template-param> + // ::= <substitution> + // + // If this is followed by some <template-args>, and we're permitted to + // parse them, take the second production. + + if (TryToParseTemplateArgs && look() == 'I') { + Node *TA = getDerived().parseTemplateArgs(); + if (TA == nullptr) + return nullptr; + Result = make<NameWithTemplateArgs>(Result, TA); + } + break; + } + // ::= P <type> # pointer + case 'P': { + ++First; + Node *Ptr = getDerived().parseType(); + if (Ptr == nullptr) + return nullptr; + Result = make<PointerType>(Ptr); + break; + } + // ::= R <type> # l-value reference + case 'R': { + ++First; + Node *Ref = getDerived().parseType(); + if (Ref == nullptr) + return nullptr; + Result = make<ReferenceType>(Ref, ReferenceKind::LValue); + break; + } + // ::= O <type> # r-value reference (C++11) + case 'O': { + ++First; + Node *Ref = getDerived().parseType(); + if (Ref == nullptr) + return nullptr; + Result = make<ReferenceType>(Ref, ReferenceKind::RValue); + break; + } + // ::= C <type> # complex pair (C99) + case 'C': { + ++First; + Node *P = getDerived().parseType(); + if (P == nullptr) + return nullptr; + Result = make<PostfixQualifiedType>(P, " complex"); + break; + } + // ::= G <type> # imaginary (C99) + case 'G': { + ++First; + Node *P = getDerived().parseType(); + if (P == nullptr) + return P; + Result = make<PostfixQualifiedType>(P, " imaginary"); + break; + } + // ::= <substitution> # See Compression below + case 'S': { + if (look(1) != 't') { + bool IsSubst = false; + Result = getDerived().parseUnscopedName(nullptr, &IsSubst); + if (!Result) + return nullptr; + + // Sub could be either of: + // <type> ::= <substitution> + // <type> ::= <template-template-param> <template-args> + // + // <template-template-param> ::= <template-param> + // ::= <substitution> + // + // If this is followed by some <template-args>, and we're permitted to + // parse them, take the second production. + + if (look() == 'I' && (!IsSubst || TryToParseTemplateArgs)) { + if (!IsSubst) + Subs.push_back(Result); + Node *TA = getDerived().parseTemplateArgs(); + if (TA == nullptr) + return nullptr; + Result = make<NameWithTemplateArgs>(Result, TA); + } else if (IsSubst) { + // If all we parsed was a substitution, don't re-insert into the + // substitution table. + return Result; + } + break; + } + DEMANGLE_FALLTHROUGH; + } + // ::= <class-enum-type> + default: { + Result = getDerived().parseClassEnumType(); + break; + } + } + + // If we parsed a type, insert it into the substitution table. Note that all + // <builtin-type>s and <substitution>s have already bailed out, because they + // don't get substitutions. + if (Result != nullptr) + Subs.push_back(Result); + return Result; +} + +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parsePrefixExpr(StringView Kind, + Node::Prec Prec) { + Node *E = getDerived().parseExpr(); + if (E == nullptr) + return nullptr; + return make<PrefixExpr>(Kind, E, Prec); +} + +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseBinaryExpr(StringView Kind, + Node::Prec Prec) { + Node *LHS = getDerived().parseExpr(); + if (LHS == nullptr) + return nullptr; + Node *RHS = getDerived().parseExpr(); + if (RHS == nullptr) + return nullptr; + return make<BinaryExpr>(LHS, Kind, RHS, Prec); +} + +template <typename Derived, typename Alloc> +Node * +AbstractManglingParser<Derived, Alloc>::parseIntegerLiteral(StringView Lit) { + StringView Tmp = parseNumber(true); + if (!Tmp.empty() && consumeIf('E')) + return make<IntegerLiteral>(Lit, Tmp); + return nullptr; +} + +// <CV-Qualifiers> ::= [r] [V] [K] +template <typename Alloc, typename Derived> +Qualifiers AbstractManglingParser<Alloc, Derived>::parseCVQualifiers() { + Qualifiers CVR = QualNone; + if (consumeIf('r')) + CVR |= QualRestrict; + if (consumeIf('V')) + CVR |= QualVolatile; + if (consumeIf('K')) + CVR |= QualConst; + return CVR; +} + +// <function-param> ::= fp <top-level CV-Qualifiers> _ # L == 0, first parameter +// ::= fp <top-level CV-Qualifiers> <parameter-2 non-negative number> _ # L == 0, second and later parameters +// ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> _ # L > 0, first parameter +// ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> <parameter-2 non-negative number> _ # L > 0, second and later parameters +// ::= fpT # 'this' expression (not part of standard?) +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseFunctionParam() { + if (consumeIf("fpT")) + return make<NameType>("this"); + if (consumeIf("fp")) { + parseCVQualifiers(); + StringView Num = parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make<FunctionParam>(Num); + } + if (consumeIf("fL")) { + if (parseNumber().empty()) + return nullptr; + if (!consumeIf('p')) + return nullptr; + parseCVQualifiers(); + StringView Num = parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make<FunctionParam>(Num); + } + return nullptr; +} + +// cv <type> <expression> # conversion with one argument +// cv <type> _ <expression>* E # conversion with a different number of arguments +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseConversionExpr() { + if (!consumeIf("cv")) + return nullptr; + Node *Ty; + { + ScopedOverride<bool> SaveTemp(TryToParseTemplateArgs, false); + Ty = getDerived().parseType(); + } + + if (Ty == nullptr) + return nullptr; + + if (consumeIf('_')) { + size_t ExprsBegin = Names.size(); + while (!consumeIf('E')) { + Node *E = getDerived().parseExpr(); + if (E == nullptr) + return E; + Names.push_back(E); + } + NodeArray Exprs = popTrailingNodeArray(ExprsBegin); + return make<ConversionExpr>(Ty, Exprs); + } + + Node *E[1] = {getDerived().parseExpr()}; + if (E[0] == nullptr) + return nullptr; + return make<ConversionExpr>(Ty, makeNodeArray(E, E + 1)); +} + +// <expr-primary> ::= L <type> <value number> E # integer literal +// ::= L <type> <value float> E # floating literal +// ::= L <string type> E # string literal +// ::= L <nullptr type> E # nullptr literal (i.e., "LDnE") +// ::= L <lambda type> E # lambda expression +// FIXME: ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000) +// ::= L <mangled-name> E # external name +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseExprPrimary() { + if (!consumeIf('L')) + return nullptr; + switch (look()) { + case 'w': + ++First; + return getDerived().parseIntegerLiteral("wchar_t"); + case 'b': + if (consumeIf("b0E")) + return make<BoolExpr>(0); + if (consumeIf("b1E")) + return make<BoolExpr>(1); + return nullptr; + case 'c': + ++First; + return getDerived().parseIntegerLiteral("char"); + case 'a': + ++First; + return getDerived().parseIntegerLiteral("signed char"); + case 'h': + ++First; + return getDerived().parseIntegerLiteral("unsigned char"); + case 's': + ++First; + return getDerived().parseIntegerLiteral("short"); + case 't': + ++First; + return getDerived().parseIntegerLiteral("unsigned short"); + case 'i': + ++First; + return getDerived().parseIntegerLiteral(""); + case 'j': + ++First; + return getDerived().parseIntegerLiteral("u"); + case 'l': + ++First; + return getDerived().parseIntegerLiteral("l"); + case 'm': + ++First; + return getDerived().parseIntegerLiteral("ul"); + case 'x': + ++First; + return getDerived().parseIntegerLiteral("ll"); + case 'y': + ++First; + return getDerived().parseIntegerLiteral("ull"); + case 'n': + ++First; + return getDerived().parseIntegerLiteral("__int128"); + case 'o': + ++First; + return getDerived().parseIntegerLiteral("unsigned __int128"); + case 'f': + ++First; + return getDerived().template parseFloatingLiteral<float>(); + case 'd': + ++First; + return getDerived().template parseFloatingLiteral<double>(); + case 'e': + ++First; +#if defined(__powerpc__) || defined(__s390__) + // Handle cases where long doubles encoded with e have the same size + // and representation as doubles. + return getDerived().template parseFloatingLiteral<double>(); +#else + return getDerived().template parseFloatingLiteral<long double>(); +#endif + case '_': + if (consumeIf("_Z")) { + Node *R = getDerived().parseEncoding(); + if (R != nullptr && consumeIf('E')) + return R; + } + return nullptr; + case 'A': { + Node *T = getDerived().parseType(); + if (T == nullptr) + return nullptr; + // FIXME: We need to include the string contents in the mangling. + if (consumeIf('E')) + return make<StringLiteral>(T); + return nullptr; + } + case 'D': + if (consumeIf("Dn") && (consumeIf('0'), consumeIf('E'))) + return make<NameType>("nullptr"); + return nullptr; + case 'T': + // Invalid mangled name per + // http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html + return nullptr; + case 'U': { + // FIXME: Should we support LUb... for block literals? + if (look(1) != 'l') + return nullptr; + Node *T = parseUnnamedTypeName(nullptr); + if (!T || !consumeIf('E')) + return nullptr; + return make<LambdaExpr>(T); + } + default: { + // might be named type + Node *T = getDerived().parseType(); + if (T == nullptr) + return nullptr; + StringView N = parseNumber(/*AllowNegative=*/true); + if (N.empty()) + return nullptr; + if (!consumeIf('E')) + return nullptr; + return make<EnumLiteral>(T, N); + } + } +} + +// <braced-expression> ::= <expression> +// ::= di <field source-name> <braced-expression> # .name = expr +// ::= dx <index expression> <braced-expression> # [expr] = expr +// ::= dX <range begin expression> <range end expression> <braced-expression> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseBracedExpr() { + if (look() == 'd') { + switch (look(1)) { + case 'i': { + First += 2; + Node *Field = getDerived().parseSourceName(/*NameState=*/nullptr); + if (Field == nullptr) + return nullptr; + Node *Init = getDerived().parseBracedExpr(); + if (Init == nullptr) + return nullptr; + return make<BracedExpr>(Field, Init, /*isArray=*/false); + } + case 'x': { + First += 2; + Node *Index = getDerived().parseExpr(); + if (Index == nullptr) + return nullptr; + Node *Init = getDerived().parseBracedExpr(); + if (Init == nullptr) + return nullptr; + return make<BracedExpr>(Index, Init, /*isArray=*/true); + } + case 'X': { + First += 2; + Node *RangeBegin = getDerived().parseExpr(); + if (RangeBegin == nullptr) + return nullptr; + Node *RangeEnd = getDerived().parseExpr(); + if (RangeEnd == nullptr) + return nullptr; + Node *Init = getDerived().parseBracedExpr(); + if (Init == nullptr) + return nullptr; + return make<BracedRangeExpr>(RangeBegin, RangeEnd, Init); + } + } + } + return getDerived().parseExpr(); +} + +// (not yet in the spec) +// <fold-expr> ::= fL <binary-operator-name> <expression> <expression> +// ::= fR <binary-operator-name> <expression> <expression> +// ::= fl <binary-operator-name> <expression> +// ::= fr <binary-operator-name> <expression> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseFoldExpr() { + if (!consumeIf('f')) + return nullptr; + + bool IsLeftFold = false, HasInitializer = false; + switch (look()) { + default: + return nullptr; + case 'L': + IsLeftFold = true; + HasInitializer = true; + break; + case 'R': + HasInitializer = true; + break; + case 'l': + IsLeftFold = true; + break; + case 'r': + break; + } + ++First; + + const auto *Op = parseOperatorEncoding(); + if (!Op) + return nullptr; + if (!(Op->getKind() == OperatorInfo::Binary + || (Op->getKind() == OperatorInfo::Member + && Op->getName().back() == '*'))) + return nullptr; + + Node *Pack = getDerived().parseExpr(); + if (Pack == nullptr) + return nullptr; + + Node *Init = nullptr; + if (HasInitializer) { + Init = getDerived().parseExpr(); + if (Init == nullptr) + return nullptr; + } + + if (IsLeftFold && Init) + std::swap(Pack, Init); + + return make<FoldExpr>(IsLeftFold, Op->getSymbol(), Pack, Init); +} + +// <expression> ::= mc <parameter type> <expr> [<offset number>] E +// +// Not yet in the spec: https://github.com/itanium-cxx-abi/cxx-abi/issues/47 +template <typename Derived, typename Alloc> +Node * +AbstractManglingParser<Derived, Alloc>::parsePointerToMemberConversionExpr( + Node::Prec Prec) { + Node *Ty = getDerived().parseType(); + if (!Ty) + return nullptr; + Node *Expr = getDerived().parseExpr(); + if (!Expr) + return nullptr; + StringView Offset = getDerived().parseNumber(true); + if (!consumeIf('E')) + return nullptr; + return make<PointerToMemberConversionExpr>(Ty, Expr, Offset, Prec); +} + +// <expression> ::= so <referent type> <expr> [<offset number>] <union-selector>* [p] E +// <union-selector> ::= _ [<number>] +// +// Not yet in the spec: https://github.com/itanium-cxx-abi/cxx-abi/issues/47 +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseSubobjectExpr() { + Node *Ty = getDerived().parseType(); + if (!Ty) + return nullptr; + Node *Expr = getDerived().parseExpr(); + if (!Expr) + return nullptr; + StringView Offset = getDerived().parseNumber(true); + size_t SelectorsBegin = Names.size(); + while (consumeIf('_')) { + Node *Selector = make<NameType>(parseNumber()); + if (!Selector) + return nullptr; + Names.push_back(Selector); + } + bool OnePastTheEnd = consumeIf('p'); + if (!consumeIf('E')) + return nullptr; + return make<SubobjectExpr>( + Ty, Expr, Offset, popTrailingNodeArray(SelectorsBegin), OnePastTheEnd); +} + +// <expression> ::= <unary operator-name> <expression> +// ::= <binary operator-name> <expression> <expression> +// ::= <ternary operator-name> <expression> <expression> <expression> +// ::= cl <expression>+ E # call +// ::= cv <type> <expression> # conversion with one argument +// ::= cv <type> _ <expression>* E # conversion with a different number of arguments +// ::= [gs] nw <expression>* _ <type> E # new (expr-list) type +// ::= [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) +// ::= [gs] na <expression>* _ <type> E # new[] (expr-list) type +// ::= [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) +// ::= [gs] dl <expression> # delete expression +// ::= [gs] da <expression> # delete[] expression +// ::= pp_ <expression> # prefix ++ +// ::= mm_ <expression> # prefix -- +// ::= ti <type> # typeid (type) +// ::= te <expression> # typeid (expression) +// ::= dc <type> <expression> # dynamic_cast<type> (expression) +// ::= sc <type> <expression> # static_cast<type> (expression) +// ::= cc <type> <expression> # const_cast<type> (expression) +// ::= rc <type> <expression> # reinterpret_cast<type> (expression) +// ::= st <type> # sizeof (a type) +// ::= sz <expression> # sizeof (an expression) +// ::= at <type> # alignof (a type) +// ::= az <expression> # alignof (an expression) +// ::= nx <expression> # noexcept (expression) +// ::= <template-param> +// ::= <function-param> +// ::= dt <expression> <unresolved-name> # expr.name +// ::= pt <expression> <unresolved-name> # expr->name +// ::= ds <expression> <expression> # expr.*expr +// ::= sZ <template-param> # size of a parameter pack +// ::= sZ <function-param> # size of a function parameter pack +// ::= sP <template-arg>* E # sizeof...(T), size of a captured template parameter pack from an alias template +// ::= sp <expression> # pack expansion +// ::= tw <expression> # throw expression +// ::= tr # throw with no operand (rethrow) +// ::= <unresolved-name> # f(p), N::f(p), ::f(p), +// # freestanding dependent name (e.g., T::x), +// # objectless nonstatic member reference +// ::= fL <binary-operator-name> <expression> <expression> +// ::= fR <binary-operator-name> <expression> <expression> +// ::= fl <binary-operator-name> <expression> +// ::= fr <binary-operator-name> <expression> +// ::= <expr-primary> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseExpr() { + bool Global = consumeIf("gs"); + + const auto *Op = parseOperatorEncoding(); + if (Op) { + auto Sym = Op->getSymbol(); + switch (Op->getKind()) { + case OperatorInfo::Binary: + // Binary operator: lhs @ rhs + return getDerived().parseBinaryExpr(Sym, Op->getPrecedence()); + case OperatorInfo::Prefix: + // Prefix unary operator: @ expr + return getDerived().parsePrefixExpr(Sym, Op->getPrecedence()); + case OperatorInfo::Postfix: { + // Postfix unary operator: expr @ + if (consumeIf('_')) + return getDerived().parsePrefixExpr(Sym, Op->getPrecedence()); + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return nullptr; + return make<PostfixExpr>(Ex, Sym, Op->getPrecedence()); + } + case OperatorInfo::Array: { + // Array Index: lhs [ rhs ] + Node *Base = getDerived().parseExpr(); + if (Base == nullptr) + return nullptr; + Node *Index = getDerived().parseExpr(); + if (Index == nullptr) + return nullptr; + return make<ArraySubscriptExpr>(Base, Index, Op->getPrecedence()); + } + case OperatorInfo::Member: { + // Member access lhs @ rhs + Node *LHS = getDerived().parseExpr(); + if (LHS == nullptr) + return nullptr; + Node *RHS = getDerived().parseExpr(); + if (RHS == nullptr) + return nullptr; + return make<MemberExpr>(LHS, Sym, RHS, Op->getPrecedence()); + } + case OperatorInfo::New: { + // New + // # new (expr-list) type [(init)] + // [gs] nw <expression>* _ <type> [pi <expression>*] E + // # new[] (expr-list) type [(init)] + // [gs] na <expression>* _ <type> [pi <expression>*] E + size_t Exprs = Names.size(); + while (!consumeIf('_')) { + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return nullptr; + Names.push_back(Ex); + } + NodeArray ExprList = popTrailingNodeArray(Exprs); + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + bool HaveInits = consumeIf("pi"); + size_t InitsBegin = Names.size(); + while (!consumeIf('E')) { + if (!HaveInits) + return nullptr; + Node *Init = getDerived().parseExpr(); + if (Init == nullptr) + return Init; + Names.push_back(Init); + } + NodeArray Inits = popTrailingNodeArray(InitsBegin); + return make<NewExpr>(ExprList, Ty, Inits, Global, + /*IsArray=*/Op->getFlag(), Op->getPrecedence()); + } + case OperatorInfo::Del: { + // Delete + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return nullptr; + return make<DeleteExpr>(Ex, Global, /*IsArray=*/Op->getFlag(), + Op->getPrecedence()); + } + case OperatorInfo::Call: { + // Function Call + Node *Callee = getDerived().parseExpr(); + if (Callee == nullptr) + return nullptr; + size_t ExprsBegin = Names.size(); + while (!consumeIf('E')) { + Node *E = getDerived().parseExpr(); + if (E == nullptr) + return nullptr; + Names.push_back(E); + } + return make<CallExpr>(Callee, popTrailingNodeArray(ExprsBegin), + Op->getPrecedence()); + } + case OperatorInfo::CCast: { + // C Cast: (type)expr + Node *Ty; + { + ScopedOverride<bool> SaveTemp(TryToParseTemplateArgs, false); + Ty = getDerived().parseType(); + } + if (Ty == nullptr) + return nullptr; + + size_t ExprsBegin = Names.size(); + bool IsMany = consumeIf('_'); + while (!consumeIf('E')) { + Node *E = getDerived().parseExpr(); + if (E == nullptr) + return E; + Names.push_back(E); + if (!IsMany) + break; + } + NodeArray Exprs = popTrailingNodeArray(ExprsBegin); + if (!IsMany && Exprs.size() != 1) + return nullptr; + return make<ConversionExpr>(Ty, Exprs, Op->getPrecedence()); + } + case OperatorInfo::Conditional: { + // Conditional operator: expr ? expr : expr + Node *Cond = getDerived().parseExpr(); + if (Cond == nullptr) + return nullptr; + Node *LHS = getDerived().parseExpr(); + if (LHS == nullptr) + return nullptr; + Node *RHS = getDerived().parseExpr(); + if (RHS == nullptr) + return nullptr; + return make<ConditionalExpr>(Cond, LHS, RHS, Op->getPrecedence()); + } + case OperatorInfo::NamedCast: { + // Named cast operation, @<type>(expr) + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return nullptr; + return make<CastExpr>(Sym, Ty, Ex, Op->getPrecedence()); + } + case OperatorInfo::OfIdOp: { + // [sizeof/alignof/typeid] ( <type>|<expr> ) + Node *Arg = + Op->getFlag() ? getDerived().parseType() : getDerived().parseExpr(); + if (!Arg) + return nullptr; + return make<EnclosingExpr>(Sym, Arg, Op->getPrecedence()); + } + case OperatorInfo::NameOnly: { + // Not valid as an expression operand. + return nullptr; + } + } + DEMANGLE_UNREACHABLE; + } + + if (numLeft() < 2) + return nullptr; + + if (look() == 'L') + return getDerived().parseExprPrimary(); + if (look() == 'T') + return getDerived().parseTemplateParam(); + if (look() == 'f') { + // Disambiguate a fold expression from a <function-param>. + if (look(1) == 'p' || (look(1) == 'L' && std::isdigit(look(2)))) + return getDerived().parseFunctionParam(); + return getDerived().parseFoldExpr(); + } + if (consumeIf("il")) { + size_t InitsBegin = Names.size(); + while (!consumeIf('E')) { + Node *E = getDerived().parseBracedExpr(); + if (E == nullptr) + return nullptr; + Names.push_back(E); + } + return make<InitListExpr>(nullptr, popTrailingNodeArray(InitsBegin)); + } + if (consumeIf("mc")) + return parsePointerToMemberConversionExpr(Node::Prec::Unary); + if (consumeIf("nx")) { + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return Ex; + return make<EnclosingExpr>("noexcept ", Ex, Node::Prec::Unary); + } + if (consumeIf("so")) + return parseSubobjectExpr(); + if (consumeIf("sp")) { + Node *Child = getDerived().parseExpr(); + if (Child == nullptr) + return nullptr; + return make<ParameterPackExpansion>(Child); + } + if (consumeIf("sZ")) { + if (look() == 'T') { + Node *R = getDerived().parseTemplateParam(); + if (R == nullptr) + return nullptr; + return make<SizeofParamPackExpr>(R); + } + Node *FP = getDerived().parseFunctionParam(); + if (FP == nullptr) + return nullptr; + return make<EnclosingExpr>("sizeof... ", FP); + } + if (consumeIf("sP")) { + size_t ArgsBegin = Names.size(); + while (!consumeIf('E')) { + Node *Arg = getDerived().parseTemplateArg(); + if (Arg == nullptr) + return nullptr; + Names.push_back(Arg); + } + auto *Pack = make<NodeArrayNode>(popTrailingNodeArray(ArgsBegin)); + if (!Pack) + return nullptr; + return make<EnclosingExpr>("sizeof... ", Pack); + } + if (consumeIf("tl")) { + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + size_t InitsBegin = Names.size(); + while (!consumeIf('E')) { + Node *E = getDerived().parseBracedExpr(); + if (E == nullptr) + return nullptr; + Names.push_back(E); + } + return make<InitListExpr>(Ty, popTrailingNodeArray(InitsBegin)); + } + if (consumeIf("tr")) + return make<NameType>("throw"); + if (consumeIf("tw")) { + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return nullptr; + return make<ThrowExpr>(Ex); + } + if (consumeIf('u')) { + Node *Name = getDerived().parseSourceName(/*NameState=*/nullptr); + if (!Name) + return nullptr; + // Special case legacy __uuidof mangling. The 't' and 'z' appear where the + // standard encoding expects a <template-arg>, and would be otherwise be + // interpreted as <type> node 'short' or 'ellipsis'. However, neither + // __uuidof(short) nor __uuidof(...) can actually appear, so there is no + // actual conflict here. + bool IsUUID = false; + Node *UUID = nullptr; + if (Name->getBaseName() == "__uuidof") { + if (consumeIf('t')) { + UUID = getDerived().parseType(); + IsUUID = true; + } else if (consumeIf('z')) { + UUID = getDerived().parseExpr(); + IsUUID = true; + } + } + size_t ExprsBegin = Names.size(); + if (IsUUID) { + if (UUID == nullptr) + return nullptr; + Names.push_back(UUID); + } else { + while (!consumeIf('E')) { + Node *E = getDerived().parseTemplateArg(); + if (E == nullptr) + return E; + Names.push_back(E); + } + } + return make<CallExpr>(Name, popTrailingNodeArray(ExprsBegin), + Node::Prec::Postfix); + } + + // Only unresolved names remain. + return getDerived().parseUnresolvedName(Global); +} + +// <call-offset> ::= h <nv-offset> _ +// ::= v <v-offset> _ +// +// <nv-offset> ::= <offset number> +// # non-virtual base override +// +// <v-offset> ::= <offset number> _ <virtual offset number> +// # virtual base override, with vcall offset +template <typename Alloc, typename Derived> +bool AbstractManglingParser<Alloc, Derived>::parseCallOffset() { + // Just scan through the call offset, we never add this information into the + // output. + if (consumeIf('h')) + return parseNumber(true).empty() || !consumeIf('_'); + if (consumeIf('v')) + return parseNumber(true).empty() || !consumeIf('_') || + parseNumber(true).empty() || !consumeIf('_'); + return true; +} + +// <special-name> ::= TV <type> # virtual table +// ::= TT <type> # VTT structure (construction vtable index) +// ::= TI <type> # typeinfo structure +// ::= TS <type> # typeinfo name (null-terminated byte string) +// ::= Tc <call-offset> <call-offset> <base encoding> +// # base is the nominal target function of thunk +// # first call-offset is 'this' adjustment +// # second call-offset is result adjustment +// ::= T <call-offset> <base encoding> +// # base is the nominal target function of thunk +// # Guard variable for one-time initialization +// ::= GV <object name> +// # No <type> +// ::= TW <object name> # Thread-local wrapper +// ::= TH <object name> # Thread-local initialization +// ::= GR <object name> _ # First temporary +// ::= GR <object name> <seq-id> _ # Subsequent temporaries +// # construction vtable for second-in-first +// extension ::= TC <first type> <number> _ <second type> +// extension ::= GR <object name> # reference temporary for object +// extension ::= GI <module name> # module global initializer +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseSpecialName() { + switch (look()) { + case 'T': + switch (look(1)) { + // TA <template-arg> # template parameter object + // + // Not yet in the spec: https://github.com/itanium-cxx-abi/cxx-abi/issues/63 + case 'A': { + First += 2; + Node *Arg = getDerived().parseTemplateArg(); + if (Arg == nullptr) + return nullptr; + return make<SpecialName>("template parameter object for ", Arg); + } + // TV <type> # virtual table + case 'V': { + First += 2; + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + return make<SpecialName>("vtable for ", Ty); + } + // TT <type> # VTT structure (construction vtable index) + case 'T': { + First += 2; + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + return make<SpecialName>("VTT for ", Ty); + } + // TI <type> # typeinfo structure + case 'I': { + First += 2; + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + return make<SpecialName>("typeinfo for ", Ty); + } + // TS <type> # typeinfo name (null-terminated byte string) + case 'S': { + First += 2; + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + return make<SpecialName>("typeinfo name for ", Ty); + } + // Tc <call-offset> <call-offset> <base encoding> + case 'c': { + First += 2; + if (parseCallOffset() || parseCallOffset()) + return nullptr; + Node *Encoding = getDerived().parseEncoding(); + if (Encoding == nullptr) + return nullptr; + return make<SpecialName>("covariant return thunk to ", Encoding); + } + // extension ::= TC <first type> <number> _ <second type> + // # construction vtable for second-in-first + case 'C': { + First += 2; + Node *FirstType = getDerived().parseType(); + if (FirstType == nullptr) + return nullptr; + if (parseNumber(true).empty() || !consumeIf('_')) + return nullptr; + Node *SecondType = getDerived().parseType(); + if (SecondType == nullptr) + return nullptr; + return make<CtorVtableSpecialName>(SecondType, FirstType); + } + // TW <object name> # Thread-local wrapper + case 'W': { + First += 2; + Node *Name = getDerived().parseName(); + if (Name == nullptr) + return nullptr; + return make<SpecialName>("thread-local wrapper routine for ", Name); + } + // TH <object name> # Thread-local initialization + case 'H': { + First += 2; + Node *Name = getDerived().parseName(); + if (Name == nullptr) + return nullptr; + return make<SpecialName>("thread-local initialization routine for ", Name); + } + // T <call-offset> <base encoding> + default: { + ++First; + bool IsVirt = look() == 'v'; + if (parseCallOffset()) + return nullptr; + Node *BaseEncoding = getDerived().parseEncoding(); + if (BaseEncoding == nullptr) + return nullptr; + if (IsVirt) + return make<SpecialName>("virtual thunk to ", BaseEncoding); + else + return make<SpecialName>("non-virtual thunk to ", BaseEncoding); + } + } + case 'G': + switch (look(1)) { + // GV <object name> # Guard variable for one-time initialization + case 'V': { + First += 2; + Node *Name = getDerived().parseName(); + if (Name == nullptr) + return nullptr; + return make<SpecialName>("guard variable for ", Name); + } + // GR <object name> # reference temporary for object + // GR <object name> _ # First temporary + // GR <object name> <seq-id> _ # Subsequent temporaries + case 'R': { + First += 2; + Node *Name = getDerived().parseName(); + if (Name == nullptr) + return nullptr; + size_t Count; + bool ParsedSeqId = !parseSeqId(&Count); + if (!consumeIf('_') && ParsedSeqId) + return nullptr; + return make<SpecialName>("reference temporary for ", Name); + } + // GI <module-name> v + case 'I': { + First += 2; + ModuleName *Module = nullptr; + if (getDerived().parseModuleNameOpt(Module)) + return nullptr; + if (Module == nullptr) + return nullptr; + return make<SpecialName>("initializer for module ", Module); + } + } + } + return nullptr; +} + +// <encoding> ::= <function name> <bare-function-type> +// ::= <data name> +// ::= <special-name> +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseEncoding() { + // The template parameters of an encoding are unrelated to those of the + // enclosing context. + class SaveTemplateParams { + AbstractManglingParser *Parser; + decltype(TemplateParams) OldParams; + decltype(OuterTemplateParams) OldOuterParams; + + public: + SaveTemplateParams(AbstractManglingParser *TheParser) : Parser(TheParser) { + OldParams = std::move(Parser->TemplateParams); + OldOuterParams = std::move(Parser->OuterTemplateParams); + Parser->TemplateParams.clear(); + Parser->OuterTemplateParams.clear(); + } + ~SaveTemplateParams() { + Parser->TemplateParams = std::move(OldParams); + Parser->OuterTemplateParams = std::move(OldOuterParams); + } + } SaveTemplateParams(this); + + if (look() == 'G' || look() == 'T') + return getDerived().parseSpecialName(); + + auto IsEndOfEncoding = [&] { + // The set of chars that can potentially follow an <encoding> (none of which + // can start a <type>). Enumerating these allows us to avoid speculative + // parsing. + return numLeft() == 0 || look() == 'E' || look() == '.' || look() == '_'; + }; + + NameState NameInfo(this); + Node *Name = getDerived().parseName(&NameInfo); + if (Name == nullptr) + return nullptr; + + if (resolveForwardTemplateRefs(NameInfo)) + return nullptr; + + if (IsEndOfEncoding()) + return Name; + + Node *Attrs = nullptr; + if (consumeIf("Ua9enable_ifI")) { + size_t BeforeArgs = Names.size(); + while (!consumeIf('E')) { + Node *Arg = getDerived().parseTemplateArg(); + if (Arg == nullptr) + return nullptr; + Names.push_back(Arg); + } + Attrs = make<EnableIfAttr>(popTrailingNodeArray(BeforeArgs)); + if (!Attrs) + return nullptr; + } + + Node *ReturnType = nullptr; + if (!NameInfo.CtorDtorConversion && NameInfo.EndsWithTemplateArgs) { + ReturnType = getDerived().parseType(); + if (ReturnType == nullptr) + return nullptr; + } + + if (consumeIf('v')) + return make<FunctionEncoding>(ReturnType, Name, NodeArray(), + Attrs, NameInfo.CVQualifiers, + NameInfo.ReferenceQualifier); + + size_t ParamsBegin = Names.size(); + do { + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + Names.push_back(Ty); + } while (!IsEndOfEncoding()); + + return make<FunctionEncoding>(ReturnType, Name, + popTrailingNodeArray(ParamsBegin), + Attrs, NameInfo.CVQualifiers, + NameInfo.ReferenceQualifier); +} + +template <class Float> +struct FloatData; + +template <> +struct FloatData<float> +{ + static const size_t mangled_size = 8; + static const size_t max_demangled_size = 24; + static constexpr const char* spec = "%af"; +}; + +template <> +struct FloatData<double> +{ + static const size_t mangled_size = 16; + static const size_t max_demangled_size = 32; + static constexpr const char* spec = "%a"; +}; + +template <> +struct FloatData<long double> +{ +#if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \ + defined(__wasm__) || defined(__riscv) + static const size_t mangled_size = 32; +#elif defined(__arm__) || defined(__mips__) || defined(__hexagon__) + static const size_t mangled_size = 16; +#else + static const size_t mangled_size = 20; // May need to be adjusted to 16 or 24 on other platforms +#endif + // `-0x1.ffffffffffffffffffffffffffffp+16383` + 'L' + '\0' == 42 bytes. + // 28 'f's * 4 bits == 112 bits, which is the number of mantissa bits. + // Negatives are one character longer than positives. + // `0x1.` and `p` are constant, and exponents `+16383` and `-16382` are the + // same length. 1 sign bit, 112 mantissa bits, and 15 exponent bits == 128. + static const size_t max_demangled_size = 42; + static constexpr const char *spec = "%LaL"; +}; + +template <typename Alloc, typename Derived> +template <class Float> +Node *AbstractManglingParser<Alloc, Derived>::parseFloatingLiteral() { + const size_t N = FloatData<Float>::mangled_size; + if (numLeft() <= N) + return nullptr; + StringView Data(First, First + N); + for (char C : Data) + if (!std::isxdigit(C)) + return nullptr; + First += N; + if (!consumeIf('E')) + return nullptr; + return make<FloatLiteralImpl<Float>>(Data); +} + +// <seq-id> ::= <0-9A-Z>+ +template <typename Alloc, typename Derived> +bool AbstractManglingParser<Alloc, Derived>::parseSeqId(size_t *Out) { + if (!(look() >= '0' && look() <= '9') && + !(look() >= 'A' && look() <= 'Z')) + return true; + + size_t Id = 0; + while (true) { + if (look() >= '0' && look() <= '9') { + Id *= 36; + Id += static_cast<size_t>(look() - '0'); + } else if (look() >= 'A' && look() <= 'Z') { + Id *= 36; + Id += static_cast<size_t>(look() - 'A') + 10; + } else { + *Out = Id; + return false; + } + ++First; + } +} + +// <substitution> ::= S <seq-id> _ +// ::= S_ +// <substitution> ::= Sa # ::std::allocator +// <substitution> ::= Sb # ::std::basic_string +// <substitution> ::= Ss # ::std::basic_string < char, +// ::std::char_traits<char>, +// ::std::allocator<char> > +// <substitution> ::= Si # ::std::basic_istream<char, std::char_traits<char> > +// <substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> > +// <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> > +// The St case is handled specially in parseNestedName. +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseSubstitution() { + if (!consumeIf('S')) + return nullptr; + + if (look() >= 'a' && look() <= 'z') { + SpecialSubKind Kind; + switch (look()) { + case 'a': + Kind = SpecialSubKind::allocator; + break; + case 'b': + Kind = SpecialSubKind::basic_string; + break; + case 'd': + Kind = SpecialSubKind::iostream; + break; + case 'i': + Kind = SpecialSubKind::istream; + break; + case 'o': + Kind = SpecialSubKind::ostream; + break; + case 's': + Kind = SpecialSubKind::string; + break; + default: + return nullptr; + } + ++First; + auto *SpecialSub = make<SpecialSubstitution>(Kind); + if (!SpecialSub) + return nullptr; + + // Itanium C++ ABI 5.1.2: If a name that would use a built-in <substitution> + // has ABI tags, the tags are appended to the substitution; the result is a + // substitutable component. + Node *WithTags = getDerived().parseAbiTags(SpecialSub); + if (WithTags != SpecialSub) { + Subs.push_back(WithTags); + SpecialSub = WithTags; + } + return SpecialSub; + } + + // ::= S_ + if (consumeIf('_')) { + if (Subs.empty()) + return nullptr; + return Subs[0]; + } + + // ::= S <seq-id> _ + size_t Index = 0; + if (parseSeqId(&Index)) + return nullptr; + ++Index; + if (!consumeIf('_') || Index >= Subs.size()) + return nullptr; + return Subs[Index]; +} + +// <template-param> ::= T_ # first template parameter +// ::= T <parameter-2 non-negative number> _ +// ::= TL <level-1> __ +// ::= TL <level-1> _ <parameter-2 non-negative number> _ +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseTemplateParam() { + if (!consumeIf('T')) + return nullptr; + + size_t Level = 0; + if (consumeIf('L')) { + if (parsePositiveInteger(&Level)) + return nullptr; + ++Level; + if (!consumeIf('_')) + return nullptr; + } + + size_t Index = 0; + if (!consumeIf('_')) { + if (parsePositiveInteger(&Index)) + return nullptr; + ++Index; + if (!consumeIf('_')) + return nullptr; + } + + // If we're in a context where this <template-param> refers to a + // <template-arg> further ahead in the mangled name (currently just conversion + // operator types), then we should only look it up in the right context. + // This can only happen at the outermost level. + if (PermitForwardTemplateReferences && Level == 0) { + Node *ForwardRef = make<ForwardTemplateReference>(Index); + if (!ForwardRef) + return nullptr; + assert(ForwardRef->getKind() == Node::KForwardTemplateReference); + ForwardTemplateRefs.push_back( + static_cast<ForwardTemplateReference *>(ForwardRef)); + return ForwardRef; + } + + if (Level >= TemplateParams.size() || !TemplateParams[Level] || + Index >= TemplateParams[Level]->size()) { + // Itanium ABI 5.1.8: In a generic lambda, uses of auto in the parameter + // list are mangled as the corresponding artificial template type parameter. + if (ParsingLambdaParamsAtLevel == Level && Level <= TemplateParams.size()) { + // This will be popped by the ScopedTemplateParamList in + // parseUnnamedTypeName. + if (Level == TemplateParams.size()) + TemplateParams.push_back(nullptr); + return make<NameType>("auto"); + } + + return nullptr; + } + + return (*TemplateParams[Level])[Index]; +} + +// <template-param-decl> ::= Ty # type parameter +// ::= Tn <type> # non-type parameter +// ::= Tt <template-param-decl>* E # template parameter +// ::= Tp <template-param-decl> # parameter pack +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseTemplateParamDecl() { + auto InventTemplateParamName = [&](TemplateParamKind Kind) { + unsigned Index = NumSyntheticTemplateParameters[(int)Kind]++; + Node *N = make<SyntheticTemplateParamName>(Kind, Index); + if (N) TemplateParams.back()->push_back(N); + return N; + }; + + if (consumeIf("Ty")) { + Node *Name = InventTemplateParamName(TemplateParamKind::Type); + if (!Name) + return nullptr; + return make<TypeTemplateParamDecl>(Name); + } + + if (consumeIf("Tn")) { + Node *Name = InventTemplateParamName(TemplateParamKind::NonType); + if (!Name) + return nullptr; + Node *Type = parseType(); + if (!Type) + return nullptr; + return make<NonTypeTemplateParamDecl>(Name, Type); + } + + if (consumeIf("Tt")) { + Node *Name = InventTemplateParamName(TemplateParamKind::Template); + if (!Name) + return nullptr; + size_t ParamsBegin = Names.size(); + ScopedTemplateParamList TemplateTemplateParamParams(this); + while (!consumeIf("E")) { + Node *P = parseTemplateParamDecl(); + if (!P) + return nullptr; + Names.push_back(P); + } + NodeArray Params = popTrailingNodeArray(ParamsBegin); + return make<TemplateTemplateParamDecl>(Name, Params); + } + + if (consumeIf("Tp")) { + Node *P = parseTemplateParamDecl(); + if (!P) + return nullptr; + return make<TemplateParamPackDecl>(P); + } + + return nullptr; +} + +// <template-arg> ::= <type> # type or template +// ::= X <expression> E # expression +// ::= <expr-primary> # simple expressions +// ::= J <template-arg>* E # argument pack +// ::= LZ <encoding> E # extension +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseTemplateArg() { + switch (look()) { + case 'X': { + ++First; + Node *Arg = getDerived().parseExpr(); + if (Arg == nullptr || !consumeIf('E')) + return nullptr; + return Arg; + } + case 'J': { + ++First; + size_t ArgsBegin = Names.size(); + while (!consumeIf('E')) { + Node *Arg = getDerived().parseTemplateArg(); + if (Arg == nullptr) + return nullptr; + Names.push_back(Arg); + } + NodeArray Args = popTrailingNodeArray(ArgsBegin); + return make<TemplateArgumentPack>(Args); + } + case 'L': { + // ::= LZ <encoding> E # extension + if (look(1) == 'Z') { + First += 2; + Node *Arg = getDerived().parseEncoding(); + if (Arg == nullptr || !consumeIf('E')) + return nullptr; + return Arg; + } + // ::= <expr-primary> # simple expressions + return getDerived().parseExprPrimary(); + } + default: + return getDerived().parseType(); + } +} + +// <template-args> ::= I <template-arg>* E +// extension, the abi says <template-arg>+ +template <typename Derived, typename Alloc> +Node * +AbstractManglingParser<Derived, Alloc>::parseTemplateArgs(bool TagTemplates) { + if (!consumeIf('I')) + return nullptr; + + // <template-params> refer to the innermost <template-args>. Clear out any + // outer args that we may have inserted into TemplateParams. + if (TagTemplates) { + TemplateParams.clear(); + TemplateParams.push_back(&OuterTemplateParams); + OuterTemplateParams.clear(); + } + + size_t ArgsBegin = Names.size(); + while (!consumeIf('E')) { + if (TagTemplates) { + auto OldParams = std::move(TemplateParams); + Node *Arg = getDerived().parseTemplateArg(); + TemplateParams = std::move(OldParams); + if (Arg == nullptr) + return nullptr; + Names.push_back(Arg); + Node *TableEntry = Arg; + if (Arg->getKind() == Node::KTemplateArgumentPack) { + TableEntry = make<ParameterPack>( + static_cast<TemplateArgumentPack*>(TableEntry)->getElements()); + if (!TableEntry) + return nullptr; + } + TemplateParams.back()->push_back(TableEntry); + } else { + Node *Arg = getDerived().parseTemplateArg(); + if (Arg == nullptr) + return nullptr; + Names.push_back(Arg); + } + } + return make<TemplateArgs>(popTrailingNodeArray(ArgsBegin)); +} + +// <mangled-name> ::= _Z <encoding> +// ::= <type> +// extension ::= ___Z <encoding> _block_invoke +// extension ::= ___Z <encoding> _block_invoke<decimal-digit>+ +// extension ::= ___Z <encoding> _block_invoke_<decimal-digit>+ +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parse() { + if (consumeIf("_Z") || consumeIf("__Z")) { + Node *Encoding = getDerived().parseEncoding(); + if (Encoding == nullptr) + return nullptr; + if (look() == '.') { + Encoding = make<DotSuffix>(Encoding, StringView(First, Last)); + First = Last; + } + if (numLeft() != 0) + return nullptr; + return Encoding; + } + + if (consumeIf("___Z") || consumeIf("____Z")) { + Node *Encoding = getDerived().parseEncoding(); + if (Encoding == nullptr || !consumeIf("_block_invoke")) + return nullptr; + bool RequireNumber = consumeIf('_'); + if (parseNumber().empty() && RequireNumber) + return nullptr; + if (look() == '.') + First = Last; + if (numLeft() != 0) + return nullptr; + return make<SpecialName>("invocation function for block in ", Encoding); + } + + Node *Ty = getDerived().parseType(); + if (numLeft() != 0) + return nullptr; + return Ty; +} + +template <typename Alloc> +struct ManglingParser : AbstractManglingParser<ManglingParser<Alloc>, Alloc> { + using AbstractManglingParser<ManglingParser<Alloc>, + Alloc>::AbstractManglingParser; +}; + +DEMANGLE_NAMESPACE_END + +#endif // DEMANGLE_ITANIUMDEMANGLE_H diff --git a/demangle/third_party/llvm/include/llvm/Demangle/ItaniumNodes.def b/demangle/third_party/llvm/include/llvm/Demangle/ItaniumNodes.def new file mode 100644 index 00000000..c0e277d5 --- /dev/null +++ b/demangle/third_party/llvm/include/llvm/Demangle/ItaniumNodes.def @@ -0,0 +1,95 @@ +//===--- ItaniumNodes.def ------------*- mode:c++;eval:(read-only-mode) -*-===// +// Do not edit! See README.txt. +// 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 +// +//===----------------------------------------------------------------------===// +// +// Define the demangler's node names + +#ifndef NODE +#error Define NODE to handle nodes +#endif + +NODE(NodeArrayNode) +NODE(DotSuffix) +NODE(VendorExtQualType) +NODE(QualType) +NODE(ConversionOperatorType) +NODE(PostfixQualifiedType) +NODE(ElaboratedTypeSpefType) +NODE(NameType) +NODE(AbiTagAttr) +NODE(EnableIfAttr) +NODE(ObjCProtoName) +NODE(PointerType) +NODE(ReferenceType) +NODE(PointerToMemberType) +NODE(ArrayType) +NODE(FunctionType) +NODE(NoexceptSpec) +NODE(DynamicExceptionSpec) +NODE(FunctionEncoding) +NODE(LiteralOperator) +NODE(SpecialName) +NODE(CtorVtableSpecialName) +NODE(QualifiedName) +NODE(NestedName) +NODE(LocalName) +NODE(ModuleName) +NODE(ModuleEntity) +NODE(VectorType) +NODE(PixelVectorType) +NODE(BinaryFPType) +NODE(BitIntType) +NODE(SyntheticTemplateParamName) +NODE(TypeTemplateParamDecl) +NODE(NonTypeTemplateParamDecl) +NODE(TemplateTemplateParamDecl) +NODE(TemplateParamPackDecl) +NODE(ParameterPack) +NODE(TemplateArgumentPack) +NODE(ParameterPackExpansion) +NODE(TemplateArgs) +NODE(ForwardTemplateReference) +NODE(NameWithTemplateArgs) +NODE(GlobalQualifiedName) +NODE(ExpandedSpecialSubstitution) +NODE(SpecialSubstitution) +NODE(CtorDtorName) +NODE(DtorName) +NODE(UnnamedTypeName) +NODE(ClosureTypeName) +NODE(StructuredBindingName) +NODE(BinaryExpr) +NODE(ArraySubscriptExpr) +NODE(PostfixExpr) +NODE(ConditionalExpr) +NODE(MemberExpr) +NODE(SubobjectExpr) +NODE(EnclosingExpr) +NODE(CastExpr) +NODE(SizeofParamPackExpr) +NODE(CallExpr) +NODE(NewExpr) +NODE(DeleteExpr) +NODE(PrefixExpr) +NODE(FunctionParam) +NODE(ConversionExpr) +NODE(PointerToMemberConversionExpr) +NODE(InitListExpr) +NODE(FoldExpr) +NODE(ThrowExpr) +NODE(BoolExpr) +NODE(StringLiteral) +NODE(LambdaExpr) +NODE(EnumLiteral) +NODE(IntegerLiteral) +NODE(FloatLiteral) +NODE(DoubleLiteral) +NODE(LongDoubleLiteral) +NODE(BracedExpr) +NODE(BracedRangeExpr) + +#undef NODE diff --git a/demangle/third_party/llvm/include/llvm/Demangle/MicrosoftDemangle.h b/demangle/third_party/llvm/include/llvm/Demangle/MicrosoftDemangle.h new file mode 100644 index 00000000..f1a5e1b6 --- /dev/null +++ b/demangle/third_party/llvm/include/llvm/Demangle/MicrosoftDemangle.h @@ -0,0 +1,276 @@ +//===------------------------- MicrosoftDemangle.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_MICROSOFTDEMANGLE_H +#define LLVM_DEMANGLE_MICROSOFTDEMANGLE_H + +#include "llvm/Demangle/MicrosoftDemangleNodes.h" +#include "llvm/Demangle/StringView.h" + +#include <utility> + +namespace llvm { +namespace ms_demangle { +// This memory allocator is extremely fast, but it doesn't call dtors +// for allocated objects. That means you can't use STL containers +// (such as std::vector) with this allocator. But it pays off -- +// the demangler is 3x faster with this allocator compared to one with +// STL containers. +constexpr size_t AllocUnit = 4096; + +class ArenaAllocator { + struct AllocatorNode { + uint8_t *Buf = nullptr; + size_t Used = 0; + size_t Capacity = 0; + AllocatorNode *Next = nullptr; + }; + + void addNode(size_t Capacity) { + AllocatorNode *NewHead = new AllocatorNode; + NewHead->Buf = new uint8_t[Capacity]; + NewHead->Next = Head; + NewHead->Capacity = Capacity; + Head = NewHead; + NewHead->Used = 0; + } + +public: + ArenaAllocator() { addNode(AllocUnit); } + + ~ArenaAllocator() { + while (Head) { + assert(Head->Buf); + delete[] Head->Buf; + AllocatorNode *Next = Head->Next; + delete Head; + Head = Next; + } + } + + char *allocUnalignedBuffer(size_t Size) { + assert(Head && Head->Buf); + + uint8_t *P = Head->Buf + Head->Used; + + Head->Used += Size; + if (Head->Used <= Head->Capacity) + return reinterpret_cast<char *>(P); + + addNode(std::max(AllocUnit, Size)); + Head->Used = Size; + return reinterpret_cast<char *>(Head->Buf); + } + + template <typename T, typename... Args> T *allocArray(size_t Count) { + size_t Size = Count * sizeof(T); + assert(Head && Head->Buf); + + size_t P = (size_t)Head->Buf + Head->Used; + uintptr_t AlignedP = + (((size_t)P + alignof(T) - 1) & ~(size_t)(alignof(T) - 1)); + uint8_t *PP = (uint8_t *)AlignedP; + size_t Adjustment = AlignedP - P; + + Head->Used += Size + Adjustment; + if (Head->Used <= Head->Capacity) + return new (PP) T[Count](); + + addNode(std::max(AllocUnit, Size)); + Head->Used = Size; + return new (Head->Buf) T[Count](); + } + + template <typename T, typename... Args> T *alloc(Args &&... ConstructorArgs) { + constexpr size_t Size = sizeof(T); + assert(Head && Head->Buf); + + size_t P = (size_t)Head->Buf + Head->Used; + uintptr_t AlignedP = + (((size_t)P + alignof(T) - 1) & ~(size_t)(alignof(T) - 1)); + uint8_t *PP = (uint8_t *)AlignedP; + size_t Adjustment = AlignedP - P; + + Head->Used += Size + Adjustment; + if (Head->Used <= Head->Capacity) + return new (PP) T(std::forward<Args>(ConstructorArgs)...); + + static_assert(Size < AllocUnit); + addNode(AllocUnit); + Head->Used = Size; + return new (Head->Buf) T(std::forward<Args>(ConstructorArgs)...); + } + +private: + AllocatorNode *Head = nullptr; +}; + +struct BackrefContext { + static constexpr size_t Max = 10; + + TypeNode *FunctionParams[Max]; + size_t FunctionParamCount = 0; + + // The first 10 BackReferences in a mangled name can be back-referenced by + // special name @[0-9]. This is a storage for the first 10 BackReferences. + NamedIdentifierNode *Names[Max]; + size_t NamesCount = 0; +}; + +enum class QualifierMangleMode { Drop, Mangle, Result }; + +enum NameBackrefBehavior : uint8_t { + NBB_None = 0, // don't save any names as backrefs. + NBB_Template = 1 << 0, // save template instanations. + NBB_Simple = 1 << 1, // save simple names. +}; + +enum class FunctionIdentifierCodeGroup { Basic, Under, DoubleUnder }; + +// Demangler class takes the main role in demangling symbols. +// It has a set of functions to parse mangled symbols into Type instances. +// It also has a set of functions to convert Type instances to strings. +class Demangler { +public: + Demangler() = default; + virtual ~Demangler() = default; + + // You are supposed to call parse() first and then check if error is true. If + // it is false, call output() to write the formatted name to the given stream. + SymbolNode *parse(StringView &MangledName); + + TagTypeNode *parseTagUniqueName(StringView &MangledName); + + // True if an error occurred. + bool Error = false; + + void dumpBackReferences(); + +private: + SymbolNode *demangleEncodedSymbol(StringView &MangledName, + QualifiedNameNode *QN); + SymbolNode *demangleDeclarator(StringView &MangledName); + SymbolNode *demangleMD5Name(StringView &MangledName); + SymbolNode *demangleTypeinfoName(StringView &MangledName); + + VariableSymbolNode *demangleVariableEncoding(StringView &MangledName, + StorageClass SC); + FunctionSymbolNode *demangleFunctionEncoding(StringView &MangledName); + + Qualifiers demanglePointerExtQualifiers(StringView &MangledName); + + // Parser functions. This is a recursive-descent parser. + TypeNode *demangleType(StringView &MangledName, QualifierMangleMode QMM); + PrimitiveTypeNode *demanglePrimitiveType(StringView &MangledName); + CustomTypeNode *demangleCustomType(StringView &MangledName); + TagTypeNode *demangleClassType(StringView &MangledName); + PointerTypeNode *demanglePointerType(StringView &MangledName); + PointerTypeNode *demangleMemberPointerType(StringView &MangledName); + FunctionSignatureNode *demangleFunctionType(StringView &MangledName, + bool HasThisQuals); + + ArrayTypeNode *demangleArrayType(StringView &MangledName); + + NodeArrayNode *demangleFunctionParameterList(StringView &MangledName, + bool &IsVariadic); + NodeArrayNode *demangleTemplateParameterList(StringView &MangledName); + + std::pair<uint64_t, bool> demangleNumber(StringView &MangledName); + uint64_t demangleUnsigned(StringView &MangledName); + int64_t demangleSigned(StringView &MangledName); + + void memorizeString(StringView s); + void memorizeIdentifier(IdentifierNode *Identifier); + + /// Allocate a copy of \p Borrowed into memory that we own. + StringView copyString(StringView Borrowed); + + QualifiedNameNode *demangleFullyQualifiedTypeName(StringView &MangledName); + QualifiedNameNode *demangleFullyQualifiedSymbolName(StringView &MangledName); + + IdentifierNode *demangleUnqualifiedTypeName(StringView &MangledName, + bool Memorize); + IdentifierNode *demangleUnqualifiedSymbolName(StringView &MangledName, + NameBackrefBehavior NBB); + + QualifiedNameNode *demangleNameScopeChain(StringView &MangledName, + IdentifierNode *UnqualifiedName); + IdentifierNode *demangleNameScopePiece(StringView &MangledName); + + NamedIdentifierNode *demangleBackRefName(StringView &MangledName); + IdentifierNode *demangleTemplateInstantiationName(StringView &MangledName, + NameBackrefBehavior NBB); + IntrinsicFunctionKind + translateIntrinsicFunctionCode(char CH, FunctionIdentifierCodeGroup Group); + IdentifierNode *demangleFunctionIdentifierCode(StringView &MangledName); + IdentifierNode * + demangleFunctionIdentifierCode(StringView &MangledName, + FunctionIdentifierCodeGroup Group); + StructorIdentifierNode *demangleStructorIdentifier(StringView &MangledName, + bool IsDestructor); + ConversionOperatorIdentifierNode * + demangleConversionOperatorIdentifier(StringView &MangledName); + LiteralOperatorIdentifierNode * + demangleLiteralOperatorIdentifier(StringView &MangledName); + + SymbolNode *demangleSpecialIntrinsic(StringView &MangledName); + SpecialTableSymbolNode * + demangleSpecialTableSymbolNode(StringView &MangledName, + SpecialIntrinsicKind SIK); + LocalStaticGuardVariableNode * + demangleLocalStaticGuard(StringView &MangledName, bool IsThread); + VariableSymbolNode *demangleUntypedVariable(ArenaAllocator &Arena, + StringView &MangledName, + StringView VariableName); + VariableSymbolNode * + demangleRttiBaseClassDescriptorNode(ArenaAllocator &Arena, + StringView &MangledName); + FunctionSymbolNode *demangleInitFiniStub(StringView &MangledName, + bool IsDestructor); + + NamedIdentifierNode *demangleSimpleName(StringView &MangledName, + bool Memorize); + NamedIdentifierNode *demangleAnonymousNamespaceName(StringView &MangledName); + NamedIdentifierNode *demangleLocallyScopedNamePiece(StringView &MangledName); + EncodedStringLiteralNode *demangleStringLiteral(StringView &MangledName); + FunctionSymbolNode *demangleVcallThunkNode(StringView &MangledName); + + StringView demangleSimpleString(StringView &MangledName, bool Memorize); + + FuncClass demangleFunctionClass(StringView &MangledName); + CallingConv demangleCallingConvention(StringView &MangledName); + StorageClass demangleVariableStorageClass(StringView &MangledName); + bool demangleThrowSpecification(StringView &MangledName); + wchar_t demangleWcharLiteral(StringView &MangledName); + uint8_t demangleCharLiteral(StringView &MangledName); + + std::pair<Qualifiers, bool> demangleQualifiers(StringView &MangledName); + + // Memory allocator. + ArenaAllocator Arena; + + // A single type uses one global back-ref table for all function params. + // This means back-refs can even go "into" other types. Examples: + // + // // Second int* is a back-ref to first. + // void foo(int *, int*); + // + // // Second int* is not a back-ref to first (first is not a function param). + // int* foo(int*); + // + // // Second int* is a back-ref to first (ALL function types share the same + // // back-ref map. + // using F = void(*)(int*); + // F G(int *); + BackrefContext Backrefs; +}; + +} // namespace ms_demangle +} // namespace llvm + +#endif // LLVM_DEMANGLE_MICROSOFTDEMANGLE_H diff --git a/demangle/third_party/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h b/demangle/third_party/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h new file mode 100644 index 00000000..8ad24723 --- /dev/null +++ b/demangle/third_party/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h @@ -0,0 +1,629 @@ +//===- MicrosoftDemangleNodes.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 defines the AST nodes used in the MSVC demangler. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEMANGLE_MICROSOFTDEMANGLENODES_H +#define LLVM_DEMANGLE_MICROSOFTDEMANGLENODES_H + +#include "llvm/Demangle/StringView.h" +#include <array> +#include <cstdint> +#include <string> + +namespace llvm { +namespace itanium_demangle { +class OutputBuffer; +} +} + +using llvm::itanium_demangle::OutputBuffer; +using llvm::itanium_demangle::StringView; + +namespace llvm { +namespace ms_demangle { + +// Storage classes +enum Qualifiers : uint8_t { + Q_None = 0, + Q_Const = 1 << 0, + Q_Volatile = 1 << 1, + Q_Far = 1 << 2, + Q_Huge = 1 << 3, + Q_Unaligned = 1 << 4, + Q_Restrict = 1 << 5, + Q_Pointer64 = 1 << 6 +}; + +enum class StorageClass : uint8_t { + None, + PrivateStatic, + ProtectedStatic, + PublicStatic, + Global, + FunctionLocalStatic, +}; + +enum class PointerAffinity { None, Pointer, Reference, RValueReference }; +enum class FunctionRefQualifier { None, Reference, RValueReference }; + +// Calling conventions +enum class CallingConv : uint8_t { + None, + Cdecl, + Pascal, + Thiscall, + Stdcall, + Fastcall, + Clrcall, + Eabi, + Vectorcall, + Regcall, + Swift, // Clang-only + SwiftAsync, // Clang-only +}; + +enum class ReferenceKind : uint8_t { None, LValueRef, RValueRef }; + +enum OutputFlags { + OF_Default = 0, + OF_NoCallingConvention = 1, + OF_NoTagSpecifier = 2, + OF_NoAccessSpecifier = 4, + OF_NoMemberType = 8, + OF_NoReturnType = 16, + OF_NoVariableType = 32, +}; + +// Types +enum class PrimitiveKind { + Void, + Bool, + Char, + Schar, + Uchar, + Char8, + Char16, + Char32, + Short, + Ushort, + Int, + Uint, + Long, + Ulong, + Int64, + Uint64, + Wchar, + Float, + Double, + Ldouble, + Nullptr, +}; + +enum class CharKind { + Char, + Char16, + Char32, + Wchar, +}; + +enum class IntrinsicFunctionKind : uint8_t { + None, + New, // ?2 # operator new + Delete, // ?3 # operator delete + Assign, // ?4 # operator= + RightShift, // ?5 # operator>> + LeftShift, // ?6 # operator<< + LogicalNot, // ?7 # operator! + Equals, // ?8 # operator== + NotEquals, // ?9 # operator!= + ArraySubscript, // ?A # operator[] + Pointer, // ?C # operator-> + Dereference, // ?D # operator* + Increment, // ?E # operator++ + Decrement, // ?F # operator-- + Minus, // ?G # operator- + Plus, // ?H # operator+ + BitwiseAnd, // ?I # operator& + MemberPointer, // ?J # operator->* + Divide, // ?K # operator/ + Modulus, // ?L # operator% + LessThan, // ?M operator< + LessThanEqual, // ?N operator<= + GreaterThan, // ?O operator> + GreaterThanEqual, // ?P operator>= + Comma, // ?Q operator, + Parens, // ?R operator() + BitwiseNot, // ?S operator~ + BitwiseXor, // ?T operator^ + BitwiseOr, // ?U operator| + LogicalAnd, // ?V operator&& + LogicalOr, // ?W operator|| + TimesEqual, // ?X operator*= + PlusEqual, // ?Y operator+= + MinusEqual, // ?Z operator-= + DivEqual, // ?_0 operator/= + ModEqual, // ?_1 operator%= + RshEqual, // ?_2 operator>>= + LshEqual, // ?_3 operator<<= + BitwiseAndEqual, // ?_4 operator&= + BitwiseOrEqual, // ?_5 operator|= + BitwiseXorEqual, // ?_6 operator^= + VbaseDtor, // ?_D # vbase destructor + VecDelDtor, // ?_E # vector deleting destructor + DefaultCtorClosure, // ?_F # default constructor closure + ScalarDelDtor, // ?_G # scalar deleting destructor + VecCtorIter, // ?_H # vector constructor iterator + VecDtorIter, // ?_I # vector destructor iterator + VecVbaseCtorIter, // ?_J # vector vbase constructor iterator + VdispMap, // ?_K # virtual displacement map + EHVecCtorIter, // ?_L # eh vector constructor iterator + EHVecDtorIter, // ?_M # eh vector destructor iterator + EHVecVbaseCtorIter, // ?_N # eh vector vbase constructor iterator + CopyCtorClosure, // ?_O # copy constructor closure + LocalVftableCtorClosure, // ?_T # local vftable constructor closure + ArrayNew, // ?_U operator new[] + ArrayDelete, // ?_V operator delete[] + ManVectorCtorIter, // ?__A managed vector ctor iterator + ManVectorDtorIter, // ?__B managed vector dtor iterator + EHVectorCopyCtorIter, // ?__C EH vector copy ctor iterator + EHVectorVbaseCopyCtorIter, // ?__D EH vector vbase copy ctor iterator + VectorCopyCtorIter, // ?__G vector copy constructor iterator + VectorVbaseCopyCtorIter, // ?__H vector vbase copy constructor iterator + ManVectorVbaseCopyCtorIter, // ?__I managed vector vbase copy constructor + CoAwait, // ?__L operator co_await + Spaceship, // ?__M operator<=> + MaxIntrinsic +}; + +enum class SpecialIntrinsicKind { + None, + Vftable, + Vbtable, + Typeof, + VcallThunk, + LocalStaticGuard, + StringLiteralSymbol, + UdtReturning, + Unknown, + DynamicInitializer, + DynamicAtexitDestructor, + RttiTypeDescriptor, + RttiBaseClassDescriptor, + RttiBaseClassArray, + RttiClassHierarchyDescriptor, + RttiCompleteObjLocator, + LocalVftable, + LocalStaticThreadGuard, +}; + +// Function classes +enum FuncClass : uint16_t { + FC_None = 0, + FC_Public = 1 << 0, + FC_Protected = 1 << 1, + FC_Private = 1 << 2, + FC_Global = 1 << 3, + FC_Static = 1 << 4, + FC_Virtual = 1 << 5, + FC_Far = 1 << 6, + FC_ExternC = 1 << 7, + FC_NoParameterList = 1 << 8, + FC_VirtualThisAdjust = 1 << 9, + FC_VirtualThisAdjustEx = 1 << 10, + FC_StaticThisAdjust = 1 << 11, +}; + +enum class TagKind { Class, Struct, Union, Enum }; + +enum class NodeKind { + Unknown, + Md5Symbol, + PrimitiveType, + FunctionSignature, + Identifier, + NamedIdentifier, + VcallThunkIdentifier, + LocalStaticGuardIdentifier, + IntrinsicFunctionIdentifier, + ConversionOperatorIdentifier, + DynamicStructorIdentifier, + StructorIdentifier, + LiteralOperatorIdentifier, + ThunkSignature, + PointerType, + TagType, + ArrayType, + Custom, + IntrinsicType, + NodeArray, + QualifiedName, + TemplateParameterReference, + EncodedStringLiteral, + IntegerLiteral, + RttiBaseClassDescriptor, + LocalStaticGuardVariable, + FunctionSymbol, + VariableSymbol, + SpecialTableSymbol +}; + +struct Node { + explicit Node(NodeKind K) : Kind(K) {} + virtual ~Node() = default; + + NodeKind kind() const { return Kind; } + + virtual void output(OutputBuffer &OB, OutputFlags Flags) const = 0; + + std::string toString(OutputFlags Flags = OF_Default) const; + +private: + NodeKind Kind; +}; + +struct TypeNode; +struct PrimitiveTypeNode; +struct FunctionSignatureNode; +struct IdentifierNode; +struct NamedIdentifierNode; +struct VcallThunkIdentifierNode; +struct IntrinsicFunctionIdentifierNode; +struct LiteralOperatorIdentifierNode; +struct ConversionOperatorIdentifierNode; +struct StructorIdentifierNode; +struct ThunkSignatureNode; +struct PointerTypeNode; +struct ArrayTypeNode; +struct TagTypeNode; +struct NodeArrayNode; +struct QualifiedNameNode; +struct TemplateParameterReferenceNode; +struct EncodedStringLiteralNode; +struct IntegerLiteralNode; +struct RttiBaseClassDescriptorNode; +struct LocalStaticGuardVariableNode; +struct SymbolNode; +struct FunctionSymbolNode; +struct VariableSymbolNode; +struct SpecialTableSymbolNode; + +struct TypeNode : public Node { + explicit TypeNode(NodeKind K) : Node(K) {} + + virtual void outputPre(OutputBuffer &OB, OutputFlags Flags) const = 0; + virtual void outputPost(OutputBuffer &OB, OutputFlags Flags) const = 0; + + void output(OutputBuffer &OB, OutputFlags Flags) const override { + outputPre(OB, Flags); + outputPost(OB, Flags); + } + + Qualifiers Quals = Q_None; +}; + +struct PrimitiveTypeNode : public TypeNode { + explicit PrimitiveTypeNode(PrimitiveKind K) + : TypeNode(NodeKind::PrimitiveType), PrimKind(K) {} + + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override {} + + PrimitiveKind PrimKind; +}; + +struct FunctionSignatureNode : public TypeNode { + explicit FunctionSignatureNode(NodeKind K) : TypeNode(K) {} + FunctionSignatureNode() : TypeNode(NodeKind::FunctionSignature) {} + + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override; + + // Valid if this FunctionTypeNode is the Pointee of a PointerType or + // MemberPointerType. + PointerAffinity Affinity = PointerAffinity::None; + + // The function's calling convention. + CallingConv CallConvention = CallingConv::None; + + // Function flags (gloabl, public, etc) + FuncClass FunctionClass = FC_Global; + + FunctionRefQualifier RefQualifier = FunctionRefQualifier::None; + + // The return type of the function. + TypeNode *ReturnType = nullptr; + + // True if this is a C-style ... varargs function. + bool IsVariadic = false; + + // Function parameters + NodeArrayNode *Params = nullptr; + + // True if the function type is noexcept. + bool IsNoexcept = false; +}; + +struct IdentifierNode : public Node { + explicit IdentifierNode(NodeKind K) : Node(K) {} + + NodeArrayNode *TemplateParams = nullptr; + +protected: + void outputTemplateParameters(OutputBuffer &OB, OutputFlags Flags) const; +}; + +struct VcallThunkIdentifierNode : public IdentifierNode { + VcallThunkIdentifierNode() : IdentifierNode(NodeKind::VcallThunkIdentifier) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + uint64_t OffsetInVTable = 0; +}; + +struct DynamicStructorIdentifierNode : public IdentifierNode { + DynamicStructorIdentifierNode() + : IdentifierNode(NodeKind::DynamicStructorIdentifier) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + VariableSymbolNode *Variable = nullptr; + QualifiedNameNode *Name = nullptr; + bool IsDestructor = false; +}; + +struct NamedIdentifierNode : public IdentifierNode { + NamedIdentifierNode() : IdentifierNode(NodeKind::NamedIdentifier) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + StringView Name; +}; + +struct IntrinsicFunctionIdentifierNode : public IdentifierNode { + explicit IntrinsicFunctionIdentifierNode(IntrinsicFunctionKind Operator) + : IdentifierNode(NodeKind::IntrinsicFunctionIdentifier), + Operator(Operator) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + IntrinsicFunctionKind Operator; +}; + +struct LiteralOperatorIdentifierNode : public IdentifierNode { + LiteralOperatorIdentifierNode() + : IdentifierNode(NodeKind::LiteralOperatorIdentifier) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + StringView Name; +}; + +struct LocalStaticGuardIdentifierNode : public IdentifierNode { + LocalStaticGuardIdentifierNode() + : IdentifierNode(NodeKind::LocalStaticGuardIdentifier) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + bool IsThread = false; + uint32_t ScopeIndex = 0; +}; + +struct ConversionOperatorIdentifierNode : public IdentifierNode { + ConversionOperatorIdentifierNode() + : IdentifierNode(NodeKind::ConversionOperatorIdentifier) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + // The type that this operator converts too. + TypeNode *TargetType = nullptr; +}; + +struct StructorIdentifierNode : public IdentifierNode { + StructorIdentifierNode() : IdentifierNode(NodeKind::StructorIdentifier) {} + explicit StructorIdentifierNode(bool IsDestructor) + : IdentifierNode(NodeKind::StructorIdentifier), + IsDestructor(IsDestructor) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + // The name of the class that this is a structor of. + IdentifierNode *Class = nullptr; + bool IsDestructor = false; +}; + +struct ThunkSignatureNode : public FunctionSignatureNode { + ThunkSignatureNode() : FunctionSignatureNode(NodeKind::ThunkSignature) {} + + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override; + + struct ThisAdjustor { + uint32_t StaticOffset = 0; + int32_t VBPtrOffset = 0; + int32_t VBOffsetOffset = 0; + int32_t VtordispOffset = 0; + }; + + ThisAdjustor ThisAdjust; +}; + +struct PointerTypeNode : public TypeNode { + PointerTypeNode() : TypeNode(NodeKind::PointerType) {} + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override; + + // Is this a pointer, reference, or rvalue-reference? + PointerAffinity Affinity = PointerAffinity::None; + + // If this is a member pointer, this is the class that the member is in. + QualifiedNameNode *ClassParent = nullptr; + + // Represents a type X in "a pointer to X", "a reference to X", or + // "rvalue-reference to X" + TypeNode *Pointee = nullptr; +}; + +struct TagTypeNode : public TypeNode { + explicit TagTypeNode(TagKind Tag) : TypeNode(NodeKind::TagType), Tag(Tag) {} + + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override; + + QualifiedNameNode *QualifiedName = nullptr; + TagKind Tag; +}; + +struct ArrayTypeNode : public TypeNode { + ArrayTypeNode() : TypeNode(NodeKind::ArrayType) {} + + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override; + + void outputDimensionsImpl(OutputBuffer &OB, OutputFlags Flags) const; + void outputOneDimension(OutputBuffer &OB, OutputFlags Flags, Node *N) const; + + // A list of array dimensions. e.g. [3,4,5] in `int Foo[3][4][5]` + NodeArrayNode *Dimensions = nullptr; + + // The type of array element. + TypeNode *ElementType = nullptr; +}; + +struct IntrinsicNode : public TypeNode { + IntrinsicNode() : TypeNode(NodeKind::IntrinsicType) {} + void output(OutputBuffer &OB, OutputFlags Flags) const override {} +}; + +struct CustomTypeNode : public TypeNode { + CustomTypeNode() : TypeNode(NodeKind::Custom) {} + + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override; + + IdentifierNode *Identifier = nullptr; +}; + +struct NodeArrayNode : public Node { + NodeArrayNode() : Node(NodeKind::NodeArray) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + void output(OutputBuffer &OB, OutputFlags Flags, StringView Separator) const; + + Node **Nodes = nullptr; + size_t Count = 0; +}; + +struct QualifiedNameNode : public Node { + QualifiedNameNode() : Node(NodeKind::QualifiedName) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + NodeArrayNode *Components = nullptr; + + IdentifierNode *getUnqualifiedIdentifier() { + Node *LastComponent = Components->Nodes[Components->Count - 1]; + return static_cast<IdentifierNode *>(LastComponent); + } +}; + +struct TemplateParameterReferenceNode : public Node { + TemplateParameterReferenceNode() + : Node(NodeKind::TemplateParameterReference) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + SymbolNode *Symbol = nullptr; + + int ThunkOffsetCount = 0; + std::array<int64_t, 3> ThunkOffsets; + PointerAffinity Affinity = PointerAffinity::None; + bool IsMemberPointer = false; +}; + +struct IntegerLiteralNode : public Node { + IntegerLiteralNode() : Node(NodeKind::IntegerLiteral) {} + IntegerLiteralNode(uint64_t Value, bool IsNegative) + : Node(NodeKind::IntegerLiteral), Value(Value), IsNegative(IsNegative) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + uint64_t Value = 0; + bool IsNegative = false; +}; + +struct RttiBaseClassDescriptorNode : public IdentifierNode { + RttiBaseClassDescriptorNode() + : IdentifierNode(NodeKind::RttiBaseClassDescriptor) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + uint32_t NVOffset = 0; + int32_t VBPtrOffset = 0; + uint32_t VBTableOffset = 0; + uint32_t Flags = 0; +}; + +struct SymbolNode : public Node { + explicit SymbolNode(NodeKind K) : Node(K) {} + void output(OutputBuffer &OB, OutputFlags Flags) const override; + QualifiedNameNode *Name = nullptr; +}; + +struct SpecialTableSymbolNode : public SymbolNode { + explicit SpecialTableSymbolNode() + : SymbolNode(NodeKind::SpecialTableSymbol) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + QualifiedNameNode *TargetName = nullptr; + Qualifiers Quals = Qualifiers::Q_None; +}; + +struct LocalStaticGuardVariableNode : public SymbolNode { + LocalStaticGuardVariableNode() + : SymbolNode(NodeKind::LocalStaticGuardVariable) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + bool IsVisible = false; +}; + +struct EncodedStringLiteralNode : public SymbolNode { + EncodedStringLiteralNode() : SymbolNode(NodeKind::EncodedStringLiteral) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + StringView DecodedString; + bool IsTruncated = false; + CharKind Char = CharKind::Char; +}; + +struct VariableSymbolNode : public SymbolNode { + VariableSymbolNode() : SymbolNode(NodeKind::VariableSymbol) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + StorageClass SC = StorageClass::None; + TypeNode *Type = nullptr; +}; + +struct FunctionSymbolNode : public SymbolNode { + FunctionSymbolNode() : SymbolNode(NodeKind::FunctionSymbol) {} + + void output(OutputBuffer &OB, OutputFlags Flags) const override; + + FunctionSignatureNode *Signature = nullptr; +}; + +} // namespace ms_demangle +} // namespace llvm + +#endif diff --git a/demangle/third_party/llvm/include/llvm/Demangle/StringView.h b/demangle/third_party/llvm/include/llvm/Demangle/StringView.h new file mode 100644 index 00000000..6bbb8837 --- /dev/null +++ b/demangle/third_party/llvm/include/llvm/Demangle/StringView.h @@ -0,0 +1,122 @@ +//===--- StringView.h ----------------*- mode:c++;eval:(read-only-mode) -*-===// +// Do not edit! See README.txt. +// 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: Use std::string_view instead when we support C++17. +// There are two copies of this file in the source tree. The one under +// libcxxabi is the original and the one under llvm is the copy. Use +// cp-to-llvm.sh to update the copy. See README.txt for more details. +// +//===----------------------------------------------------------------------===// + +#ifndef DEMANGLE_STRINGVIEW_H +#define DEMANGLE_STRINGVIEW_H + +#include "DemangleConfig.h" +#include <cassert> +#include <cstring> + +DEMANGLE_NAMESPACE_BEGIN + +class StringView { + const char *First; + const char *Last; + +public: + static const size_t npos = ~size_t(0); + + template <size_t N> + StringView(const char (&Str)[N]) : First(Str), Last(Str + N - 1) {} + StringView(const char *First_, const char *Last_) + : First(First_), Last(Last_) {} + StringView(const char *First_, size_t Len) + : First(First_), Last(First_ + Len) {} + StringView(const char *Str) : First(Str), Last(Str + std::strlen(Str)) {} + StringView() : First(nullptr), Last(nullptr) {} + + StringView substr(size_t Pos, size_t Len = npos) const { + assert(Pos <= size()); + if (Len > size() - Pos) + Len = size() - Pos; + return StringView(begin() + Pos, Len); + } + + size_t find(char C, size_t From = 0) const { + // Avoid calling memchr with nullptr. + if (From < size()) { + // Just forward to memchr, which is faster than a hand-rolled loop. + if (const void *P = ::memchr(First + From, C, size() - From)) + return size_t(static_cast<const char *>(P) - First); + } + return npos; + } + + StringView dropFront(size_t N = 1) const { + if (N >= size()) + N = size(); + return StringView(First + N, Last); + } + + StringView dropBack(size_t N = 1) const { + if (N >= size()) + N = size(); + return StringView(First, Last - N); + } + + char front() const { + assert(!empty()); + return *begin(); + } + + char back() const { + assert(!empty()); + return *(end() - 1); + } + + char popFront() { + assert(!empty()); + return *First++; + } + + bool consumeFront(char C) { + if (!startsWith(C)) + return false; + *this = dropFront(1); + return true; + } + + bool consumeFront(StringView S) { + if (!startsWith(S)) + return false; + *this = dropFront(S.size()); + return true; + } + + bool startsWith(char C) const { return !empty() && *begin() == C; } + + bool startsWith(StringView Str) const { + if (Str.size() > size()) + return false; + return std::strncmp(Str.begin(), begin(), Str.size()) == 0; + } + + const char &operator[](size_t Idx) const { return *(begin() + Idx); } + + const char *begin() const { return First; } + const char *end() const { return Last; } + size_t size() const { return static_cast<size_t>(Last - First); } + bool empty() const { return First == Last; } +}; + +inline bool operator==(const StringView &LHS, const StringView &RHS) { + return LHS.size() == RHS.size() && + std::strncmp(LHS.begin(), RHS.begin(), LHS.size()) == 0; +} + +DEMANGLE_NAMESPACE_END + +#endif diff --git a/demangle/third_party/llvm/include/llvm/Demangle/Utility.h b/demangle/third_party/llvm/include/llvm/Demangle/Utility.h new file mode 100644 index 00000000..855c56e9 --- /dev/null +++ b/demangle/third_party/llvm/include/llvm/Demangle/Utility.h @@ -0,0 +1,199 @@ +//===--- Utility.h -------------------*- mode:c++;eval:(read-only-mode) -*-===// +// Do not edit! See README.txt. +// 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 +// +//===----------------------------------------------------------------------===// +// +// Provide some utility classes for use in the demangler. +// There are two copies of this file in the source tree. The one in libcxxabi +// is the original and the one in llvm is the copy. Use cp-to-llvm.sh to update +// the copy. See README.txt for more details. +// +//===----------------------------------------------------------------------===// + +#ifndef DEMANGLE_UTILITY_H +#define DEMANGLE_UTILITY_H + +#include "StringView.h" +#include <array> +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <exception> +#include <limits> + +DEMANGLE_NAMESPACE_BEGIN + +// Stream that AST nodes write their string representation into after the AST +// has been parsed. +class OutputBuffer { + char *Buffer = nullptr; + size_t CurrentPosition = 0; + size_t BufferCapacity = 0; + + // Ensure there are at least N more positions in the buffer. + void grow(size_t N) { + size_t Need = N + CurrentPosition; + if (Need > BufferCapacity) { + // Reduce the number of reallocations, with a bit of hysteresis. The + // number here is chosen so the first allocation will more-than-likely not + // allocate more than 1K. + Need += 1024 - 32; + BufferCapacity *= 2; + if (BufferCapacity < Need) + BufferCapacity = Need; + Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity)); + if (Buffer == nullptr) + std::terminate(); + } + } + + OutputBuffer &writeUnsigned(uint64_t N, bool isNeg = false) { + std::array<char, 21> Temp; + char *TempPtr = Temp.data() + Temp.size(); + + // Output at least one character. + do { + *--TempPtr = char('0' + N % 10); + N /= 10; + } while (N); + + // Add negative sign. + if (isNeg) + *--TempPtr = '-'; + + return operator+=(StringView(TempPtr, Temp.data() + Temp.size())); + } + +public: + OutputBuffer(char *StartBuf, size_t Size) + : Buffer(StartBuf), BufferCapacity(Size) {} + OutputBuffer(char *StartBuf, size_t *SizePtr) + : OutputBuffer(StartBuf, StartBuf ? *SizePtr : 0) {} + OutputBuffer() = default; + // Non-copyable + OutputBuffer(const OutputBuffer &) = delete; + OutputBuffer &operator=(const OutputBuffer &) = delete; + + operator StringView() const { return StringView(Buffer, CurrentPosition); } + + /// If a ParameterPackExpansion (or similar type) is encountered, the offset + /// into the pack that we're currently printing. + unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max(); + unsigned CurrentPackMax = std::numeric_limits<unsigned>::max(); + + /// When zero, we're printing template args and '>' needs to be parenthesized. + /// Use a counter so we can simply increment inside parentheses. + unsigned GtIsGt = 1; + + bool isGtInsideTemplateArgs() const { return GtIsGt == 0; } + + void printOpen(char Open = '(') { + GtIsGt++; + *this += Open; + } + void printClose(char Close = ')') { + GtIsGt--; + *this += Close; + } + + OutputBuffer &operator+=(StringView R) { + if (size_t Size = R.size()) { + grow(Size); + std::memcpy(Buffer + CurrentPosition, R.begin(), Size); + CurrentPosition += Size; + } + return *this; + } + + OutputBuffer &operator+=(char C) { + grow(1); + Buffer[CurrentPosition++] = C; + return *this; + } + + OutputBuffer &prepend(StringView R) { + size_t Size = R.size(); + + grow(Size); + std::memmove(Buffer + Size, Buffer, CurrentPosition); + std::memcpy(Buffer, R.begin(), Size); + CurrentPosition += Size; + + return *this; + } + + OutputBuffer &operator<<(StringView R) { return (*this += R); } + + OutputBuffer &operator<<(char C) { return (*this += C); } + + OutputBuffer &operator<<(long long N) { + return writeUnsigned(static_cast<unsigned long long>(std::abs(N)), N < 0); + } + + OutputBuffer &operator<<(unsigned long long N) { + return writeUnsigned(N, false); + } + + OutputBuffer &operator<<(long N) { + return this->operator<<(static_cast<long long>(N)); + } + + OutputBuffer &operator<<(unsigned long N) { + return this->operator<<(static_cast<unsigned long long>(N)); + } + + OutputBuffer &operator<<(int N) { + return this->operator<<(static_cast<long long>(N)); + } + + OutputBuffer &operator<<(unsigned int N) { + return this->operator<<(static_cast<unsigned long long>(N)); + } + + void insert(size_t Pos, const char *S, size_t N) { + assert(Pos <= CurrentPosition); + if (N == 0) + return; + grow(N); + std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos); + std::memcpy(Buffer + Pos, S, N); + CurrentPosition += N; + } + + size_t getCurrentPosition() const { return CurrentPosition; } + void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; } + + char back() const { + assert(CurrentPosition); + return Buffer[CurrentPosition - 1]; + } + + bool empty() const { return CurrentPosition == 0; } + + char *getBuffer() { return Buffer; } + char *getBufferEnd() { return Buffer + CurrentPosition - 1; } + size_t getBufferCapacity() const { return BufferCapacity; } +}; + +template <class T> class ScopedOverride { + T &Loc; + T Original; + +public: + ScopedOverride(T &Loc_) : ScopedOverride(Loc_, Loc_) {} + + ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) { + Loc_ = std::move(NewVal); + } + ~ScopedOverride() { Loc = std::move(Original); } + + ScopedOverride(const ScopedOverride &) = delete; + ScopedOverride &operator=(const ScopedOverride &) = delete; +}; + +DEMANGLE_NAMESPACE_END + +#endif diff --git a/demangle/third_party/llvm/lib/Demangle/Demangle.cpp b/demangle/third_party/llvm/lib/Demangle/Demangle.cpp new file mode 100644 index 00000000..b5f2369d --- /dev/null +++ b/demangle/third_party/llvm/lib/Demangle/Demangle.cpp @@ -0,0 +1,68 @@ +//===-- Demangle.cpp - Common demangling functions ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file This file contains definitions of common demangling functions. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/Demangle/Demangle.h" +#include <cstdlib> +#include <cstring> + +static bool isItaniumEncoding(const char *S) { + // Itanium encoding requires 1 or 3 leading underscores, followed by 'Z'. + return std::strncmp(S, "_Z", 2) == 0 || std::strncmp(S, "___Z", 4) == 0; +} + +#if 0 +static bool isRustEncoding(const char *S) { return S[0] == '_' && S[1] == 'R'; } + +static bool isDLangEncoding(const std::string &MangledName) { + return MangledName.size() >= 2 && MangledName[0] == '_' && + MangledName[1] == 'D'; +} +#endif + +std::string llvm::demangle(const std::string &MangledName) { + std::string Result; + const char *S = MangledName.c_str(); + + if (nonMicrosoftDemangle(S, Result)) + return Result; + + if (S[0] == '_' && nonMicrosoftDemangle(S + 1, Result)) + return Result; + + if (char *Demangled = + microsoftDemangle(S, nullptr, nullptr, nullptr, nullptr)) { + Result = Demangled; + std::free(Demangled); + return Result; + } + + return MangledName; +} + +bool llvm::nonMicrosoftDemangle(const char *MangledName, std::string &Result) { + char *Demangled = nullptr; + if (isItaniumEncoding(MangledName)) + Demangled = itaniumDemangle(MangledName, nullptr, nullptr, nullptr); +#if 0 + else if (isRustEncoding(MangledName)) + Demangled = rustDemangle(MangledName); + else if (isDLangEncoding(MangledName)) + Demangled = dlangDemangle(MangledName); +#endif + + if (!Demangled) + return false; + + Result = Demangled; + std::free(Demangled); + return true; +} diff --git a/demangle/third_party/llvm/lib/Demangle/ItaniumDemangle.cpp b/demangle/third_party/llvm/lib/Demangle/ItaniumDemangle.cpp new file mode 100644 index 00000000..9b646ea8 --- /dev/null +++ b/demangle/third_party/llvm/lib/Demangle/ItaniumDemangle.cpp @@ -0,0 +1,608 @@ +//===------------------------- ItaniumDemangle.cpp ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// FIXME: (possibly) incomplete list of features that clang mangles that this +// file does not yet support: +// - C++ modules TS + +#include "llvm/Demangle/Demangle.h" +#include "llvm/Demangle/ItaniumDemangle.h" + +#include <cassert> +#include <cctype> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <functional> +#include <utility> + +using namespace llvm; +using namespace llvm::itanium_demangle; + +constexpr const char *itanium_demangle::FloatData<float>::spec; +constexpr const char *itanium_demangle::FloatData<double>::spec; +constexpr const char *itanium_demangle::FloatData<long double>::spec; + +// <discriminator> := _ <non-negative number> # when number < 10 +// := __ <non-negative number> _ # when number >= 10 +// extension := decimal-digit+ # at the end of string +const char *itanium_demangle::parse_discriminator(const char *first, + const char *last) { + // parse but ignore discriminator + if (first != last) { + if (*first == '_') { + const char *t1 = first + 1; + if (t1 != last) { + if (std::isdigit(*t1)) + first = t1 + 1; + else if (*t1 == '_') { + for (++t1; t1 != last && std::isdigit(*t1); ++t1) + ; + if (t1 != last && *t1 == '_') + first = t1 + 1; + } + } + } else if (std::isdigit(*first)) { + const char *t1 = first + 1; + for (; t1 != last && std::isdigit(*t1); ++t1) + ; + if (t1 == last) + first = last; + } + } + return first; +} + +#ifndef NDEBUG +namespace { +struct DumpVisitor { + unsigned Depth = 0; + bool PendingNewline = false; + + template<typename NodeT> static constexpr bool wantsNewline(const NodeT *) { + return true; + } + static bool wantsNewline(NodeArray A) { return !A.empty(); } + static constexpr bool wantsNewline(...) { return false; } + + template<typename ...Ts> static bool anyWantNewline(Ts ...Vs) { + for (bool B : {wantsNewline(Vs)...}) + if (B) + return true; + return false; + } + + void printStr(const char *S) { fprintf(stderr, "%s", S); } + void print(StringView SV) { + fprintf(stderr, "\"%.*s\"", (int)SV.size(), SV.begin()); + } + void print(const Node *N) { + if (N) + N->visit(std::ref(*this)); + else + printStr("<null>"); + } + void print(NodeArray A) { + ++Depth; + printStr("{"); + bool First = true; + for (const Node *N : A) { + if (First) + print(N); + else + printWithComma(N); + First = false; + } + printStr("}"); + --Depth; + } + + // Overload used when T is exactly 'bool', not merely convertible to 'bool'. + void print(bool B) { printStr(B ? "true" : "false"); } + + template <class T> std::enable_if_t<std::is_unsigned<T>::value> print(T N) { + fprintf(stderr, "%llu", (unsigned long long)N); + } + + template <class T> std::enable_if_t<std::is_signed<T>::value> print(T N) { + fprintf(stderr, "%lld", (long long)N); + } + + void print(ReferenceKind RK) { + switch (RK) { + case ReferenceKind::LValue: + return printStr("ReferenceKind::LValue"); + case ReferenceKind::RValue: + return printStr("ReferenceKind::RValue"); + } + } + void print(FunctionRefQual RQ) { + switch (RQ) { + case FunctionRefQual::FrefQualNone: + return printStr("FunctionRefQual::FrefQualNone"); + case FunctionRefQual::FrefQualLValue: + return printStr("FunctionRefQual::FrefQualLValue"); + case FunctionRefQual::FrefQualRValue: + return printStr("FunctionRefQual::FrefQualRValue"); + } + } + void print(Qualifiers Qs) { + if (!Qs) return printStr("QualNone"); + struct QualName { Qualifiers Q; const char *Name; } Names[] = { + {QualConst, "QualConst"}, + {QualVolatile, "QualVolatile"}, + {QualRestrict, "QualRestrict"}, + }; + for (QualName Name : Names) { + if (Qs & Name.Q) { + printStr(Name.Name); + Qs = Qualifiers(Qs & ~Name.Q); + if (Qs) printStr(" | "); + } + } + } + void print(SpecialSubKind SSK) { + switch (SSK) { + case SpecialSubKind::allocator: + return printStr("SpecialSubKind::allocator"); + case SpecialSubKind::basic_string: + return printStr("SpecialSubKind::basic_string"); + case SpecialSubKind::string: + return printStr("SpecialSubKind::string"); + case SpecialSubKind::istream: + return printStr("SpecialSubKind::istream"); + case SpecialSubKind::ostream: + return printStr("SpecialSubKind::ostream"); + case SpecialSubKind::iostream: + return printStr("SpecialSubKind::iostream"); + } + } + void print(TemplateParamKind TPK) { + switch (TPK) { + case TemplateParamKind::Type: + return printStr("TemplateParamKind::Type"); + case TemplateParamKind::NonType: + return printStr("TemplateParamKind::NonType"); + case TemplateParamKind::Template: + return printStr("TemplateParamKind::Template"); + } + } + void print(Node::Prec P) { + switch (P) { + case Node::Prec::Primary: + return printStr("Node::Prec::Primary"); + case Node::Prec::Postfix: + return printStr("Node::Prec::Postfix"); + case Node::Prec::Unary: + return printStr("Node::Prec::Unary"); + case Node::Prec::Cast: + return printStr("Node::Prec::Cast"); + case Node::Prec::PtrMem: + return printStr("Node::Prec::PtrMem"); + case Node::Prec::Multiplicative: + return printStr("Node::Prec::Multiplicative"); + case Node::Prec::Additive: + return printStr("Node::Prec::Additive"); + case Node::Prec::Shift: + return printStr("Node::Prec::Shift"); + case Node::Prec::Spaceship: + return printStr("Node::Prec::Spaceship"); + case Node::Prec::Relational: + return printStr("Node::Prec::Relational"); + case Node::Prec::Equality: + return printStr("Node::Prec::Equality"); + case Node::Prec::And: + return printStr("Node::Prec::And"); + case Node::Prec::Xor: + return printStr("Node::Prec::Xor"); + case Node::Prec::Ior: + return printStr("Node::Prec::Ior"); + case Node::Prec::AndIf: + return printStr("Node::Prec::AndIf"); + case Node::Prec::OrIf: + return printStr("Node::Prec::OrIf"); + case Node::Prec::Conditional: + return printStr("Node::Prec::Conditional"); + case Node::Prec::Assign: + return printStr("Node::Prec::Assign"); + case Node::Prec::Comma: + return printStr("Node::Prec::Comma"); + case Node::Prec::Default: + return printStr("Node::Prec::Default"); + } + } + + void newLine() { + printStr("\n"); + for (unsigned I = 0; I != Depth; ++I) + printStr(" "); + PendingNewline = false; + } + + template<typename T> void printWithPendingNewline(T V) { + print(V); + if (wantsNewline(V)) + PendingNewline = true; + } + + template<typename T> void printWithComma(T V) { + if (PendingNewline || wantsNewline(V)) { + printStr(","); + newLine(); + } else { + printStr(", "); + } + + printWithPendingNewline(V); + } + + struct CtorArgPrinter { + DumpVisitor &Visitor; + + template<typename T, typename ...Rest> void operator()(T V, Rest ...Vs) { + if (Visitor.anyWantNewline(V, Vs...)) + Visitor.newLine(); + Visitor.printWithPendingNewline(V); + int PrintInOrder[] = { (Visitor.printWithComma(Vs), 0)..., 0 }; + (void)PrintInOrder; + } + }; + + template<typename NodeT> void operator()(const NodeT *Node) { + Depth += 2; + fprintf(stderr, "%s(", itanium_demangle::NodeKind<NodeT>::name()); + Node->match(CtorArgPrinter{*this}); + fprintf(stderr, ")"); + Depth -= 2; + } + + void operator()(const ForwardTemplateReference *Node) { + Depth += 2; + fprintf(stderr, "ForwardTemplateReference("); + if (Node->Ref && !Node->Printing) { + Node->Printing = true; + CtorArgPrinter{*this}(Node->Ref); + Node->Printing = false; + } else { + CtorArgPrinter{*this}(Node->Index); + } + fprintf(stderr, ")"); + Depth -= 2; + } +}; +} + +void itanium_demangle::Node::dump() const { + DumpVisitor V; + visit(std::ref(V)); + V.newLine(); +} +#endif + +namespace { +class BumpPointerAllocator { + struct BlockMeta { + BlockMeta* Next; + size_t Current; + }; + + static constexpr size_t AllocSize = 4096; + static constexpr size_t UsableAllocSize = AllocSize - sizeof(BlockMeta); + + alignas(long double) char InitialBuffer[AllocSize]; + BlockMeta* BlockList = nullptr; + + void grow() { + char* NewMeta = static_cast<char *>(std::malloc(AllocSize)); + if (NewMeta == nullptr) + std::terminate(); + BlockList = new (NewMeta) BlockMeta{BlockList, 0}; + } + + void* allocateMassive(size_t NBytes) { + NBytes += sizeof(BlockMeta); + BlockMeta* NewMeta = reinterpret_cast<BlockMeta*>(std::malloc(NBytes)); + if (NewMeta == nullptr) + std::terminate(); + BlockList->Next = new (NewMeta) BlockMeta{BlockList->Next, 0}; + return static_cast<void*>(NewMeta + 1); + } + +public: + BumpPointerAllocator() + : BlockList(new (InitialBuffer) BlockMeta{nullptr, 0}) {} + + void* allocate(size_t N) { + N = (N + 15u) & ~15u; + if (N + BlockList->Current >= UsableAllocSize) { + if (N > UsableAllocSize) + return allocateMassive(N); + grow(); + } + BlockList->Current += N; + return static_cast<void*>(reinterpret_cast<char*>(BlockList + 1) + + BlockList->Current - N); + } + + void reset() { + while (BlockList) { + BlockMeta* Tmp = BlockList; + BlockList = BlockList->Next; + if (reinterpret_cast<char*>(Tmp) != InitialBuffer) + std::free(Tmp); + } + BlockList = new (InitialBuffer) BlockMeta{nullptr, 0}; + } + + ~BumpPointerAllocator() { reset(); } +}; + +class DefaultAllocator { + BumpPointerAllocator Alloc; + +public: + void reset() { Alloc.reset(); } + + template<typename T, typename ...Args> T *makeNode(Args &&...args) { + return new (Alloc.allocate(sizeof(T))) + T(std::forward<Args>(args)...); + } + + void *allocateNodeArray(size_t sz) { + return Alloc.allocate(sizeof(Node *) * sz); + } +}; +} // unnamed namespace + +//===----------------------------------------------------------------------===// +// Code beyond this point should not be synchronized with libc++abi. +//===----------------------------------------------------------------------===// + +using Demangler = itanium_demangle::ManglingParser<DefaultAllocator>; + +char *llvm::itaniumDemangle(const char *MangledName, char *Buf, + size_t *N, int *Status) { + if (MangledName == nullptr || (Buf != nullptr && N == nullptr)) { + if (Status) + *Status = demangle_invalid_args; + return nullptr; + } + + int InternalStatus = demangle_success; + Demangler Parser(MangledName, MangledName + std::strlen(MangledName)); + Node *AST = Parser.parse(); + + if (AST == nullptr) + InternalStatus = demangle_invalid_mangled_name; + else { + OutputBuffer OB(Buf, N); + assert(Parser.ForwardTemplateRefs.empty()); + AST->print(OB); + OB += '\0'; + if (N != nullptr) + *N = OB.getCurrentPosition(); + Buf = OB.getBuffer(); + } + + if (Status) + *Status = InternalStatus; + return InternalStatus == demangle_success ? Buf : nullptr; +} + +ItaniumPartialDemangler::ItaniumPartialDemangler() + : RootNode(nullptr), Context(new Demangler{nullptr, nullptr}) {} + +ItaniumPartialDemangler::~ItaniumPartialDemangler() { + delete static_cast<Demangler *>(Context); +} + +ItaniumPartialDemangler::ItaniumPartialDemangler( + ItaniumPartialDemangler &&Other) + : RootNode(Other.RootNode), Context(Other.Context) { + Other.Context = Other.RootNode = nullptr; +} + +ItaniumPartialDemangler &ItaniumPartialDemangler:: +operator=(ItaniumPartialDemangler &&Other) { + std::swap(RootNode, Other.RootNode); + std::swap(Context, Other.Context); + return *this; +} + +// Demangle MangledName into an AST, storing it into this->RootNode. +bool ItaniumPartialDemangler::partialDemangle(const char *MangledName) { + Demangler *Parser = static_cast<Demangler *>(Context); + size_t Len = std::strlen(MangledName); + Parser->reset(MangledName, MangledName + Len); + RootNode = Parser->parse(); + return RootNode == nullptr; +} + +static char *printNode(const Node *RootNode, char *Buf, size_t *N) { + OutputBuffer OB(Buf, N); + RootNode->print(OB); + OB += '\0'; + if (N != nullptr) + *N = OB.getCurrentPosition(); + return OB.getBuffer(); +} + +char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const { + if (!isFunction()) + return nullptr; + + const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName(); + + while (true) { + switch (Name->getKind()) { + case Node::KAbiTagAttr: + Name = static_cast<const AbiTagAttr *>(Name)->Base; + continue; + case Node::KModuleEntity: + Name = static_cast<const ModuleEntity *>(Name)->Name; + continue; + case Node::KNestedName: + Name = static_cast<const NestedName *>(Name)->Name; + continue; + case Node::KLocalName: + Name = static_cast<const LocalName *>(Name)->Entity; + continue; + case Node::KNameWithTemplateArgs: + Name = static_cast<const NameWithTemplateArgs *>(Name)->Name; + continue; + default: + return printNode(Name, Buf, N); + } + } +} + +char *ItaniumPartialDemangler::getFunctionDeclContextName(char *Buf, + size_t *N) const { + if (!isFunction()) + return nullptr; + const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName(); + + OutputBuffer OB(Buf, N); + + KeepGoingLocalFunction: + while (true) { + if (Name->getKind() == Node::KAbiTagAttr) { + Name = static_cast<const AbiTagAttr *>(Name)->Base; + continue; + } + if (Name->getKind() == Node::KNameWithTemplateArgs) { + Name = static_cast<const NameWithTemplateArgs *>(Name)->Name; + continue; + } + break; + } + + if (Name->getKind() == Node::KModuleEntity) + Name = static_cast<const ModuleEntity *>(Name)->Name; + + switch (Name->getKind()) { + case Node::KNestedName: + static_cast<const NestedName *>(Name)->Qual->print(OB); + break; + case Node::KLocalName: { + auto *LN = static_cast<const LocalName *>(Name); + LN->Encoding->print(OB); + OB += "::"; + Name = LN->Entity; + goto KeepGoingLocalFunction; + } + default: + break; + } + OB += '\0'; + if (N != nullptr) + *N = OB.getCurrentPosition(); + return OB.getBuffer(); +} + +char *ItaniumPartialDemangler::getFunctionName(char *Buf, size_t *N) const { + if (!isFunction()) + return nullptr; + auto *Name = static_cast<FunctionEncoding *>(RootNode)->getName(); + return printNode(Name, Buf, N); +} + +char *ItaniumPartialDemangler::getFunctionParameters(char *Buf, + size_t *N) const { + if (!isFunction()) + return nullptr; + NodeArray Params = static_cast<FunctionEncoding *>(RootNode)->getParams(); + + OutputBuffer OB(Buf, N); + + OB += '('; + Params.printWithComma(OB); + OB += ')'; + OB += '\0'; + if (N != nullptr) + *N = OB.getCurrentPosition(); + return OB.getBuffer(); +} + +char *ItaniumPartialDemangler::getFunctionReturnType( + char *Buf, size_t *N) const { + if (!isFunction()) + return nullptr; + + OutputBuffer OB(Buf, N); + + if (const Node *Ret = + static_cast<const FunctionEncoding *>(RootNode)->getReturnType()) + Ret->print(OB); + + OB += '\0'; + if (N != nullptr) + *N = OB.getCurrentPosition(); + return OB.getBuffer(); +} + +char *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const { + assert(RootNode != nullptr && "must call partialDemangle()"); + return printNode(static_cast<Node *>(RootNode), Buf, N); +} + +bool ItaniumPartialDemangler::hasFunctionQualifiers() const { + assert(RootNode != nullptr && "must call partialDemangle()"); + if (!isFunction()) + return false; + auto *E = static_cast<const FunctionEncoding *>(RootNode); + return E->getCVQuals() != QualNone || E->getRefQual() != FrefQualNone; +} + +bool ItaniumPartialDemangler::isCtorOrDtor() const { + const Node *N = static_cast<const Node *>(RootNode); + while (N) { + switch (N->getKind()) { + default: + return false; + case Node::KCtorDtorName: + return true; + + case Node::KAbiTagAttr: + N = static_cast<const AbiTagAttr *>(N)->Base; + break; + case Node::KFunctionEncoding: + N = static_cast<const FunctionEncoding *>(N)->getName(); + break; + case Node::KLocalName: + N = static_cast<const LocalName *>(N)->Entity; + break; + case Node::KNameWithTemplateArgs: + N = static_cast<const NameWithTemplateArgs *>(N)->Name; + break; + case Node::KNestedName: + N = static_cast<const NestedName *>(N)->Name; + break; + case Node::KModuleEntity: + N = static_cast<const ModuleEntity *>(N)->Name; + break; + } + } + return false; +} + +bool ItaniumPartialDemangler::isFunction() const { + assert(RootNode != nullptr && "must call partialDemangle()"); + return static_cast<const Node *>(RootNode)->getKind() == + Node::KFunctionEncoding; +} + +bool ItaniumPartialDemangler::isSpecialName() const { + assert(RootNode != nullptr && "must call partialDemangle()"); + auto K = static_cast<const Node *>(RootNode)->getKind(); + return K == Node::KSpecialName || K == Node::KCtorVtableSpecialName; +} + +bool ItaniumPartialDemangler::isData() const { + return !isFunction() && !isSpecialName(); +} diff --git a/demangle/third_party/llvm/lib/Demangle/MicrosoftDemangle.cpp b/demangle/third_party/llvm/lib/Demangle/MicrosoftDemangle.cpp new file mode 100644 index 00000000..c21b0a30 --- /dev/null +++ b/demangle/third_party/llvm/lib/Demangle/MicrosoftDemangle.cpp @@ -0,0 +1,2368 @@ +//===- MicrosoftDemangle.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a demangler for MSVC-style mangled symbols. +// +// This file has no dependencies on the rest of LLVM so that it can be +// easily reused in other programs such as libcxxabi. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Demangle/MicrosoftDemangle.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/Demangle/MicrosoftDemangleNodes.h" + +#include "llvm/Demangle/DemangleConfig.h" +#include "llvm/Demangle/StringView.h" +#include "llvm/Demangle/Utility.h" + +#include <array> +#include <cctype> +#include <cstdio> +#include <tuple> + +using namespace llvm; +using namespace ms_demangle; + +static bool startsWithDigit(StringView S) { + return !S.empty() && std::isdigit(S.front()); +} + + +struct NodeList { + Node *N = nullptr; + NodeList *Next = nullptr; +}; + +static bool isMemberPointer(StringView MangledName, bool &Error) { + Error = false; + switch (MangledName.popFront()) { + case '$': + // This is probably an rvalue reference (e.g. $$Q), and you cannot have an + // rvalue reference to a member. + return false; + case 'A': + // 'A' indicates a reference, and you cannot have a reference to a member + // function or member. + return false; + case 'P': + case 'Q': + case 'R': + case 'S': + // These 4 values indicate some kind of pointer, but we still don't know + // what. + break; + default: + // isMemberPointer() is called only if isPointerType() returns true, + // and it rejects other prefixes. + DEMANGLE_UNREACHABLE; + } + + // If it starts with a number, then 6 indicates a non-member function + // pointer, and 8 indicates a member function pointer. + if (startsWithDigit(MangledName)) { + if (MangledName[0] != '6' && MangledName[0] != '8') { + Error = true; + return false; + } + return (MangledName[0] == '8'); + } + + // Remove ext qualifiers since those can appear on either type and are + // therefore not indicative. + MangledName.consumeFront('E'); // 64-bit + MangledName.consumeFront('I'); // restrict + MangledName.consumeFront('F'); // unaligned + + if (MangledName.empty()) { + Error = true; + return false; + } + + // The next value should be either ABCD (non-member) or QRST (member). + switch (MangledName.front()) { + case 'A': + case 'B': + case 'C': + case 'D': + return false; + case 'Q': + case 'R': + case 'S': + case 'T': + return true; + default: + Error = true; + return false; + } +} + +static SpecialIntrinsicKind +consumeSpecialIntrinsicKind(StringView &MangledName) { + if (MangledName.consumeFront("?_7")) + return SpecialIntrinsicKind::Vftable; + if (MangledName.consumeFront("?_8")) + return SpecialIntrinsicKind::Vbtable; + if (MangledName.consumeFront("?_9")) + return SpecialIntrinsicKind::VcallThunk; + if (MangledName.consumeFront("?_A")) + return SpecialIntrinsicKind::Typeof; + if (MangledName.consumeFront("?_B")) + return SpecialIntrinsicKind::LocalStaticGuard; + if (MangledName.consumeFront("?_C")) + return SpecialIntrinsicKind::StringLiteralSymbol; + if (MangledName.consumeFront("?_P")) + return SpecialIntrinsicKind::UdtReturning; + if (MangledName.consumeFront("?_R0")) + return SpecialIntrinsicKind::RttiTypeDescriptor; + if (MangledName.consumeFront("?_R1")) + return SpecialIntrinsicKind::RttiBaseClassDescriptor; + if (MangledName.consumeFront("?_R2")) + return SpecialIntrinsicKind::RttiBaseClassArray; + if (MangledName.consumeFront("?_R3")) + return SpecialIntrinsicKind::RttiClassHierarchyDescriptor; + if (MangledName.consumeFront("?_R4")) + return SpecialIntrinsicKind::RttiCompleteObjLocator; + if (MangledName.consumeFront("?_S")) + return SpecialIntrinsicKind::LocalVftable; + if (MangledName.consumeFront("?__E")) + return SpecialIntrinsicKind::DynamicInitializer; + if (MangledName.consumeFront("?__F")) + return SpecialIntrinsicKind::DynamicAtexitDestructor; + if (MangledName.consumeFront("?__J")) + return SpecialIntrinsicKind::LocalStaticThreadGuard; + return SpecialIntrinsicKind::None; +} + +static bool startsWithLocalScopePattern(StringView S) { + if (!S.consumeFront('?')) + return false; + + size_t End = S.find('?'); + if (End == StringView::npos) + return false; + StringView Candidate = S.substr(0, End); + if (Candidate.empty()) + return false; + + // \?[0-9]\? + // ?@? is the discriminator 0. + if (Candidate.size() == 1) + return Candidate[0] == '@' || (Candidate[0] >= '0' && Candidate[0] <= '9'); + + // If it's not 0-9, then it's an encoded number terminated with an @ + if (Candidate.back() != '@') + return false; + Candidate = Candidate.dropBack(); + + // An encoded number starts with B-P and all subsequent digits are in A-P. + // Note that the reason the first digit cannot be A is two fold. First, it + // would create an ambiguity with ?A which delimits the beginning of an + // anonymous namespace. Second, A represents 0, and you don't start a multi + // digit number with a leading 0. Presumably the anonymous namespace + // ambiguity is also why single digit encoded numbers use 0-9 rather than A-J. + if (Candidate[0] < 'B' || Candidate[0] > 'P') + return false; + Candidate = Candidate.dropFront(); + while (!Candidate.empty()) { + if (Candidate[0] < 'A' || Candidate[0] > 'P') + return false; + Candidate = Candidate.dropFront(); + } + + return true; +} + +static bool isTagType(StringView S) { + switch (S.front()) { + case 'T': // union + case 'U': // struct + case 'V': // class + case 'W': // enum + return true; + } + return false; +} + +static bool isCustomType(StringView S) { return S[0] == '?'; } + +static bool isPointerType(StringView S) { + if (S.startsWith("$$Q")) // foo && + return true; + + switch (S.front()) { + case 'A': // foo & + case 'P': // foo * + case 'Q': // foo *const + case 'R': // foo *volatile + case 'S': // foo *const volatile + return true; + } + return false; +} + +static bool isArrayType(StringView S) { return S[0] == 'Y'; } + +static bool isFunctionType(StringView S) { + return S.startsWith("$$A8@@") || S.startsWith("$$A6"); +} + +static FunctionRefQualifier +demangleFunctionRefQualifier(StringView &MangledName) { + if (MangledName.consumeFront('G')) + return FunctionRefQualifier::Reference; + else if (MangledName.consumeFront('H')) + return FunctionRefQualifier::RValueReference; + return FunctionRefQualifier::None; +} + +static std::pair<Qualifiers, PointerAffinity> +demanglePointerCVQualifiers(StringView &MangledName) { + if (MangledName.consumeFront("$$Q")) + return std::make_pair(Q_None, PointerAffinity::RValueReference); + + switch (MangledName.popFront()) { + case 'A': + return std::make_pair(Q_None, PointerAffinity::Reference); + case 'P': + return std::make_pair(Q_None, PointerAffinity::Pointer); + case 'Q': + return std::make_pair(Q_Const, PointerAffinity::Pointer); + case 'R': + return std::make_pair(Q_Volatile, PointerAffinity::Pointer); + case 'S': + return std::make_pair(Qualifiers(Q_Const | Q_Volatile), + PointerAffinity::Pointer); + } + // This function is only called if isPointerType() returns true, + // and it only returns true for the six cases listed above. + DEMANGLE_UNREACHABLE; +} + +StringView Demangler::copyString(StringView Borrowed) { + char *Stable = Arena.allocUnalignedBuffer(Borrowed.size()); + // This is not a micro-optimization, it avoids UB, should Borrowed be an null + // buffer. + if (Borrowed.size()) + std::memcpy(Stable, Borrowed.begin(), Borrowed.size()); + + return {Stable, Borrowed.size()}; +} + +SpecialTableSymbolNode * +Demangler::demangleSpecialTableSymbolNode(StringView &MangledName, + SpecialIntrinsicKind K) { + NamedIdentifierNode *NI = Arena.alloc<NamedIdentifierNode>(); + switch (K) { + case SpecialIntrinsicKind::Vftable: + NI->Name = "`vftable'"; + break; + case SpecialIntrinsicKind::Vbtable: + NI->Name = "`vbtable'"; + break; + case SpecialIntrinsicKind::LocalVftable: + NI->Name = "`local vftable'"; + break; + case SpecialIntrinsicKind::RttiCompleteObjLocator: + NI->Name = "`RTTI Complete Object Locator'"; + break; + default: + DEMANGLE_UNREACHABLE; + } + QualifiedNameNode *QN = demangleNameScopeChain(MangledName, NI); + SpecialTableSymbolNode *STSN = Arena.alloc<SpecialTableSymbolNode>(); + STSN->Name = QN; + bool IsMember = false; + if (MangledName.empty()) { + Error = true; + return nullptr; + } + char Front = MangledName.popFront(); + if (Front != '6' && Front != '7') { + Error = true; + return nullptr; + } + + std::tie(STSN->Quals, IsMember) = demangleQualifiers(MangledName); + if (!MangledName.consumeFront('@')) + STSN->TargetName = demangleFullyQualifiedTypeName(MangledName); + return STSN; +} + +LocalStaticGuardVariableNode * +Demangler::demangleLocalStaticGuard(StringView &MangledName, bool IsThread) { + LocalStaticGuardIdentifierNode *LSGI = + Arena.alloc<LocalStaticGuardIdentifierNode>(); + LSGI->IsThread = IsThread; + QualifiedNameNode *QN = demangleNameScopeChain(MangledName, LSGI); + LocalStaticGuardVariableNode *LSGVN = + Arena.alloc<LocalStaticGuardVariableNode>(); + LSGVN->Name = QN; + + if (MangledName.consumeFront("4IA")) + LSGVN->IsVisible = false; + else if (MangledName.consumeFront("5")) + LSGVN->IsVisible = true; + else { + Error = true; + return nullptr; + } + + if (!MangledName.empty()) + LSGI->ScopeIndex = demangleUnsigned(MangledName); + return LSGVN; +} + +static NamedIdentifierNode *synthesizeNamedIdentifier(ArenaAllocator &Arena, + StringView Name) { + NamedIdentifierNode *Id = Arena.alloc<NamedIdentifierNode>(); + Id->Name = Name; + return Id; +} + +static QualifiedNameNode *synthesizeQualifiedName(ArenaAllocator &Arena, + IdentifierNode *Identifier) { + QualifiedNameNode *QN = Arena.alloc<QualifiedNameNode>(); + QN->Components = Arena.alloc<NodeArrayNode>(); + QN->Components->Count = 1; + QN->Components->Nodes = Arena.allocArray<Node *>(1); + QN->Components->Nodes[0] = Identifier; + return QN; +} + +static QualifiedNameNode *synthesizeQualifiedName(ArenaAllocator &Arena, + StringView Name) { + NamedIdentifierNode *Id = synthesizeNamedIdentifier(Arena, Name); + return synthesizeQualifiedName(Arena, Id); +} + +static VariableSymbolNode *synthesizeVariable(ArenaAllocator &Arena, + TypeNode *Type, + StringView VariableName) { + VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>(); + VSN->Type = Type; + VSN->Name = synthesizeQualifiedName(Arena, VariableName); + return VSN; +} + +VariableSymbolNode *Demangler::demangleUntypedVariable( + ArenaAllocator &Arena, StringView &MangledName, StringView VariableName) { + NamedIdentifierNode *NI = synthesizeNamedIdentifier(Arena, VariableName); + QualifiedNameNode *QN = demangleNameScopeChain(MangledName, NI); + VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>(); + VSN->Name = QN; + if (MangledName.consumeFront("8")) + return VSN; + + Error = true; + return nullptr; +} + +VariableSymbolNode * +Demangler::demangleRttiBaseClassDescriptorNode(ArenaAllocator &Arena, + StringView &MangledName) { + RttiBaseClassDescriptorNode *RBCDN = + Arena.alloc<RttiBaseClassDescriptorNode>(); + RBCDN->NVOffset = demangleUnsigned(MangledName); + RBCDN->VBPtrOffset = demangleSigned(MangledName); + RBCDN->VBTableOffset = demangleUnsigned(MangledName); + RBCDN->Flags = demangleUnsigned(MangledName); + if (Error) + return nullptr; + + VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>(); + VSN->Name = demangleNameScopeChain(MangledName, RBCDN); + MangledName.consumeFront('8'); + return VSN; +} + +FunctionSymbolNode *Demangler::demangleInitFiniStub(StringView &MangledName, + bool IsDestructor) { + DynamicStructorIdentifierNode *DSIN = + Arena.alloc<DynamicStructorIdentifierNode>(); + DSIN->IsDestructor = IsDestructor; + + bool IsKnownStaticDataMember = false; + if (MangledName.consumeFront('?')) + IsKnownStaticDataMember = true; + + SymbolNode *Symbol = demangleDeclarator(MangledName); + if (Error) + return nullptr; + + FunctionSymbolNode *FSN = nullptr; + + if (Symbol->kind() == NodeKind::VariableSymbol) { + DSIN->Variable = static_cast<VariableSymbolNode *>(Symbol); + + // Older versions of clang mangled this type of symbol incorrectly. They + // would omit the leading ? and they would only emit a single @ at the end. + // The correct mangling is a leading ? and 2 trailing @ signs. Handle + // both cases. + int AtCount = IsKnownStaticDataMember ? 2 : 1; + for (int I = 0; I < AtCount; ++I) { + if (MangledName.consumeFront('@')) + continue; + Error = true; + return nullptr; + } + + FSN = demangleFunctionEncoding(MangledName); + if (FSN) + FSN->Name = synthesizeQualifiedName(Arena, DSIN); + } else { + if (IsKnownStaticDataMember) { + // This was supposed to be a static data member, but we got a function. + Error = true; + return nullptr; + } + + FSN = static_cast<FunctionSymbolNode *>(Symbol); + DSIN->Name = Symbol->Name; + FSN->Name = synthesizeQualifiedName(Arena, DSIN); + } + + return FSN; +} + +SymbolNode *Demangler::demangleSpecialIntrinsic(StringView &MangledName) { + SpecialIntrinsicKind SIK = consumeSpecialIntrinsicKind(MangledName); + + switch (SIK) { + case SpecialIntrinsicKind::None: + return nullptr; + case SpecialIntrinsicKind::StringLiteralSymbol: + return demangleStringLiteral(MangledName); + case SpecialIntrinsicKind::Vftable: + case SpecialIntrinsicKind::Vbtable: + case SpecialIntrinsicKind::LocalVftable: + case SpecialIntrinsicKind::RttiCompleteObjLocator: + return demangleSpecialTableSymbolNode(MangledName, SIK); + case SpecialIntrinsicKind::VcallThunk: + return demangleVcallThunkNode(MangledName); + case SpecialIntrinsicKind::LocalStaticGuard: + return demangleLocalStaticGuard(MangledName, /*IsThread=*/false); + case SpecialIntrinsicKind::LocalStaticThreadGuard: + return demangleLocalStaticGuard(MangledName, /*IsThread=*/true); + case SpecialIntrinsicKind::RttiTypeDescriptor: { + TypeNode *T = demangleType(MangledName, QualifierMangleMode::Result); + if (Error) + break; + if (!MangledName.consumeFront("@8")) + break; + if (!MangledName.empty()) + break; + return synthesizeVariable(Arena, T, "`RTTI Type Descriptor'"); + } + case SpecialIntrinsicKind::RttiBaseClassArray: + return demangleUntypedVariable(Arena, MangledName, + "`RTTI Base Class Array'"); + case SpecialIntrinsicKind::RttiClassHierarchyDescriptor: + return demangleUntypedVariable(Arena, MangledName, + "`RTTI Class Hierarchy Descriptor'"); + case SpecialIntrinsicKind::RttiBaseClassDescriptor: + return demangleRttiBaseClassDescriptorNode(Arena, MangledName); + case SpecialIntrinsicKind::DynamicInitializer: + return demangleInitFiniStub(MangledName, /*IsDestructor=*/false); + case SpecialIntrinsicKind::DynamicAtexitDestructor: + return demangleInitFiniStub(MangledName, /*IsDestructor=*/true); + case SpecialIntrinsicKind::Typeof: + case SpecialIntrinsicKind::UdtReturning: + // It's unclear which tools produces these manglings, so demangling + // support is not (yet?) implemented. + break; + case SpecialIntrinsicKind::Unknown: + DEMANGLE_UNREACHABLE; // Never returned by consumeSpecialIntrinsicKind. + } + Error = true; + return nullptr; +} + +IdentifierNode * +Demangler::demangleFunctionIdentifierCode(StringView &MangledName) { + assert(MangledName.startsWith('?')); + MangledName = MangledName.dropFront(); + if (MangledName.empty()) { + Error = true; + return nullptr; + } + + if (MangledName.consumeFront("__")) + return demangleFunctionIdentifierCode( + MangledName, FunctionIdentifierCodeGroup::DoubleUnder); + if (MangledName.consumeFront("_")) + return demangleFunctionIdentifierCode(MangledName, + FunctionIdentifierCodeGroup::Under); + return demangleFunctionIdentifierCode(MangledName, + FunctionIdentifierCodeGroup::Basic); +} + +StructorIdentifierNode * +Demangler::demangleStructorIdentifier(StringView &MangledName, + bool IsDestructor) { + StructorIdentifierNode *N = Arena.alloc<StructorIdentifierNode>(); + N->IsDestructor = IsDestructor; + return N; +} + +ConversionOperatorIdentifierNode * +Demangler::demangleConversionOperatorIdentifier(StringView &MangledName) { + ConversionOperatorIdentifierNode *N = + Arena.alloc<ConversionOperatorIdentifierNode>(); + return N; +} + +LiteralOperatorIdentifierNode * +Demangler::demangleLiteralOperatorIdentifier(StringView &MangledName) { + LiteralOperatorIdentifierNode *N = + Arena.alloc<LiteralOperatorIdentifierNode>(); + N->Name = demangleSimpleString(MangledName, /*Memorize=*/false); + return N; +} + +IntrinsicFunctionKind +Demangler::translateIntrinsicFunctionCode(char CH, + FunctionIdentifierCodeGroup Group) { + using IFK = IntrinsicFunctionKind; + if (!(CH >= '0' && CH <= '9') && !(CH >= 'A' && CH <= 'Z')) { + Error = true; + return IFK::None; + } + + // Not all ? identifiers are intrinsics *functions*. This function only maps + // operator codes for the special functions, all others are handled elsewhere, + // hence the IFK::None entries in the table. + static IFK Basic[36] = { + IFK::None, // ?0 # Foo::Foo() + IFK::None, // ?1 # Foo::~Foo() + IFK::New, // ?2 # operator new + IFK::Delete, // ?3 # operator delete + IFK::Assign, // ?4 # operator= + IFK::RightShift, // ?5 # operator>> + IFK::LeftShift, // ?6 # operator<< + IFK::LogicalNot, // ?7 # operator! + IFK::Equals, // ?8 # operator== + IFK::NotEquals, // ?9 # operator!= + IFK::ArraySubscript, // ?A # operator[] + IFK::None, // ?B # Foo::operator <type>() + IFK::Pointer, // ?C # operator-> + IFK::Dereference, // ?D # operator* + IFK::Increment, // ?E # operator++ + IFK::Decrement, // ?F # operator-- + IFK::Minus, // ?G # operator- + IFK::Plus, // ?H # operator+ + IFK::BitwiseAnd, // ?I # operator& + IFK::MemberPointer, // ?J # operator->* + IFK::Divide, // ?K # operator/ + IFK::Modulus, // ?L # operator% + IFK::LessThan, // ?M operator< + IFK::LessThanEqual, // ?N operator<= + IFK::GreaterThan, // ?O operator> + IFK::GreaterThanEqual, // ?P operator>= + IFK::Comma, // ?Q operator, + IFK::Parens, // ?R operator() + IFK::BitwiseNot, // ?S operator~ + IFK::BitwiseXor, // ?T operator^ + IFK::BitwiseOr, // ?U operator| + IFK::LogicalAnd, // ?V operator&& + IFK::LogicalOr, // ?W operator|| + IFK::TimesEqual, // ?X operator*= + IFK::PlusEqual, // ?Y operator+= + IFK::MinusEqual, // ?Z operator-= + }; + static IFK Under[36] = { + IFK::DivEqual, // ?_0 operator/= + IFK::ModEqual, // ?_1 operator%= + IFK::RshEqual, // ?_2 operator>>= + IFK::LshEqual, // ?_3 operator<<= + IFK::BitwiseAndEqual, // ?_4 operator&= + IFK::BitwiseOrEqual, // ?_5 operator|= + IFK::BitwiseXorEqual, // ?_6 operator^= + IFK::None, // ?_7 # vftable + IFK::None, // ?_8 # vbtable + IFK::None, // ?_9 # vcall + IFK::None, // ?_A # typeof + IFK::None, // ?_B # local static guard + IFK::None, // ?_C # string literal + IFK::VbaseDtor, // ?_D # vbase destructor + IFK::VecDelDtor, // ?_E # vector deleting destructor + IFK::DefaultCtorClosure, // ?_F # default constructor closure + IFK::ScalarDelDtor, // ?_G # scalar deleting destructor + IFK::VecCtorIter, // ?_H # vector constructor iterator + IFK::VecDtorIter, // ?_I # vector destructor iterator + IFK::VecVbaseCtorIter, // ?_J # vector vbase constructor iterator + IFK::VdispMap, // ?_K # virtual displacement map + IFK::EHVecCtorIter, // ?_L # eh vector constructor iterator + IFK::EHVecDtorIter, // ?_M # eh vector destructor iterator + IFK::EHVecVbaseCtorIter, // ?_N # eh vector vbase constructor iterator + IFK::CopyCtorClosure, // ?_O # copy constructor closure + IFK::None, // ?_P<name> # udt returning <name> + IFK::None, // ?_Q # <unknown> + IFK::None, // ?_R0 - ?_R4 # RTTI Codes + IFK::None, // ?_S # local vftable + IFK::LocalVftableCtorClosure, // ?_T # local vftable constructor closure + IFK::ArrayNew, // ?_U operator new[] + IFK::ArrayDelete, // ?_V operator delete[] + IFK::None, // ?_W <unused> + IFK::None, // ?_X <unused> + IFK::None, // ?_Y <unused> + IFK::None, // ?_Z <unused> + }; + static IFK DoubleUnder[36] = { + IFK::None, // ?__0 <unused> + IFK::None, // ?__1 <unused> + IFK::None, // ?__2 <unused> + IFK::None, // ?__3 <unused> + IFK::None, // ?__4 <unused> + IFK::None, // ?__5 <unused> + IFK::None, // ?__6 <unused> + IFK::None, // ?__7 <unused> + IFK::None, // ?__8 <unused> + IFK::None, // ?__9 <unused> + IFK::ManVectorCtorIter, // ?__A managed vector ctor iterator + IFK::ManVectorDtorIter, // ?__B managed vector dtor iterator + IFK::EHVectorCopyCtorIter, // ?__C EH vector copy ctor iterator + IFK::EHVectorVbaseCopyCtorIter, // ?__D EH vector vbase copy ctor iter + IFK::None, // ?__E dynamic initializer for `T' + IFK::None, // ?__F dynamic atexit destructor for `T' + IFK::VectorCopyCtorIter, // ?__G vector copy constructor iter + IFK::VectorVbaseCopyCtorIter, // ?__H vector vbase copy ctor iter + IFK::ManVectorVbaseCopyCtorIter, // ?__I managed vector vbase copy ctor + // iter + IFK::None, // ?__J local static thread guard + IFK::None, // ?__K operator ""_name + IFK::CoAwait, // ?__L operator co_await + IFK::Spaceship, // ?__M operator<=> + IFK::None, // ?__N <unused> + IFK::None, // ?__O <unused> + IFK::None, // ?__P <unused> + IFK::None, // ?__Q <unused> + IFK::None, // ?__R <unused> + IFK::None, // ?__S <unused> + IFK::None, // ?__T <unused> + IFK::None, // ?__U <unused> + IFK::None, // ?__V <unused> + IFK::None, // ?__W <unused> + IFK::None, // ?__X <unused> + IFK::None, // ?__Y <unused> + IFK::None, // ?__Z <unused> + }; + + int Index = (CH >= '0' && CH <= '9') ? (CH - '0') : (CH - 'A' + 10); + switch (Group) { + case FunctionIdentifierCodeGroup::Basic: + return Basic[Index]; + case FunctionIdentifierCodeGroup::Under: + return Under[Index]; + case FunctionIdentifierCodeGroup::DoubleUnder: + return DoubleUnder[Index]; + } + DEMANGLE_UNREACHABLE; +} + +IdentifierNode * +Demangler::demangleFunctionIdentifierCode(StringView &MangledName, + FunctionIdentifierCodeGroup Group) { + if (MangledName.empty()) { + Error = true; + return nullptr; + } + switch (Group) { + case FunctionIdentifierCodeGroup::Basic: + switch (char CH = MangledName.popFront()) { + case '0': + case '1': + return demangleStructorIdentifier(MangledName, CH == '1'); + case 'B': + return demangleConversionOperatorIdentifier(MangledName); + default: + return Arena.alloc<IntrinsicFunctionIdentifierNode>( + translateIntrinsicFunctionCode(CH, Group)); + } + case FunctionIdentifierCodeGroup::Under: + return Arena.alloc<IntrinsicFunctionIdentifierNode>( + translateIntrinsicFunctionCode(MangledName.popFront(), Group)); + case FunctionIdentifierCodeGroup::DoubleUnder: + switch (char CH = MangledName.popFront()) { + case 'K': + return demangleLiteralOperatorIdentifier(MangledName); + default: + return Arena.alloc<IntrinsicFunctionIdentifierNode>( + translateIntrinsicFunctionCode(CH, Group)); + } + } + + DEMANGLE_UNREACHABLE; +} + +SymbolNode *Demangler::demangleEncodedSymbol(StringView &MangledName, + QualifiedNameNode *Name) { + if (MangledName.empty()) { + Error = true; + return nullptr; + } + + // Read a variable. + switch (MangledName.front()) { + case '0': + case '1': + case '2': + case '3': + case '4': { + StorageClass SC = demangleVariableStorageClass(MangledName); + return demangleVariableEncoding(MangledName, SC); + } + } + FunctionSymbolNode *FSN = demangleFunctionEncoding(MangledName); + + IdentifierNode *UQN = Name->getUnqualifiedIdentifier(); + if (UQN->kind() == NodeKind::ConversionOperatorIdentifier) { + ConversionOperatorIdentifierNode *COIN = + static_cast<ConversionOperatorIdentifierNode *>(UQN); + if (FSN) + COIN->TargetType = FSN->Signature->ReturnType; + } + return FSN; +} + +SymbolNode *Demangler::demangleDeclarator(StringView &MangledName) { + // What follows is a main symbol name. This may include namespaces or class + // back references. + QualifiedNameNode *QN = demangleFullyQualifiedSymbolName(MangledName); + if (Error) + return nullptr; + + SymbolNode *Symbol = demangleEncodedSymbol(MangledName, QN); + if (Error) + return nullptr; + Symbol->Name = QN; + + IdentifierNode *UQN = QN->getUnqualifiedIdentifier(); + if (UQN->kind() == NodeKind::ConversionOperatorIdentifier) { + ConversionOperatorIdentifierNode *COIN = + static_cast<ConversionOperatorIdentifierNode *>(UQN); + if (!COIN->TargetType) { + Error = true; + return nullptr; + } + } + return Symbol; +} + +SymbolNode *Demangler::demangleMD5Name(StringView &MangledName) { + assert(MangledName.startsWith("??@")); + // This is an MD5 mangled name. We can't demangle it, just return the + // mangled name. + // An MD5 mangled name is ??@ followed by 32 characters and a terminating @. + size_t MD5Last = MangledName.find('@', strlen("??@")); + if (MD5Last == StringView::npos) { + Error = true; + return nullptr; + } + const char *Start = MangledName.begin(); + MangledName = MangledName.dropFront(MD5Last + 1); + + // There are two additional special cases for MD5 names: + // 1. For complete object locators where the object name is long enough + // for the object to have an MD5 name, the complete object locator is + // called ??@...@??_R4@ (with a trailing "??_R4@" instead of the usual + // leading "??_R4". This is handled here. + // 2. For catchable types, in versions of MSVC before 2015 (<1900) or after + // 2017.2 (>= 1914), the catchable type mangling is _CT??@...@??@...@8 + // instead of_CT??@...@8 with just one MD5 name. Since we don't yet + // demangle catchable types anywhere, this isn't handled for MD5 names + // either. + MangledName.consumeFront("??_R4@"); + + StringView MD5(Start, MangledName.begin()); + SymbolNode *S = Arena.alloc<SymbolNode>(NodeKind::Md5Symbol); + S->Name = synthesizeQualifiedName(Arena, MD5); + + return S; +} + +SymbolNode *Demangler::demangleTypeinfoName(StringView &MangledName) { + assert(MangledName.startsWith('.')); + MangledName.consumeFront('.'); + + TypeNode *T = demangleType(MangledName, QualifierMangleMode::Result); + if (Error || !MangledName.empty()) { + Error = true; + return nullptr; + } + return synthesizeVariable(Arena, T, "`RTTI Type Descriptor Name'"); +} + +// Parser entry point. +SymbolNode *Demangler::parse(StringView &MangledName) { + // Typeinfo names are strings stored in RTTI data. They're not symbol names. + // It's still useful to demangle them. They're the only demangled entity + // that doesn't start with a "?" but a ".". + if (MangledName.startsWith('.')) + return demangleTypeinfoName(MangledName); + + if (MangledName.startsWith("??@")) + return demangleMD5Name(MangledName); + + // MSVC-style mangled symbols must start with '?'. + if (!MangledName.startsWith('?')) { + Error = true; + return nullptr; + } + + MangledName.consumeFront('?'); + + // ?$ is a template instantiation, but all other names that start with ? are + // operators / special names. + if (SymbolNode *SI = demangleSpecialIntrinsic(MangledName)) + return SI; + + return demangleDeclarator(MangledName); +} + +TagTypeNode *Demangler::parseTagUniqueName(StringView &MangledName) { + if (!MangledName.consumeFront(".?A")) { + Error = true; + return nullptr; + } + MangledName.consumeFront(".?A"); + if (MangledName.empty()) { + Error = true; + return nullptr; + } + + return demangleClassType(MangledName); +} + +// <type-encoding> ::= <storage-class> <variable-type> +// <storage-class> ::= 0 # private static member +// ::= 1 # protected static member +// ::= 2 # public static member +// ::= 3 # global +// ::= 4 # static local + +VariableSymbolNode *Demangler::demangleVariableEncoding(StringView &MangledName, + StorageClass SC) { + VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>(); + + VSN->Type = demangleType(MangledName, QualifierMangleMode::Drop); + VSN->SC = SC; + + if (Error) + return nullptr; + + // <variable-type> ::= <type> <cvr-qualifiers> + // ::= <type> <pointee-cvr-qualifiers> # pointers, references + switch (VSN->Type->kind()) { + case NodeKind::PointerType: { + PointerTypeNode *PTN = static_cast<PointerTypeNode *>(VSN->Type); + + Qualifiers ExtraChildQuals = Q_None; + PTN->Quals = Qualifiers(VSN->Type->Quals | + demanglePointerExtQualifiers(MangledName)); + + bool IsMember = false; + std::tie(ExtraChildQuals, IsMember) = demangleQualifiers(MangledName); + + if (PTN->ClassParent) { + QualifiedNameNode *BackRefName = + demangleFullyQualifiedTypeName(MangledName); + (void)BackRefName; + } + PTN->Pointee->Quals = Qualifiers(PTN->Pointee->Quals | ExtraChildQuals); + + break; + } + default: + VSN->Type->Quals = demangleQualifiers(MangledName).first; + break; + } + + return VSN; +} + +// Sometimes numbers are encoded in mangled symbols. For example, +// "int (*x)[20]" is a valid C type (x is a pointer to an array of +// length 20), so we need some way to embed numbers as part of symbols. +// This function parses it. +// +// <number> ::= [?] <non-negative integer> +// +// <non-negative integer> ::= <decimal digit> # when 1 <= Number <= 10 +// ::= <hex digit>+ @ # when Number == 0 or >= 10 +// +// <hex-digit> ::= [A-P] # A = 0, B = 1, ... +std::pair<uint64_t, bool> Demangler::demangleNumber(StringView &MangledName) { + bool IsNegative = MangledName.consumeFront('?'); + + if (startsWithDigit(MangledName)) { + uint64_t Ret = MangledName[0] - '0' + 1; + MangledName = MangledName.dropFront(1); + return {Ret, IsNegative}; + } + + uint64_t Ret = 0; + for (size_t i = 0; i < MangledName.size(); ++i) { + char C = MangledName[i]; + if (C == '@') { + MangledName = MangledName.dropFront(i + 1); + return {Ret, IsNegative}; + } + if ('A' <= C && C <= 'P') { + Ret = (Ret << 4) + (C - 'A'); + continue; + } + break; + } + + Error = true; + return {0ULL, false}; +} + +uint64_t Demangler::demangleUnsigned(StringView &MangledName) { + bool IsNegative = false; + uint64_t Number = 0; + std::tie(Number, IsNegative) = demangleNumber(MangledName); + if (IsNegative) + Error = true; + return Number; +} + +int64_t Demangler::demangleSigned(StringView &MangledName) { + bool IsNegative = false; + uint64_t Number = 0; + std::tie(Number, IsNegative) = demangleNumber(MangledName); + if (Number > INT64_MAX) + Error = true; + int64_t I = static_cast<int64_t>(Number); + return IsNegative ? -I : I; +} + +// First 10 strings can be referenced by special BackReferences ?0, ?1, ..., ?9. +// Memorize it. +void Demangler::memorizeString(StringView S) { + if (Backrefs.NamesCount >= BackrefContext::Max) + return; + for (size_t i = 0; i < Backrefs.NamesCount; ++i) + if (S == Backrefs.Names[i]->Name) + return; + NamedIdentifierNode *N = Arena.alloc<NamedIdentifierNode>(); + N->Name = S; + Backrefs.Names[Backrefs.NamesCount++] = N; +} + +NamedIdentifierNode *Demangler::demangleBackRefName(StringView &MangledName) { + assert(startsWithDigit(MangledName)); + + size_t I = MangledName[0] - '0'; + if (I >= Backrefs.NamesCount) { + Error = true; + return nullptr; + } + + MangledName = MangledName.dropFront(); + return Backrefs.Names[I]; +} + +void Demangler::memorizeIdentifier(IdentifierNode *Identifier) { + // Render this class template name into a string buffer so that we can + // memorize it for the purpose of back-referencing. + OutputBuffer OB; + Identifier->output(OB, OF_Default); + StringView Owned = copyString(OB); + memorizeString(Owned); + std::free(OB.getBuffer()); +} + +IdentifierNode * +Demangler::demangleTemplateInstantiationName(StringView &MangledName, + NameBackrefBehavior NBB) { + assert(MangledName.startsWith("?$")); + MangledName.consumeFront("?$"); + + BackrefContext OuterContext; + std::swap(OuterContext, Backrefs); + + IdentifierNode *Identifier = + demangleUnqualifiedSymbolName(MangledName, NBB_Simple); + if (!Error) + Identifier->TemplateParams = demangleTemplateParameterList(MangledName); + + std::swap(OuterContext, Backrefs); + if (Error) + return nullptr; + + if (NBB & NBB_Template) { + // NBB_Template is only set for types and non-leaf names ("a::" in "a::b"). + // Structors and conversion operators only makes sense in a leaf name, so + // reject them in NBB_Template contexts. + if (Identifier->kind() == NodeKind::ConversionOperatorIdentifier || + Identifier->kind() == NodeKind::StructorIdentifier) { + Error = true; + return nullptr; + } + + memorizeIdentifier(Identifier); + } + + return Identifier; +} + +NamedIdentifierNode *Demangler::demangleSimpleName(StringView &MangledName, + bool Memorize) { + StringView S = demangleSimpleString(MangledName, Memorize); + if (Error) + return nullptr; + + NamedIdentifierNode *Name = Arena.alloc<NamedIdentifierNode>(); + Name->Name = S; + return Name; +} + +static bool isRebasedHexDigit(char C) { return (C >= 'A' && C <= 'P'); } + +static uint8_t rebasedHexDigitToNumber(char C) { + assert(isRebasedHexDigit(C)); + return (C <= 'J') ? (C - 'A') : (10 + C - 'K'); +} + +uint8_t Demangler::demangleCharLiteral(StringView &MangledName) { + assert(!MangledName.empty()); + if (!MangledName.startsWith('?')) + return MangledName.popFront(); + + MangledName = MangledName.dropFront(); + if (MangledName.empty()) + goto CharLiteralError; + + if (MangledName.consumeFront('$')) { + // Two hex digits + if (MangledName.size() < 2) + goto CharLiteralError; + StringView Nibbles = MangledName.substr(0, 2); + if (!isRebasedHexDigit(Nibbles[0]) || !isRebasedHexDigit(Nibbles[1])) + goto CharLiteralError; + // Don't append the null terminator. + uint8_t C1 = rebasedHexDigitToNumber(Nibbles[0]); + uint8_t C2 = rebasedHexDigitToNumber(Nibbles[1]); + MangledName = MangledName.dropFront(2); + return (C1 << 4) | C2; + } + + if (startsWithDigit(MangledName)) { + const char *Lookup = ",/\\:. \n\t'-"; + char C = Lookup[MangledName[0] - '0']; + MangledName = MangledName.dropFront(); + return C; + } + + if (MangledName[0] >= 'a' && MangledName[0] <= 'z') { + char Lookup[26] = {'\xE1', '\xE2', '\xE3', '\xE4', '\xE5', '\xE6', '\xE7', + '\xE8', '\xE9', '\xEA', '\xEB', '\xEC', '\xED', '\xEE', + '\xEF', '\xF0', '\xF1', '\xF2', '\xF3', '\xF4', '\xF5', + '\xF6', '\xF7', '\xF8', '\xF9', '\xFA'}; + char C = Lookup[MangledName[0] - 'a']; + MangledName = MangledName.dropFront(); + return C; + } + + if (MangledName[0] >= 'A' && MangledName[0] <= 'Z') { + char Lookup[26] = {'\xC1', '\xC2', '\xC3', '\xC4', '\xC5', '\xC6', '\xC7', + '\xC8', '\xC9', '\xCA', '\xCB', '\xCC', '\xCD', '\xCE', + '\xCF', '\xD0', '\xD1', '\xD2', '\xD3', '\xD4', '\xD5', + '\xD6', '\xD7', '\xD8', '\xD9', '\xDA'}; + char C = Lookup[MangledName[0] - 'A']; + MangledName = MangledName.dropFront(); + return C; + } + +CharLiteralError: + Error = true; + return '\0'; +} + +wchar_t Demangler::demangleWcharLiteral(StringView &MangledName) { + uint8_t C1, C2; + + C1 = demangleCharLiteral(MangledName); + if (Error || MangledName.empty()) + goto WCharLiteralError; + C2 = demangleCharLiteral(MangledName); + if (Error) + goto WCharLiteralError; + + return ((wchar_t)C1 << 8) | (wchar_t)C2; + +WCharLiteralError: + Error = true; + return L'\0'; +} + +static void writeHexDigit(char *Buffer, uint8_t Digit) { + assert(Digit <= 15); + *Buffer = (Digit < 10) ? ('0' + Digit) : ('A' + Digit - 10); +} + +static void outputHex(OutputBuffer &OB, unsigned C) { + assert (C != 0); + + // It's easier to do the math if we can work from right to left, but we need + // to print the numbers from left to right. So render this into a temporary + // buffer first, then output the temporary buffer. Each byte is of the form + // \xAB, which means that each byte needs 4 characters. Since there are at + // most 4 bytes, we need a 4*4+1 = 17 character temporary buffer. + char TempBuffer[17]; + + ::memset(TempBuffer, 0, sizeof(TempBuffer)); + constexpr int MaxPos = sizeof(TempBuffer) - 1; + + int Pos = MaxPos - 1; // TempBuffer[MaxPos] is the terminating \0. + while (C != 0) { + for (int I = 0; I < 2; ++I) { + writeHexDigit(&TempBuffer[Pos--], C % 16); + C /= 16; + } + } + TempBuffer[Pos--] = 'x'; + assert(Pos >= 0); + TempBuffer[Pos--] = '\\'; + OB << StringView(&TempBuffer[Pos + 1]); +} + +static void outputEscapedChar(OutputBuffer &OB, unsigned C) { + switch (C) { + case '\0': // nul + OB << "\\0"; + return; + case '\'': // single quote + OB << "\\\'"; + return; + case '\"': // double quote + OB << "\\\""; + return; + case '\\': // backslash + OB << "\\\\"; + return; + case '\a': // bell + OB << "\\a"; + return; + case '\b': // backspace + OB << "\\b"; + return; + case '\f': // form feed + OB << "\\f"; + return; + case '\n': // new line + OB << "\\n"; + return; + case '\r': // carriage return + OB << "\\r"; + return; + case '\t': // tab + OB << "\\t"; + return; + case '\v': // vertical tab + OB << "\\v"; + return; + default: + break; + } + + if (C > 0x1F && C < 0x7F) { + // Standard ascii char. + OB << (char)C; + return; + } + + outputHex(OB, C); +} + +static unsigned countTrailingNullBytes(const uint8_t *StringBytes, int Length) { + const uint8_t *End = StringBytes + Length - 1; + unsigned Count = 0; + while (Length > 0 && *End == 0) { + --Length; + --End; + ++Count; + } + return Count; +} + +static unsigned countEmbeddedNulls(const uint8_t *StringBytes, + unsigned Length) { + unsigned Result = 0; + for (unsigned I = 0; I < Length; ++I) { + if (*StringBytes++ == 0) + ++Result; + } + return Result; +} + +// A mangled (non-wide) string literal stores the total length of the string it +// refers to (passed in NumBytes), and it contains up to 32 bytes of actual text +// (passed in StringBytes, NumChars). +static unsigned guessCharByteSize(const uint8_t *StringBytes, unsigned NumChars, + uint64_t NumBytes) { + assert(NumBytes > 0); + + // If the number of bytes is odd, this is guaranteed to be a char string. + if (NumBytes % 2 == 1) + return 1; + + // All strings can encode at most 32 bytes of data. If it's less than that, + // then we encoded the entire string. In this case we check for a 1-byte, + // 2-byte, or 4-byte null terminator. + if (NumBytes < 32) { + unsigned TrailingNulls = countTrailingNullBytes(StringBytes, NumChars); + if (TrailingNulls >= 4 && NumBytes % 4 == 0) + return 4; + if (TrailingNulls >= 2) + return 2; + return 1; + } + + // The whole string was not able to be encoded. Try to look at embedded null + // terminators to guess. The heuristic is that we count all embedded null + // terminators. If more than 2/3 are null, it's a char32. If more than 1/3 + // are null, it's a char16. Otherwise it's a char8. This obviously isn't + // perfect and is biased towards languages that have ascii alphabets, but this + // was always going to be best effort since the encoding is lossy. + unsigned Nulls = countEmbeddedNulls(StringBytes, NumChars); + if (Nulls >= 2 * NumChars / 3 && NumBytes % 4 == 0) + return 4; + if (Nulls >= NumChars / 3) + return 2; + return 1; +} + +static unsigned decodeMultiByteChar(const uint8_t *StringBytes, + unsigned CharIndex, unsigned CharBytes) { + assert(CharBytes == 1 || CharBytes == 2 || CharBytes == 4); + unsigned Offset = CharIndex * CharBytes; + unsigned Result = 0; + StringBytes = StringBytes + Offset; + for (unsigned I = 0; I < CharBytes; ++I) { + unsigned C = static_cast<unsigned>(StringBytes[I]); + Result |= C << (8 * I); + } + return Result; +} + +FunctionSymbolNode *Demangler::demangleVcallThunkNode(StringView &MangledName) { + FunctionSymbolNode *FSN = Arena.alloc<FunctionSymbolNode>(); + VcallThunkIdentifierNode *VTIN = Arena.alloc<VcallThunkIdentifierNode>(); + FSN->Signature = Arena.alloc<ThunkSignatureNode>(); + FSN->Signature->FunctionClass = FC_NoParameterList; + + FSN->Name = demangleNameScopeChain(MangledName, VTIN); + if (!Error) + Error = !MangledName.consumeFront("$B"); + if (!Error) + VTIN->OffsetInVTable = demangleUnsigned(MangledName); + if (!Error) + Error = !MangledName.consumeFront('A'); + if (!Error) + FSN->Signature->CallConvention = demangleCallingConvention(MangledName); + return (Error) ? nullptr : FSN; +} + +EncodedStringLiteralNode * +Demangler::demangleStringLiteral(StringView &MangledName) { + // This function uses goto, so declare all variables up front. + OutputBuffer OB; + StringView CRC; + uint64_t StringByteSize; + bool IsWcharT = false; + bool IsNegative = false; + size_t CrcEndPos = 0; + + EncodedStringLiteralNode *Result = Arena.alloc<EncodedStringLiteralNode>(); + + // Prefix indicating the beginning of a string literal + if (!MangledName.consumeFront("@_")) + goto StringLiteralError; + if (MangledName.empty()) + goto StringLiteralError; + + // Char Type (regular or wchar_t) + switch (MangledName.popFront()) { + case '1': + IsWcharT = true; + DEMANGLE_FALLTHROUGH; + case '0': + break; + default: + goto StringLiteralError; + } + + // Encoded Length + std::tie(StringByteSize, IsNegative) = demangleNumber(MangledName); + if (Error || IsNegative || StringByteSize < (IsWcharT ? 2 : 1)) + goto StringLiteralError; + + // CRC 32 (always 8 characters plus a terminator) + CrcEndPos = MangledName.find('@'); + if (CrcEndPos == StringView::npos) + goto StringLiteralError; + CRC = MangledName.substr(0, CrcEndPos); + MangledName = MangledName.dropFront(CrcEndPos + 1); + if (MangledName.empty()) + goto StringLiteralError; + + if (IsWcharT) { + Result->Char = CharKind::Wchar; + if (StringByteSize > 64) + Result->IsTruncated = true; + + while (!MangledName.consumeFront('@')) { + if (MangledName.size() < 2) + goto StringLiteralError; + wchar_t W = demangleWcharLiteral(MangledName); + if (StringByteSize != 2 || Result->IsTruncated) + outputEscapedChar(OB, W); + StringByteSize -= 2; + if (Error) + goto StringLiteralError; + } + } else { + // The max byte length is actually 32, but some compilers mangled strings + // incorrectly, so we have to assume it can go higher. + constexpr unsigned MaxStringByteLength = 32 * 4; + uint8_t StringBytes[MaxStringByteLength]; + + unsigned BytesDecoded = 0; + while (!MangledName.consumeFront('@')) { + if (MangledName.size() < 1 || BytesDecoded >= MaxStringByteLength) + goto StringLiteralError; + StringBytes[BytesDecoded++] = demangleCharLiteral(MangledName); + } + + if (StringByteSize > BytesDecoded) + Result->IsTruncated = true; + + unsigned CharBytes = + guessCharByteSize(StringBytes, BytesDecoded, StringByteSize); + assert(StringByteSize % CharBytes == 0); + switch (CharBytes) { + case 1: + Result->Char = CharKind::Char; + break; + case 2: + Result->Char = CharKind::Char16; + break; + case 4: + Result->Char = CharKind::Char32; + break; + default: + DEMANGLE_UNREACHABLE; + } + const unsigned NumChars = BytesDecoded / CharBytes; + for (unsigned CharIndex = 0; CharIndex < NumChars; ++CharIndex) { + unsigned NextChar = + decodeMultiByteChar(StringBytes, CharIndex, CharBytes); + if (CharIndex + 1 < NumChars || Result->IsTruncated) + outputEscapedChar(OB, NextChar); + } + } + + Result->DecodedString = copyString(OB); + std::free(OB.getBuffer()); + return Result; + +StringLiteralError: + Error = true; + std::free(OB.getBuffer()); + return nullptr; +} + +// Returns MangledName's prefix before the first '@', or an error if +// MangledName contains no '@' or the prefix has length 0. +StringView Demangler::demangleSimpleString(StringView &MangledName, + bool Memorize) { + StringView S; + for (size_t i = 0; i < MangledName.size(); ++i) { + if (MangledName[i] != '@') + continue; + if (i == 0) + break; + S = MangledName.substr(0, i); + MangledName = MangledName.dropFront(i + 1); + + if (Memorize) + memorizeString(S); + return S; + } + + Error = true; + return {}; +} + +NamedIdentifierNode * +Demangler::demangleAnonymousNamespaceName(StringView &MangledName) { + assert(MangledName.startsWith("?A")); + MangledName.consumeFront("?A"); + + NamedIdentifierNode *Node = Arena.alloc<NamedIdentifierNode>(); + Node->Name = "`anonymous namespace'"; + size_t EndPos = MangledName.find('@'); + if (EndPos == StringView::npos) { + Error = true; + return nullptr; + } + StringView NamespaceKey = MangledName.substr(0, EndPos); + memorizeString(NamespaceKey); + MangledName = MangledName.substr(EndPos + 1); + return Node; +} + +NamedIdentifierNode * +Demangler::demangleLocallyScopedNamePiece(StringView &MangledName) { + assert(startsWithLocalScopePattern(MangledName)); + + NamedIdentifierNode *Identifier = Arena.alloc<NamedIdentifierNode>(); + MangledName.consumeFront('?'); + uint64_t Number = 0; + bool IsNegative = false; + std::tie(Number, IsNegative) = demangleNumber(MangledName); + assert(!IsNegative); + + // One ? to terminate the number + MangledName.consumeFront('?'); + + assert(!Error); + Node *Scope = parse(MangledName); + if (Error) + return nullptr; + + // Render the parent symbol's name into a buffer. + OutputBuffer OB; + OB << '`'; + Scope->output(OB, OF_Default); + OB << '\''; + OB << "::`" << Number << "'"; + + Identifier->Name = copyString(OB); + std::free(OB.getBuffer()); + return Identifier; +} + +// Parses a type name in the form of A@B@C@@ which represents C::B::A. +QualifiedNameNode * +Demangler::demangleFullyQualifiedTypeName(StringView &MangledName) { + IdentifierNode *Identifier = + demangleUnqualifiedTypeName(MangledName, /*Memorize=*/true); + if (Error) + return nullptr; + assert(Identifier); + + QualifiedNameNode *QN = demangleNameScopeChain(MangledName, Identifier); + if (Error) + return nullptr; + assert(QN); + return QN; +} + +// Parses a symbol name in the form of A@B@C@@ which represents C::B::A. +// Symbol names have slightly different rules regarding what can appear +// so we separate out the implementations for flexibility. +QualifiedNameNode * +Demangler::demangleFullyQualifiedSymbolName(StringView &MangledName) { + // This is the final component of a symbol name (i.e. the leftmost component + // of a mangled name. Since the only possible template instantiation that + // can appear in this context is a function template, and since those are + // not saved for the purposes of name backreferences, only backref simple + // names. + IdentifierNode *Identifier = + demangleUnqualifiedSymbolName(MangledName, NBB_Simple); + if (Error) + return nullptr; + + QualifiedNameNode *QN = demangleNameScopeChain(MangledName, Identifier); + if (Error) + return nullptr; + + if (Identifier->kind() == NodeKind::StructorIdentifier) { + if (QN->Components->Count < 2) { + Error = true; + return nullptr; + } + StructorIdentifierNode *SIN = + static_cast<StructorIdentifierNode *>(Identifier); + Node *ClassNode = QN->Components->Nodes[QN->Components->Count - 2]; + SIN->Class = static_cast<IdentifierNode *>(ClassNode); + } + assert(QN); + return QN; +} + +IdentifierNode *Demangler::demangleUnqualifiedTypeName(StringView &MangledName, + bool Memorize) { + // An inner-most name can be a back-reference, because a fully-qualified name + // (e.g. Scope + Inner) can contain other fully qualified names inside of + // them (for example template parameters), and these nested parameters can + // refer to previously mangled types. + if (startsWithDigit(MangledName)) + return demangleBackRefName(MangledName); + + if (MangledName.startsWith("?$")) + return demangleTemplateInstantiationName(MangledName, NBB_Template); + + return demangleSimpleName(MangledName, Memorize); +} + +IdentifierNode * +Demangler::demangleUnqualifiedSymbolName(StringView &MangledName, + NameBackrefBehavior NBB) { + if (startsWithDigit(MangledName)) + return demangleBackRefName(MangledName); + if (MangledName.startsWith("?$")) + return demangleTemplateInstantiationName(MangledName, NBB); + if (MangledName.startsWith('?')) + return demangleFunctionIdentifierCode(MangledName); + return demangleSimpleName(MangledName, /*Memorize=*/(NBB & NBB_Simple) != 0); +} + +IdentifierNode *Demangler::demangleNameScopePiece(StringView &MangledName) { + if (startsWithDigit(MangledName)) + return demangleBackRefName(MangledName); + + if (MangledName.startsWith("?$")) + return demangleTemplateInstantiationName(MangledName, NBB_Template); + + if (MangledName.startsWith("?A")) + return demangleAnonymousNamespaceName(MangledName); + + if (startsWithLocalScopePattern(MangledName)) + return demangleLocallyScopedNamePiece(MangledName); + + return demangleSimpleName(MangledName, /*Memorize=*/true); +} + +static NodeArrayNode *nodeListToNodeArray(ArenaAllocator &Arena, NodeList *Head, + size_t Count) { + NodeArrayNode *N = Arena.alloc<NodeArrayNode>(); + N->Count = Count; + N->Nodes = Arena.allocArray<Node *>(Count); + for (size_t I = 0; I < Count; ++I) { + N->Nodes[I] = Head->N; + Head = Head->Next; + } + return N; +} + +QualifiedNameNode * +Demangler::demangleNameScopeChain(StringView &MangledName, + IdentifierNode *UnqualifiedName) { + NodeList *Head = Arena.alloc<NodeList>(); + + Head->N = UnqualifiedName; + + size_t Count = 1; + while (!MangledName.consumeFront("@")) { + ++Count; + NodeList *NewHead = Arena.alloc<NodeList>(); + NewHead->Next = Head; + Head = NewHead; + + if (MangledName.empty()) { + Error = true; + return nullptr; + } + + assert(!Error); + IdentifierNode *Elem = demangleNameScopePiece(MangledName); + if (Error) + return nullptr; + + Head->N = Elem; + } + + QualifiedNameNode *QN = Arena.alloc<QualifiedNameNode>(); + QN->Components = nodeListToNodeArray(Arena, Head, Count); + return QN; +} + +FuncClass Demangler::demangleFunctionClass(StringView &MangledName) { + switch (MangledName.popFront()) { + case '9': + return FuncClass(FC_ExternC | FC_NoParameterList); + case 'A': + return FC_Private; + case 'B': + return FuncClass(FC_Private | FC_Far); + case 'C': + return FuncClass(FC_Private | FC_Static); + case 'D': + return FuncClass(FC_Private | FC_Static | FC_Far); + case 'E': + return FuncClass(FC_Private | FC_Virtual); + case 'F': + return FuncClass(FC_Private | FC_Virtual | FC_Far); + case 'G': + return FuncClass(FC_Private | FC_StaticThisAdjust); + case 'H': + return FuncClass(FC_Private | FC_StaticThisAdjust | FC_Far); + case 'I': + return FuncClass(FC_Protected); + case 'J': + return FuncClass(FC_Protected | FC_Far); + case 'K': + return FuncClass(FC_Protected | FC_Static); + case 'L': + return FuncClass(FC_Protected | FC_Static | FC_Far); + case 'M': + return FuncClass(FC_Protected | FC_Virtual); + case 'N': + return FuncClass(FC_Protected | FC_Virtual | FC_Far); + case 'O': + return FuncClass(FC_Protected | FC_Virtual | FC_StaticThisAdjust); + case 'P': + return FuncClass(FC_Protected | FC_Virtual | FC_StaticThisAdjust | FC_Far); + case 'Q': + return FuncClass(FC_Public); + case 'R': + return FuncClass(FC_Public | FC_Far); + case 'S': + return FuncClass(FC_Public | FC_Static); + case 'T': + return FuncClass(FC_Public | FC_Static | FC_Far); + case 'U': + return FuncClass(FC_Public | FC_Virtual); + case 'V': + return FuncClass(FC_Public | FC_Virtual | FC_Far); + case 'W': + return FuncClass(FC_Public | FC_Virtual | FC_StaticThisAdjust); + case 'X': + return FuncClass(FC_Public | FC_Virtual | FC_StaticThisAdjust | FC_Far); + case 'Y': + return FuncClass(FC_Global); + case 'Z': + return FuncClass(FC_Global | FC_Far); + case '$': { + FuncClass VFlag = FC_VirtualThisAdjust; + if (MangledName.consumeFront('R')) + VFlag = FuncClass(VFlag | FC_VirtualThisAdjustEx); + if (MangledName.empty()) + break; + switch (MangledName.popFront()) { + case '0': + return FuncClass(FC_Private | FC_Virtual | VFlag); + case '1': + return FuncClass(FC_Private | FC_Virtual | VFlag | FC_Far); + case '2': + return FuncClass(FC_Protected | FC_Virtual | VFlag); + case '3': + return FuncClass(FC_Protected | FC_Virtual | VFlag | FC_Far); + case '4': + return FuncClass(FC_Public | FC_Virtual | VFlag); + case '5': + return FuncClass(FC_Public | FC_Virtual | VFlag | FC_Far); + } + } + } + + Error = true; + return FC_Public; +} + +CallingConv Demangler::demangleCallingConvention(StringView &MangledName) { + if (MangledName.empty()) { + Error = true; + return CallingConv::None; + } + + switch (MangledName.popFront()) { + case 'A': + case 'B': + return CallingConv::Cdecl; + case 'C': + case 'D': + return CallingConv::Pascal; + case 'E': + case 'F': + return CallingConv::Thiscall; + case 'G': + case 'H': + return CallingConv::Stdcall; + case 'I': + case 'J': + return CallingConv::Fastcall; + case 'M': + case 'N': + return CallingConv::Clrcall; + case 'O': + case 'P': + return CallingConv::Eabi; + case 'Q': + return CallingConv::Vectorcall; + case 'S': + return CallingConv::Swift; + case 'W': + return CallingConv::SwiftAsync; + } + + return CallingConv::None; +} + +StorageClass Demangler::demangleVariableStorageClass(StringView &MangledName) { + assert(MangledName.front() >= '0' && MangledName.front() <= '4'); + + switch (MangledName.popFront()) { + case '0': + return StorageClass::PrivateStatic; + case '1': + return StorageClass::ProtectedStatic; + case '2': + return StorageClass::PublicStatic; + case '3': + return StorageClass::Global; + case '4': + return StorageClass::FunctionLocalStatic; + } + DEMANGLE_UNREACHABLE; +} + +std::pair<Qualifiers, bool> +Demangler::demangleQualifiers(StringView &MangledName) { + if (MangledName.empty()) { + Error = true; + return std::make_pair(Q_None, false); + } + + switch (MangledName.popFront()) { + // Member qualifiers + case 'Q': + return std::make_pair(Q_None, true); + case 'R': + return std::make_pair(Q_Const, true); + case 'S': + return std::make_pair(Q_Volatile, true); + case 'T': + return std::make_pair(Qualifiers(Q_Const | Q_Volatile), true); + // Non-Member qualifiers + case 'A': + return std::make_pair(Q_None, false); + case 'B': + return std::make_pair(Q_Const, false); + case 'C': + return std::make_pair(Q_Volatile, false); + case 'D': + return std::make_pair(Qualifiers(Q_Const | Q_Volatile), false); + } + Error = true; + return std::make_pair(Q_None, false); +} + +// <variable-type> ::= <type> <cvr-qualifiers> +// ::= <type> <pointee-cvr-qualifiers> # pointers, references +TypeNode *Demangler::demangleType(StringView &MangledName, + QualifierMangleMode QMM) { + Qualifiers Quals = Q_None; + bool IsMember = false; + if (QMM == QualifierMangleMode::Mangle) { + std::tie(Quals, IsMember) = demangleQualifiers(MangledName); + } else if (QMM == QualifierMangleMode::Result) { + if (MangledName.consumeFront('?')) + std::tie(Quals, IsMember) = demangleQualifiers(MangledName); + } + + if (MangledName.empty()) { + Error = true; + return nullptr; + } + + TypeNode *Ty = nullptr; + if (isTagType(MangledName)) + Ty = demangleClassType(MangledName); + else if (isPointerType(MangledName)) { + if (isMemberPointer(MangledName, Error)) + Ty = demangleMemberPointerType(MangledName); + else if (!Error) + Ty = demanglePointerType(MangledName); + else + return nullptr; + } else if (isArrayType(MangledName)) + Ty = demangleArrayType(MangledName); + else if (isFunctionType(MangledName)) { + if (MangledName.consumeFront("$$A8@@")) + Ty = demangleFunctionType(MangledName, true); + else { + assert(MangledName.startsWith("$$A6")); + MangledName.consumeFront("$$A6"); + Ty = demangleFunctionType(MangledName, false); + } + } else if (isCustomType(MangledName)) { + Ty = demangleCustomType(MangledName); + } else { + Ty = demanglePrimitiveType(MangledName); + } + + if (!Ty || Error) + return Ty; + Ty->Quals = Qualifiers(Ty->Quals | Quals); + return Ty; +} + +bool Demangler::demangleThrowSpecification(StringView &MangledName) { + if (MangledName.consumeFront("_E")) + return true; + if (MangledName.consumeFront('Z')) + return false; + + Error = true; + return false; +} + +FunctionSignatureNode *Demangler::demangleFunctionType(StringView &MangledName, + bool HasThisQuals) { + FunctionSignatureNode *FTy = Arena.alloc<FunctionSignatureNode>(); + + if (HasThisQuals) { + FTy->Quals = demanglePointerExtQualifiers(MangledName); + FTy->RefQualifier = demangleFunctionRefQualifier(MangledName); + FTy->Quals = Qualifiers(FTy->Quals | demangleQualifiers(MangledName).first); + } + + // Fields that appear on both member and non-member functions. + FTy->CallConvention = demangleCallingConvention(MangledName); + + // <return-type> ::= <type> + // ::= @ # structors (they have no declared return type) + bool IsStructor = MangledName.consumeFront('@'); + if (!IsStructor) + FTy->ReturnType = demangleType(MangledName, QualifierMangleMode::Result); + + FTy->Params = demangleFunctionParameterList(MangledName, FTy->IsVariadic); + + FTy->IsNoexcept = demangleThrowSpecification(MangledName); + + return FTy; +} + +FunctionSymbolNode * +Demangler::demangleFunctionEncoding(StringView &MangledName) { + FuncClass ExtraFlags = FC_None; + if (MangledName.consumeFront("$$J0")) + ExtraFlags = FC_ExternC; + + if (MangledName.empty()) { + Error = true; + return nullptr; + } + + FuncClass FC = demangleFunctionClass(MangledName); + FC = FuncClass(ExtraFlags | FC); + + FunctionSignatureNode *FSN = nullptr; + ThunkSignatureNode *TTN = nullptr; + if (FC & FC_StaticThisAdjust) { + TTN = Arena.alloc<ThunkSignatureNode>(); + TTN->ThisAdjust.StaticOffset = demangleSigned(MangledName); + } else if (FC & FC_VirtualThisAdjust) { + TTN = Arena.alloc<ThunkSignatureNode>(); + if (FC & FC_VirtualThisAdjustEx) { + TTN->ThisAdjust.VBPtrOffset = demangleSigned(MangledName); + TTN->ThisAdjust.VBOffsetOffset = demangleSigned(MangledName); + } + TTN->ThisAdjust.VtordispOffset = demangleSigned(MangledName); + TTN->ThisAdjust.StaticOffset = demangleSigned(MangledName); + } + + if (FC & FC_NoParameterList) { + // This is an extern "C" function whose full signature hasn't been mangled. + // This happens when we need to mangle a local symbol inside of an extern + // "C" function. + FSN = Arena.alloc<FunctionSignatureNode>(); + } else { + bool HasThisQuals = !(FC & (FC_Global | FC_Static)); + FSN = demangleFunctionType(MangledName, HasThisQuals); + } + + if (Error) + return nullptr; + + if (TTN) { + *static_cast<FunctionSignatureNode *>(TTN) = *FSN; + FSN = TTN; + } + FSN->FunctionClass = FC; + + FunctionSymbolNode *Symbol = Arena.alloc<FunctionSymbolNode>(); + Symbol->Signature = FSN; + return Symbol; +} + +CustomTypeNode *Demangler::demangleCustomType(StringView &MangledName) { + assert(MangledName.startsWith('?')); + MangledName.popFront(); + + CustomTypeNode *CTN = Arena.alloc<CustomTypeNode>(); + CTN->Identifier = demangleUnqualifiedTypeName(MangledName, /*Memorize=*/true); + if (!MangledName.consumeFront('@')) + Error = true; + if (Error) + return nullptr; + return CTN; +} + +// Reads a primitive type. +PrimitiveTypeNode *Demangler::demanglePrimitiveType(StringView &MangledName) { + if (MangledName.consumeFront("$$T")) + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Nullptr); + + switch (MangledName.popFront()) { + case 'X': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Void); + case 'D': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char); + case 'C': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Schar); + case 'E': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uchar); + case 'F': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Short); + case 'G': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ushort); + case 'H': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Int); + case 'I': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uint); + case 'J': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Long); + case 'K': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ulong); + case 'M': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Float); + case 'N': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Double); + case 'O': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ldouble); + case '_': { + if (MangledName.empty()) { + Error = true; + return nullptr; + } + switch (MangledName.popFront()) { + case 'N': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Bool); + case 'J': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Int64); + case 'K': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uint64); + case 'W': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Wchar); + case 'Q': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char8); + case 'S': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char16); + case 'U': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char32); + } + break; + } + } + Error = true; + return nullptr; +} + +TagTypeNode *Demangler::demangleClassType(StringView &MangledName) { + TagTypeNode *TT = nullptr; + + switch (MangledName.popFront()) { + case 'T': + TT = Arena.alloc<TagTypeNode>(TagKind::Union); + break; + case 'U': + TT = Arena.alloc<TagTypeNode>(TagKind::Struct); + break; + case 'V': + TT = Arena.alloc<TagTypeNode>(TagKind::Class); + break; + case 'W': + if (!MangledName.consumeFront('4')) { + Error = true; + return nullptr; + } + TT = Arena.alloc<TagTypeNode>(TagKind::Enum); + break; + default: + assert(false); + } + + TT->QualifiedName = demangleFullyQualifiedTypeName(MangledName); + return TT; +} + +// <pointer-type> ::= E? <pointer-cvr-qualifiers> <ext-qualifiers> <type> +// # the E is required for 64-bit non-static pointers +PointerTypeNode *Demangler::demanglePointerType(StringView &MangledName) { + PointerTypeNode *Pointer = Arena.alloc<PointerTypeNode>(); + + std::tie(Pointer->Quals, Pointer->Affinity) = + demanglePointerCVQualifiers(MangledName); + + if (MangledName.consumeFront("6")) { + Pointer->Pointee = demangleFunctionType(MangledName, false); + return Pointer; + } + + Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName); + Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals); + + Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Mangle); + return Pointer; +} + +PointerTypeNode *Demangler::demangleMemberPointerType(StringView &MangledName) { + PointerTypeNode *Pointer = Arena.alloc<PointerTypeNode>(); + + std::tie(Pointer->Quals, Pointer->Affinity) = + demanglePointerCVQualifiers(MangledName); + assert(Pointer->Affinity == PointerAffinity::Pointer); + + Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName); + Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals); + + // isMemberPointer() only returns true if there is at least one character + // after the qualifiers. + if (MangledName.consumeFront("8")) { + Pointer->ClassParent = demangleFullyQualifiedTypeName(MangledName); + Pointer->Pointee = demangleFunctionType(MangledName, true); + } else { + Qualifiers PointeeQuals = Q_None; + bool IsMember = false; + std::tie(PointeeQuals, IsMember) = demangleQualifiers(MangledName); + assert(IsMember || Error); + Pointer->ClassParent = demangleFullyQualifiedTypeName(MangledName); + + Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Drop); + if (Pointer->Pointee) + Pointer->Pointee->Quals = PointeeQuals; + } + + return Pointer; +} + +Qualifiers Demangler::demanglePointerExtQualifiers(StringView &MangledName) { + Qualifiers Quals = Q_None; + if (MangledName.consumeFront('E')) + Quals = Qualifiers(Quals | Q_Pointer64); + if (MangledName.consumeFront('I')) + Quals = Qualifiers(Quals | Q_Restrict); + if (MangledName.consumeFront('F')) + Quals = Qualifiers(Quals | Q_Unaligned); + + return Quals; +} + +ArrayTypeNode *Demangler::demangleArrayType(StringView &MangledName) { + assert(MangledName.front() == 'Y'); + MangledName.popFront(); + + uint64_t Rank = 0; + bool IsNegative = false; + std::tie(Rank, IsNegative) = demangleNumber(MangledName); + if (IsNegative || Rank == 0) { + Error = true; + return nullptr; + } + + ArrayTypeNode *ATy = Arena.alloc<ArrayTypeNode>(); + NodeList *Head = Arena.alloc<NodeList>(); + NodeList *Tail = Head; + + for (uint64_t I = 0; I < Rank; ++I) { + uint64_t D = 0; + std::tie(D, IsNegative) = demangleNumber(MangledName); + if (Error || IsNegative) { + Error = true; + return nullptr; + } + Tail->N = Arena.alloc<IntegerLiteralNode>(D, IsNegative); + if (I + 1 < Rank) { + Tail->Next = Arena.alloc<NodeList>(); + Tail = Tail->Next; + } + } + ATy->Dimensions = nodeListToNodeArray(Arena, Head, Rank); + + if (MangledName.consumeFront("$$C")) { + bool IsMember = false; + std::tie(ATy->Quals, IsMember) = demangleQualifiers(MangledName); + if (IsMember) { + Error = true; + return nullptr; + } + } + + ATy->ElementType = demangleType(MangledName, QualifierMangleMode::Drop); + return ATy; +} + +// Reads a function's parameters. +NodeArrayNode *Demangler::demangleFunctionParameterList(StringView &MangledName, + bool &IsVariadic) { + // Empty parameter list. + if (MangledName.consumeFront('X')) + return nullptr; + + NodeList *Head = Arena.alloc<NodeList>(); + NodeList **Current = &Head; + size_t Count = 0; + while (!Error && !MangledName.startsWith('@') && + !MangledName.startsWith('Z')) { + ++Count; + + if (startsWithDigit(MangledName)) { + size_t N = MangledName[0] - '0'; + if (N >= Backrefs.FunctionParamCount) { + Error = true; + return nullptr; + } + MangledName = MangledName.dropFront(); + + *Current = Arena.alloc<NodeList>(); + (*Current)->N = Backrefs.FunctionParams[N]; + Current = &(*Current)->Next; + continue; + } + + size_t OldSize = MangledName.size(); + + *Current = Arena.alloc<NodeList>(); + TypeNode *TN = demangleType(MangledName, QualifierMangleMode::Drop); + if (!TN || Error) + return nullptr; + + (*Current)->N = TN; + + size_t CharsConsumed = OldSize - MangledName.size(); + assert(CharsConsumed != 0); + + // Single-letter types are ignored for backreferences because memorizing + // them doesn't save anything. + if (Backrefs.FunctionParamCount <= 9 && CharsConsumed > 1) + Backrefs.FunctionParams[Backrefs.FunctionParamCount++] = TN; + + Current = &(*Current)->Next; + } + + if (Error) + return nullptr; + + NodeArrayNode *NA = nodeListToNodeArray(Arena, Head, Count); + // A non-empty parameter list is terminated by either 'Z' (variadic) parameter + // list or '@' (non variadic). Careful not to consume "@Z", as in that case + // the following Z could be a throw specifier. + if (MangledName.consumeFront('@')) + return NA; + + if (MangledName.consumeFront('Z')) { + IsVariadic = true; + return NA; + } + + DEMANGLE_UNREACHABLE; +} + +NodeArrayNode * +Demangler::demangleTemplateParameterList(StringView &MangledName) { + NodeList *Head = nullptr; + NodeList **Current = &Head; + size_t Count = 0; + + while (!MangledName.startsWith('@')) { + if (MangledName.consumeFront("$S") || MangledName.consumeFront("$$V") || + MangledName.consumeFront("$$$V") || MangledName.consumeFront("$$Z")) { + // parameter pack separator + continue; + } + + ++Count; + + // Template parameter lists don't participate in back-referencing. + *Current = Arena.alloc<NodeList>(); + + NodeList &TP = **Current; + + TemplateParameterReferenceNode *TPRN = nullptr; + if (MangledName.consumeFront("$$Y")) { + // Template alias + TP.N = demangleFullyQualifiedTypeName(MangledName); + } else if (MangledName.consumeFront("$$B")) { + // Array + TP.N = demangleType(MangledName, QualifierMangleMode::Drop); + } else if (MangledName.consumeFront("$$C")) { + // Type has qualifiers. + TP.N = demangleType(MangledName, QualifierMangleMode::Mangle); + } else if (MangledName.startsWith("$1") || MangledName.startsWith("$H") || + MangledName.startsWith("$I") || MangledName.startsWith("$J")) { + // Pointer to member + TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>(); + TPRN->IsMemberPointer = true; + + MangledName = MangledName.dropFront(); + // 1 - single inheritance <name> + // H - multiple inheritance <name> <number> + // I - virtual inheritance <name> <number> <number> + // J - unspecified inheritance <name> <number> <number> <number> + char InheritanceSpecifier = MangledName.popFront(); + SymbolNode *S = nullptr; + if (MangledName.startsWith('?')) { + S = parse(MangledName); + if (Error || !S->Name) { + Error = true; + return nullptr; + } + memorizeIdentifier(S->Name->getUnqualifiedIdentifier()); + } + + switch (InheritanceSpecifier) { + case 'J': + TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] = + demangleSigned(MangledName); + DEMANGLE_FALLTHROUGH; + case 'I': + TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] = + demangleSigned(MangledName); + DEMANGLE_FALLTHROUGH; + case 'H': + TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] = + demangleSigned(MangledName); + DEMANGLE_FALLTHROUGH; + case '1': + break; + default: + DEMANGLE_UNREACHABLE; + } + TPRN->Affinity = PointerAffinity::Pointer; + TPRN->Symbol = S; + } else if (MangledName.startsWith("$E?")) { + MangledName.consumeFront("$E"); + // Reference to symbol + TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>(); + TPRN->Symbol = parse(MangledName); + TPRN->Affinity = PointerAffinity::Reference; + } else if (MangledName.startsWith("$F") || MangledName.startsWith("$G")) { + TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>(); + + // Data member pointer. + MangledName = MangledName.dropFront(); + char InheritanceSpecifier = MangledName.popFront(); + + switch (InheritanceSpecifier) { + case 'G': + TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] = + demangleSigned(MangledName); + DEMANGLE_FALLTHROUGH; + case 'F': + TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] = + demangleSigned(MangledName); + TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] = + demangleSigned(MangledName); + break; + default: + DEMANGLE_UNREACHABLE; + } + TPRN->IsMemberPointer = true; + + } else if (MangledName.consumeFront("$0")) { + // Integral non-type template parameter + bool IsNegative = false; + uint64_t Value = 0; + std::tie(Value, IsNegative) = demangleNumber(MangledName); + + TP.N = Arena.alloc<IntegerLiteralNode>(Value, IsNegative); + } else { + TP.N = demangleType(MangledName, QualifierMangleMode::Drop); + } + if (Error) + return nullptr; + + Current = &TP.Next; + } + + // The loop above returns nullptr on Error. + assert(!Error); + + // Template parameter lists cannot be variadic, so it can only be terminated + // by @ (as opposed to 'Z' in the function parameter case). + assert(MangledName.startsWith('@')); // The above loop exits only on '@'. + MangledName.consumeFront('@'); + return nodeListToNodeArray(Arena, Head, Count); +} + +void Demangler::dumpBackReferences() { + std::printf("%d function parameter backreferences\n", + (int)Backrefs.FunctionParamCount); + + // Create an output stream so we can render each type. + OutputBuffer OB; + for (size_t I = 0; I < Backrefs.FunctionParamCount; ++I) { + OB.setCurrentPosition(0); + + TypeNode *T = Backrefs.FunctionParams[I]; + T->output(OB, OF_Default); + + StringView B = OB; + std::printf(" [%d] - %.*s\n", (int)I, (int)B.size(), B.begin()); + } + std::free(OB.getBuffer()); + + if (Backrefs.FunctionParamCount > 0) + std::printf("\n"); + std::printf("%d name backreferences\n", (int)Backrefs.NamesCount); + for (size_t I = 0; I < Backrefs.NamesCount; ++I) { + std::printf(" [%d] - %.*s\n", (int)I, (int)Backrefs.Names[I]->Name.size(), + Backrefs.Names[I]->Name.begin()); + } + if (Backrefs.NamesCount > 0) + std::printf("\n"); +} + +char *llvm::microsoftDemangle(const char *MangledName, size_t *NMangled, + char *Buf, size_t *N, + int *Status, MSDemangleFlags Flags) { + Demangler D; + + StringView Name{MangledName}; + SymbolNode *AST = D.parse(Name); + if (!D.Error && NMangled) + *NMangled = Name.begin() - MangledName; + + if (Flags & MSDF_DumpBackrefs) + D.dumpBackReferences(); + + OutputFlags OF = OF_Default; + if (Flags & MSDF_NoCallingConvention) + OF = OutputFlags(OF | OF_NoCallingConvention); + if (Flags & MSDF_NoAccessSpecifier) + OF = OutputFlags(OF | OF_NoAccessSpecifier); + if (Flags & MSDF_NoReturnType) + OF = OutputFlags(OF | OF_NoReturnType); + if (Flags & MSDF_NoMemberType) + OF = OutputFlags(OF | OF_NoMemberType); + if (Flags & MSDF_NoVariableType) + OF = OutputFlags(OF | OF_NoVariableType); + + int InternalStatus = demangle_success; + if (D.Error) + InternalStatus = demangle_invalid_mangled_name; + else { + OutputBuffer OB(Buf, N); + AST->output(OB, OF); + OB += '\0'; + if (N != nullptr) + *N = OB.getCurrentPosition(); + Buf = OB.getBuffer(); + } + + if (Status) + *Status = InternalStatus; + return InternalStatus == demangle_success ? Buf : nullptr; +} diff --git a/demangle/third_party/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp b/demangle/third_party/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp new file mode 100644 index 00000000..975649f2 --- /dev/null +++ b/demangle/third_party/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp @@ -0,0 +1,658 @@ +//===- MicrosoftDemangle.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a demangler for MSVC-style mangled symbols. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Demangle/MicrosoftDemangleNodes.h" +#include "llvm/Demangle/Utility.h" +#include <cctype> +#include <string> + +using namespace llvm; +using namespace ms_demangle; + +#define OUTPUT_ENUM_CLASS_VALUE(Enum, Value, Desc) \ + case Enum::Value: \ + OB << Desc; \ + break; + +// Writes a space if the last token does not end with a punctuation. +static void outputSpaceIfNecessary(OutputBuffer &OB) { + if (OB.empty()) + return; + + char C = OB.back(); + if (std::isalnum(C) || C == '>') + OB << " "; +} + +static void outputSingleQualifier(OutputBuffer &OB, Qualifiers Q) { + switch (Q) { + case Q_Const: + OB << "const"; + break; + case Q_Volatile: + OB << "volatile"; + break; + case Q_Restrict: + OB << "__restrict"; + break; + default: + break; + } +} + +static bool outputQualifierIfPresent(OutputBuffer &OB, Qualifiers Q, + Qualifiers Mask, bool NeedSpace) { + if (!(Q & Mask)) + return NeedSpace; + + if (NeedSpace) + OB << " "; + + outputSingleQualifier(OB, Mask); + return true; +} + +static void outputQualifiers(OutputBuffer &OB, Qualifiers Q, bool SpaceBefore, + bool SpaceAfter) { + if (Q == Q_None) + return; + + size_t Pos1 = OB.getCurrentPosition(); + SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Const, SpaceBefore); + SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Volatile, SpaceBefore); + SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Restrict, SpaceBefore); + size_t Pos2 = OB.getCurrentPosition(); + if (SpaceAfter && Pos2 > Pos1) + OB << " "; +} + +static void outputCallingConvention(OutputBuffer &OB, CallingConv CC) { + outputSpaceIfNecessary(OB); + + switch (CC) { + case CallingConv::Cdecl: + OB << "__cdecl"; + break; + case CallingConv::Fastcall: + OB << "__fastcall"; + break; + case CallingConv::Pascal: + OB << "__pascal"; + break; + case CallingConv::Regcall: + OB << "__regcall"; + break; + case CallingConv::Stdcall: + OB << "__stdcall"; + break; + case CallingConv::Thiscall: + OB << "__thiscall"; + break; + case CallingConv::Eabi: + OB << "__eabi"; + break; + case CallingConv::Vectorcall: + OB << "__vectorcall"; + break; + case CallingConv::Clrcall: + OB << "__clrcall"; + break; + case CallingConv::Swift: + OB << "__attribute__((__swiftcall__)) "; + break; + case CallingConv::SwiftAsync: + OB << "__attribute__((__swiftasynccall__)) "; + break; + default: + break; + } +} + +std::string Node::toString(OutputFlags Flags) const { + OutputBuffer OB; + this->output(OB, Flags); + StringView SV = OB; + std::string Owned(SV.begin(), SV.end()); + std::free(OB.getBuffer()); + return Owned; +} + +void PrimitiveTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const { + switch (PrimKind) { + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Void, "void"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Bool, "bool"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char, "char"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Schar, "signed char"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uchar, "unsigned char"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char8, "char8_t"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char16, "char16_t"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char32, "char32_t"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Short, "short"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ushort, "unsigned short"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Int, "int"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uint, "unsigned int"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Long, "long"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ulong, "unsigned long"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Int64, "__int64"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uint64, "unsigned __int64"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Wchar, "wchar_t"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Float, "float"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Double, "double"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ldouble, "long double"); + OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Nullptr, "std::nullptr_t"); + } + outputQualifiers(OB, Quals, true, false); +} + +void NodeArrayNode::output(OutputBuffer &OB, OutputFlags Flags) const { + output(OB, Flags, ", "); +} + +void NodeArrayNode::output(OutputBuffer &OB, OutputFlags Flags, + StringView Separator) const { + if (Count == 0) + return; + if (Nodes[0]) + Nodes[0]->output(OB, Flags); + for (size_t I = 1; I < Count; ++I) { + OB << Separator; + Nodes[I]->output(OB, Flags); + } +} + +void EncodedStringLiteralNode::output(OutputBuffer &OB, + OutputFlags Flags) const { + switch (Char) { + case CharKind::Wchar: + OB << "L\""; + break; + case CharKind::Char: + OB << "\""; + break; + case CharKind::Char16: + OB << "u\""; + break; + case CharKind::Char32: + OB << "U\""; + break; + } + OB << DecodedString << "\""; + if (IsTruncated) + OB << "..."; +} + +void IntegerLiteralNode::output(OutputBuffer &OB, OutputFlags Flags) const { + if (IsNegative) + OB << '-'; + OB << Value; +} + +void TemplateParameterReferenceNode::output(OutputBuffer &OB, + OutputFlags Flags) const { + if (ThunkOffsetCount > 0) + OB << "{"; + else if (Affinity == PointerAffinity::Pointer) + OB << "&"; + + if (Symbol) { + Symbol->output(OB, Flags); + if (ThunkOffsetCount > 0) + OB << ", "; + } + + if (ThunkOffsetCount > 0) + OB << ThunkOffsets[0]; + for (int I = 1; I < ThunkOffsetCount; ++I) { + OB << ", " << ThunkOffsets[I]; + } + if (ThunkOffsetCount > 0) + OB << "}"; +} + +void IdentifierNode::outputTemplateParameters(OutputBuffer &OB, + OutputFlags Flags) const { + if (!TemplateParams) + return; + OB << "<"; + TemplateParams->output(OB, Flags); + OB << ">"; +} + +void DynamicStructorIdentifierNode::output(OutputBuffer &OB, + OutputFlags Flags) const { + if (IsDestructor) + OB << "`dynamic atexit destructor for "; + else + OB << "`dynamic initializer for "; + + if (Variable) { + OB << "`"; + Variable->output(OB, Flags); + OB << "''"; + } else { + OB << "'"; + Name->output(OB, Flags); + OB << "''"; + } +} + +void NamedIdentifierNode::output(OutputBuffer &OB, OutputFlags Flags) const { + OB << Name; + outputTemplateParameters(OB, Flags); +} + +void IntrinsicFunctionIdentifierNode::output(OutputBuffer &OB, + OutputFlags Flags) const { + switch (Operator) { + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, New, "operator new"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Delete, "operator delete"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Assign, "operator="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, RightShift, "operator>>"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LeftShift, "operator<<"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalNot, "operator!"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Equals, "operator=="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, NotEquals, "operator!="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArraySubscript, + "operator[]"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Pointer, "operator->"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Increment, "operator++"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Decrement, "operator--"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Minus, "operator-"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Plus, "operator+"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Dereference, "operator*"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseAnd, "operator&"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, MemberPointer, + "operator->*"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Divide, "operator/"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Modulus, "operator%"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LessThan, "operator<"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LessThanEqual, "operator<="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, GreaterThan, "operator>"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, GreaterThanEqual, + "operator>="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Comma, "operator,"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Parens, "operator()"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseNot, "operator~"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseXor, "operator^"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseOr, "operator|"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalAnd, "operator&&"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalOr, "operator||"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, TimesEqual, "operator*="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, PlusEqual, "operator+="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, MinusEqual, "operator-="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, DivEqual, "operator/="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ModEqual, "operator%="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, RshEqual, "operator>>="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LshEqual, "operator<<="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseAndEqual, + "operator&="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseOrEqual, + "operator|="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseXorEqual, + "operator^="); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VbaseDtor, "`vbase dtor'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecDelDtor, + "`vector deleting dtor'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, DefaultCtorClosure, + "`default ctor closure'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ScalarDelDtor, + "`scalar deleting dtor'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecCtorIter, + "`vector ctor iterator'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecDtorIter, + "`vector dtor iterator'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecVbaseCtorIter, + "`vector vbase ctor iterator'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VdispMap, + "`virtual displacement map'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecCtorIter, + "`eh vector ctor iterator'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecDtorIter, + "`eh vector dtor iterator'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecVbaseCtorIter, + "`eh vector vbase ctor iterator'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, CopyCtorClosure, + "`copy ctor closure'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LocalVftableCtorClosure, + "`local vftable ctor closure'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArrayNew, "operator new[]"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArrayDelete, + "operator delete[]"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorCtorIter, + "`managed vector ctor iterator'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorDtorIter, + "`managed vector dtor iterator'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVectorCopyCtorIter, + "`EH vector copy ctor iterator'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVectorVbaseCopyCtorIter, + "`EH vector vbase copy ctor iterator'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VectorCopyCtorIter, + "`vector copy ctor iterator'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VectorVbaseCopyCtorIter, + "`vector vbase copy constructor iterator'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorVbaseCopyCtorIter, + "`managed vector vbase copy constructor iterator'"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, CoAwait, + "operator co_await"); + OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Spaceship, "operator<=>"); + case IntrinsicFunctionKind::MaxIntrinsic: + case IntrinsicFunctionKind::None: + break; + } + outputTemplateParameters(OB, Flags); +} + +void LocalStaticGuardIdentifierNode::output(OutputBuffer &OB, + OutputFlags Flags) const { + if (IsThread) + OB << "`local static thread guard'"; + else + OB << "`local static guard'"; + if (ScopeIndex > 0) + OB << "{" << ScopeIndex << "}"; +} + +void ConversionOperatorIdentifierNode::output(OutputBuffer &OB, + OutputFlags Flags) const { + OB << "operator"; + outputTemplateParameters(OB, Flags); + OB << " "; + TargetType->output(OB, Flags); +} + +void StructorIdentifierNode::output(OutputBuffer &OB, OutputFlags Flags) const { + if (IsDestructor) + OB << "~"; + Class->output(OB, Flags); + outputTemplateParameters(OB, Flags); +} + +void LiteralOperatorIdentifierNode::output(OutputBuffer &OB, + OutputFlags Flags) const { + OB << "operator \"\"" << Name; + outputTemplateParameters(OB, Flags); +} + +void FunctionSignatureNode::outputPre(OutputBuffer &OB, + OutputFlags Flags) const { + if (!(Flags & OF_NoAccessSpecifier)) { + if (FunctionClass & FC_Public) + OB << "public: "; + if (FunctionClass & FC_Protected) + OB << "protected: "; + if (FunctionClass & FC_Private) + OB << "private: "; + } + + if (!(Flags & OF_NoMemberType)) { + if (!(FunctionClass & FC_Global)) { + if (FunctionClass & FC_Static) + OB << "static "; + } + if (FunctionClass & FC_Virtual) + OB << "virtual "; + + if (FunctionClass & FC_ExternC) + OB << "extern \"C\" "; + } + + if (!(Flags & OF_NoReturnType) && ReturnType) { + ReturnType->outputPre(OB, Flags); + OB << " "; + } + + if (!(Flags & OF_NoCallingConvention)) + outputCallingConvention(OB, CallConvention); +} + +void FunctionSignatureNode::outputPost(OutputBuffer &OB, + OutputFlags Flags) const { + if (!(FunctionClass & FC_NoParameterList)) { + OB << "("; + if (Params) + Params->output(OB, Flags); + else + OB << "void"; + + if (IsVariadic) { + if (OB.back() != '(') + OB << ", "; + OB << "..."; + } + OB << ")"; + } + + if (Quals & Q_Const) + OB << " const"; + if (Quals & Q_Volatile) + OB << " volatile"; + if (Quals & Q_Restrict) + OB << " __restrict"; + if (Quals & Q_Unaligned) + OB << " __unaligned"; + + if (IsNoexcept) + OB << " noexcept"; + + if (RefQualifier == FunctionRefQualifier::Reference) + OB << " &"; + else if (RefQualifier == FunctionRefQualifier::RValueReference) + OB << " &&"; + + if (!(Flags & OF_NoReturnType) && ReturnType) + ReturnType->outputPost(OB, Flags); +} + +void ThunkSignatureNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const { + OB << "[thunk]: "; + + FunctionSignatureNode::outputPre(OB, Flags); +} + +void ThunkSignatureNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const { + if (FunctionClass & FC_StaticThisAdjust) { + OB << "`adjustor{" << ThisAdjust.StaticOffset << "}'"; + } else if (FunctionClass & FC_VirtualThisAdjust) { + if (FunctionClass & FC_VirtualThisAdjustEx) { + OB << "`vtordispex{" << ThisAdjust.VBPtrOffset << ", " + << ThisAdjust.VBOffsetOffset << ", " << ThisAdjust.VtordispOffset + << ", " << ThisAdjust.StaticOffset << "}'"; + } else { + OB << "`vtordisp{" << ThisAdjust.VtordispOffset << ", " + << ThisAdjust.StaticOffset << "}'"; + } + } + + FunctionSignatureNode::outputPost(OB, Flags); +} + +void PointerTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const { + if (Pointee->kind() == NodeKind::FunctionSignature) { + // If this is a pointer to a function, don't output the calling convention. + // It needs to go inside the parentheses. + const FunctionSignatureNode *Sig = + static_cast<const FunctionSignatureNode *>(Pointee); + Sig->outputPre(OB, OF_NoCallingConvention); + } else + Pointee->outputPre(OB, Flags); + + outputSpaceIfNecessary(OB); + + if (Quals & Q_Unaligned) + OB << "__unaligned "; + + if (Pointee->kind() == NodeKind::ArrayType) { + OB << "("; + } else if (Pointee->kind() == NodeKind::FunctionSignature) { + OB << "("; + const FunctionSignatureNode *Sig = + static_cast<const FunctionSignatureNode *>(Pointee); + outputCallingConvention(OB, Sig->CallConvention); + OB << " "; + } + + if (ClassParent) { + ClassParent->output(OB, Flags); + OB << "::"; + } + + switch (Affinity) { + case PointerAffinity::Pointer: + OB << "*"; + break; + case PointerAffinity::Reference: + OB << "&"; + break; + case PointerAffinity::RValueReference: + OB << "&&"; + break; + default: + assert(false); + } + outputQualifiers(OB, Quals, false, false); +} + +void PointerTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const { + if (Pointee->kind() == NodeKind::ArrayType || + Pointee->kind() == NodeKind::FunctionSignature) + OB << ")"; + + Pointee->outputPost(OB, Flags); +} + +void TagTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const { + if (!(Flags & OF_NoTagSpecifier)) { + switch (Tag) { + OUTPUT_ENUM_CLASS_VALUE(TagKind, Class, "class"); + OUTPUT_ENUM_CLASS_VALUE(TagKind, Struct, "struct"); + OUTPUT_ENUM_CLASS_VALUE(TagKind, Union, "union"); + OUTPUT_ENUM_CLASS_VALUE(TagKind, Enum, "enum"); + } + OB << " "; + } + QualifiedName->output(OB, Flags); + outputQualifiers(OB, Quals, true, false); +} + +void TagTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {} + +void ArrayTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const { + ElementType->outputPre(OB, Flags); + outputQualifiers(OB, Quals, true, false); +} + +void ArrayTypeNode::outputOneDimension(OutputBuffer &OB, OutputFlags Flags, + Node *N) const { + assert(N->kind() == NodeKind::IntegerLiteral); + IntegerLiteralNode *ILN = static_cast<IntegerLiteralNode *>(N); + if (ILN->Value != 0) + ILN->output(OB, Flags); +} + +void ArrayTypeNode::outputDimensionsImpl(OutputBuffer &OB, + OutputFlags Flags) const { + if (Dimensions->Count == 0) + return; + + outputOneDimension(OB, Flags, Dimensions->Nodes[0]); + for (size_t I = 1; I < Dimensions->Count; ++I) { + OB << "]["; + outputOneDimension(OB, Flags, Dimensions->Nodes[I]); + } +} + +void ArrayTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const { + OB << "["; + outputDimensionsImpl(OB, Flags); + OB << "]"; + + ElementType->outputPost(OB, Flags); +} + +void SymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const { + Name->output(OB, Flags); +} + +void FunctionSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const { + Signature->outputPre(OB, Flags); + outputSpaceIfNecessary(OB); + Name->output(OB, Flags); + Signature->outputPost(OB, Flags); +} + +void VariableSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const { + const char *AccessSpec = nullptr; + bool IsStatic = true; + switch (SC) { + case StorageClass::PrivateStatic: + AccessSpec = "private"; + break; + case StorageClass::PublicStatic: + AccessSpec = "public"; + break; + case StorageClass::ProtectedStatic: + AccessSpec = "protected"; + break; + default: + IsStatic = false; + break; + } + if (!(Flags & OF_NoAccessSpecifier) && AccessSpec) + OB << AccessSpec << ": "; + if (!(Flags & OF_NoMemberType) && IsStatic) + OB << "static "; + + if (!(Flags & OF_NoVariableType) && Type) { + Type->outputPre(OB, Flags); + outputSpaceIfNecessary(OB); + } + Name->output(OB, Flags); + if (!(Flags & OF_NoVariableType) && Type) + Type->outputPost(OB, Flags); +} + +void CustomTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const { + Identifier->output(OB, Flags); +} +void CustomTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {} + +void QualifiedNameNode::output(OutputBuffer &OB, OutputFlags Flags) const { + Components->output(OB, Flags, "::"); +} + +void RttiBaseClassDescriptorNode::output(OutputBuffer &OB, + OutputFlags Flags) const { + OB << "`RTTI Base Class Descriptor at ("; + OB << NVOffset << ", " << VBPtrOffset << ", " << VBTableOffset << ", " + << this->Flags; + OB << ")'"; +} + +void LocalStaticGuardVariableNode::output(OutputBuffer &OB, + OutputFlags Flags) const { + Name->output(OB, Flags); +} + +void VcallThunkIdentifierNode::output(OutputBuffer &OB, + OutputFlags Flags) const { + OB << "`vcall'{" << OffsetInVTable << ", {flat}}"; +} + +void SpecialTableSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const { + outputQualifiers(OB, Quals, false, true); + Name->output(OB, Flags); + if (TargetName) { + OB << "{for `"; + TargetName->output(OB, Flags); + OB << "'}"; + } +} diff --git a/demumble b/demumble deleted file mode 160000 -Subproject d3e838b9f4ff995506024af51d43be161b98e23 |