diff options
author | 2018-09-11 18:06:31 +0000 | |
---|---|---|
committer | 2018-09-11 18:06:31 +0000 | |
commit | 8a05a458f6e66ba4e39e97f266f4fbfbbdf85f5b (patch) | |
tree | daa979747ce9e345dc0680933d017c4bb0b5768c /lib/libcxxabi/src | |
parent | merge libunwind 6.0.0; ok patrick@, kettenis@ (diff) | |
download | wireguard-openbsd-8a05a458f6e66ba4e39e97f266f4fbfbbdf85f5b.tar.xz wireguard-openbsd-8a05a458f6e66ba4e39e97f266f4fbfbbdf85f5b.zip |
import of libc++abi 6.0.0
Diffstat (limited to 'lib/libcxxabi/src')
26 files changed, 4041 insertions, 1750 deletions
diff --git a/lib/libcxxabi/src/CMakeLists.txt b/lib/libcxxabi/src/CMakeLists.txt index b851b4ac777..240f6d0d103 100644 --- a/lib/libcxxabi/src/CMakeLists.txt +++ b/lib/libcxxabi/src/CMakeLists.txt @@ -1,22 +1,29 @@ # Get sources set(LIBCXXABI_SOURCES - abort_message.cpp + # C++ABI files cxa_aux_runtime.cpp cxa_default_handlers.cpp cxa_demangle.cpp cxa_exception_storage.cpp cxa_guard.cpp cxa_handlers.cpp - cxa_new_delete.cpp cxa_unexpected.cpp cxa_vector.cpp cxa_virtual.cpp - exception.cpp + # C++ STL files + stdlib_exception.cpp + stdlib_stdexcept.cpp + stdlib_typeinfo.cpp + # Internal files + abort_message.cpp + fallback_malloc.cpp private_typeinfo.cpp - stdexcept.cpp - typeinfo.cpp ) +if (LIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS) + list(APPEND LIBCXXABI_SOURCES stdlib_new_delete.cpp) +endif() + if (LIBCXXABI_ENABLE_EXCEPTIONS) list(APPEND LIBCXXABI_SOURCES cxa_exception.cpp) list(APPEND LIBCXXABI_SOURCES cxa_personality.cpp) @@ -24,7 +31,7 @@ else() list(APPEND LIBCXXABI_SOURCES cxa_noexception.cpp) endif() -if (UNIX AND NOT (APPLE OR CYGWIN)) +if (LIBCXXABI_ENABLE_THREADS AND UNIX AND NOT (APPLE OR CYGWIN)) list(APPEND LIBCXXABI_SOURCES cxa_thread_atexit.cpp) endif() @@ -45,24 +52,32 @@ if (LIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL) add_definitions(-DHAVE___CXA_THREAD_ATEXIT_IMPL) endif() -# Generate library list -set(libraries ${LIBCXXABI_CXX_ABI_LIBRARIES}) - if (LIBCXXABI_ENABLE_THREADS) - append_if(libraries LIBCXXABI_HAS_PTHREAD_LIB pthread) + add_library_flags_if(LIBCXXABI_HAS_PTHREAD_LIB pthread) endif() -append_if(libraries LIBCXXABI_HAS_C_LIB c) - +add_library_flags_if(LIBCXXABI_HAS_C_LIB c) if (LIBCXXABI_USE_LLVM_UNWINDER) - list(APPEND libraries unwind) + # Prefer using the in-tree version of libunwind, either shared or static. If + # none are found fall back to using -lunwind. + # FIXME: Is it correct to prefer the static version of libunwind? + if (NOT LIBCXXABI_ENABLE_STATIC_UNWINDER AND (TARGET unwind_shared OR HAVE_LIBUNWIND)) + list(APPEND LIBCXXABI_LIBRARIES unwind_shared) + elseif (LIBCXXABI_ENABLE_STATIC_UNWINDER AND (TARGET unwind_static OR HAVE_LIBUNWIND)) + list(APPEND LIBCXXABI_LIBRARIES unwind_static) + else() + list(APPEND LIBCXXABI_LIBRARIES unwind) + endif() else() - append_if(libraries LIBCXXABI_HAS_GCC_S_LIB gcc_s) + add_library_flags_if(LIBCXXABI_HAS_GCC_S_LIB gcc_s) +endif() +if (MINGW) + # MINGW_LIBRARIES is defined in config-ix.cmake + list(APPEND LIBCXXABI_LIBRARIES ${MINGW_LIBRARIES}) endif() # Setup flags. -append_if(LIBCXXABI_COMPILE_FLAGS LIBCXXABI_HAS_FPIC_FLAG -fPIC) -append_if(LIBCXXABI_LINK_FLAGS LIBCXXABI_HAS_NODEFAULTLIBS_FLAG -nodefaultlibs) +add_link_flags_if_supported(-nodefaultlibs) set(LIBCXXABI_SHARED_LINK_FLAGS) @@ -82,58 +97,108 @@ if ( APPLE ) endif() endif() -string(REPLACE ";" " " LIBCXXABI_COMPILE_FLAGS "${LIBCXXABI_COMPILE_FLAGS}") -string(REPLACE ";" " " LIBCXXABI_LINK_FLAGS "${LIBCXXABI_LINK_FLAGS}") -string(REPLACE ";" " " LIBCXXABI_SHARED_LINK_FLAGS "${LIBCXXABI_SHARED_LINK_FLAGS}") +split_list(LIBCXXABI_COMPILE_FLAGS) +split_list(LIBCXXABI_LINK_FLAGS) +split_list(LIBCXXABI_SHARED_LINK_FLAGS) + +# FIXME: libc++abi.so will not link when modules are enabled because it depends +# on symbols defined in libc++.so which has not yet been built. +if (LLVM_ENABLE_MODULES) + string(REPLACE "-Wl,-z,defs" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") +endif() # Add a object library that contains the compiled source files. add_library(cxxabi_objects OBJECT ${LIBCXXABI_SOURCES} ${LIBCXXABI_HEADERS}) - set_target_properties(cxxabi_objects - PROPERTIES - COMPILE_FLAGS "${LIBCXXABI_COMPILE_FLAGS}" - ) + PROPERTIES + CXX_EXTENSIONS + OFF + CXX_STANDARD + 11 + CXX_STANDARD_REQUIRED + ON + COMPILE_FLAGS + "${LIBCXXABI_COMPILE_FLAGS}" + POSITION_INDEPENDENT_CODE + ON) set(LIBCXXABI_TARGETS) # Build the shared library. if (LIBCXXABI_ENABLE_SHARED) add_library(cxxabi_shared SHARED $<TARGET_OBJECTS:cxxabi_objects>) - target_link_libraries(cxxabi_shared ${libraries}) + target_link_libraries(cxxabi_shared ${LIBCXXABI_LIBRARIES}) set_target_properties(cxxabi_shared - PROPERTIES - LINK_FLAGS "${LIBCXXABI_LINK_FLAGS} ${LIBCXXABI_SHARED_LINK_FLAGS}" - OUTPUT_NAME "c++abi" - VERSION "1.0" - SOVERSION "1" - ) + PROPERTIES + CXX_EXTENSIONS + OFF + CXX_STANDARD + 11 + CXX_STANDARD_REQUIRED + ON + LINK_FLAGS + "${LIBCXXABI_LINK_FLAGS} ${LIBCXXABI_SHARED_LINK_FLAGS}" + OUTPUT_NAME + "c++abi" + POSITION_INDEPENDENT_CODE + ON + SOVERSION + "1" + VERSION + "1.0") list(APPEND LIBCXXABI_TARGETS "cxxabi_shared") endif() # Build the static library. if (LIBCXXABI_ENABLE_STATIC) - add_library(cxxabi_static STATIC $<TARGET_OBJECTS:cxxabi_objects>) - target_link_libraries(cxxabi_static ${libraries}) + set(cxxabi_static_sources $<TARGET_OBJECTS:cxxabi_objects>) + if (LIBCXXABI_USE_LLVM_UNWINDER AND LIBCXXABI_ENABLE_STATIC_UNWINDER) + if (TARGET unwind_static OR HAVE_LIBUNWIND) + list(APPEND cxxabi_static_sources $<TARGET_OBJECTS:unwind_objects>) + endif() + endif() + add_library(cxxabi_static STATIC ${cxxabi_static_sources}) + target_link_libraries(cxxabi_static ${LIBCXXABI_LIBRARIES}) set_target_properties(cxxabi_static - PROPERTIES - LINK_FLAGS "${LIBCXXABI_LINK_FLAGS}" - OUTPUT_NAME "c++abi" - ) + PROPERTIES + CXX_EXTENSIONS + OFF + CXX_STANDARD + 11 + CXX_STANDARD_REQUIRED + ON + LINK_FLAGS + "${LIBCXXABI_LINK_FLAGS}" + OUTPUT_NAME + "c++abi" + POSITION_INDEPENDENT_CODE + ON) list(APPEND LIBCXXABI_TARGETS "cxxabi_static") endif() # Add a meta-target for both libraries. add_custom_target(cxxabi DEPENDS ${LIBCXXABI_TARGETS}) -install(TARGETS ${LIBCXXABI_TARGETS} - LIBRARY DESTINATION lib${LIBCXXABI_LIBDIR_SUFFIX} COMPONENT libcxxabi - ARCHIVE DESTINATION lib${LIBCXXABI_LIBDIR_SUFFIX} COMPONENT libcxxabi - ) +if (LIBCXXABI_INSTALL_LIBRARY) + install(TARGETS ${LIBCXXABI_TARGETS} + LIBRARY DESTINATION ${LIBCXXABI_INSTALL_PREFIX}lib${LIBCXXABI_LIBDIR_SUFFIX} COMPONENT cxxabi + ARCHIVE DESTINATION ${LIBCXXABI_INSTALL_PREFIX}lib${LIBCXXABI_LIBDIR_SUFFIX} COMPONENT cxxabi + ) +endif() -if (NOT CMAKE_CONFIGURATION_TYPES) - add_custom_target(install-libcxxabi +if (NOT CMAKE_CONFIGURATION_TYPES AND LIBCXXABI_INSTALL_LIBRARY) + add_custom_target(install-cxxabi DEPENDS cxxabi COMMAND "${CMAKE_COMMAND}" - -DCMAKE_INSTALL_COMPONENT=libcxxabi + -DCMAKE_INSTALL_COMPONENT=cxxabi -P "${LIBCXXABI_BINARY_DIR}/cmake_install.cmake") + add_custom_target(install-cxxabi-stripped + DEPENDS cxxabi + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=cxxabi + -DCMAKE_INSTALL_DO_STRIP=1 + -P "${LIBCXXABI_BINARY_DIR}/cmake_install.cmake") + + # TODO: This is a legacy target name and should be removed at some point. + add_custom_target(install-libcxxabi DEPENDS install-cxxabi) endif() diff --git a/lib/libcxxabi/src/abort_message.cpp b/lib/libcxxabi/src/abort_message.cpp index 5e25c0f3472..7a2a9f835bd 100644 --- a/lib/libcxxabi/src/abort_message.cpp +++ b/lib/libcxxabi/src/abort_message.cpp @@ -22,8 +22,6 @@ extern "C" void android_set_abort_message(const char* msg); #endif // __ANDROID_API__ >= 21 #endif // __BIONIC__ -#pragma GCC visibility push(hidden) - #ifdef __APPLE__ # if defined(__has_include) && __has_include(<CrashReporterClient.h>) # define HAVE_CRASHREPORTERCLIENT_H @@ -31,10 +29,10 @@ extern "C" void android_set_abort_message(const char* msg); # endif #endif -__attribute__((visibility("hidden"), noreturn)) void abort_message(const char* format, ...) { // write message to stderr +#if !defined(NDEBUG) || !defined(LIBCXXABI_BAREMETAL) #ifdef __APPLE__ fprintf(stderr, "libc++abi.dylib: "); #endif @@ -43,6 +41,7 @@ void abort_message(const char* format, ...) vfprintf(stderr, format, list); va_end(list); fprintf(stderr, "\n"); +#endif #if defined(__APPLE__) && defined(HAVE_CRASHREPORTERCLIENT_H) // record message in crash report @@ -77,5 +76,3 @@ void abort_message(const char* format, ...) abort(); } - -#pragma GCC visibility pop diff --git a/lib/libcxxabi/src/abort_message.h b/lib/libcxxabi/src/abort_message.h index 2c5cb204664..e8f9571cb56 100644 --- a/lib/libcxxabi/src/abort_message.h +++ b/lib/libcxxabi/src/abort_message.h @@ -10,24 +10,18 @@ #ifndef __ABORT_MESSAGE_H_ #define __ABORT_MESSAGE_H_ -#include <stdio.h> - -#pragma GCC visibility push(hidden) +#include "cxxabi.h" #ifdef __cplusplus extern "C" { #endif -__attribute__((visibility("hidden"), noreturn)) - void abort_message(const char* format, ...) - __attribute__((format(printf, 1, 2))); - +_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN void +abort_message(const char *format, ...) __attribute__((format(printf, 1, 2))); #ifdef __cplusplus } #endif -#pragma GCC visibility pop - #endif diff --git a/lib/libcxxabi/src/cxa_aux_runtime.cpp b/lib/libcxxabi/src/cxa_aux_runtime.cpp index bb7c9f1255c..878d3bd034d 100644 --- a/lib/libcxxabi/src/cxa_aux_runtime.cpp +++ b/lib/libcxxabi/src/cxa_aux_runtime.cpp @@ -16,7 +16,7 @@ namespace __cxxabiv1 { extern "C" { -_LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void __cxa_bad_cast(void) { +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void) { #ifndef _LIBCXXABI_NO_EXCEPTIONS throw std::bad_cast(); #else @@ -24,7 +24,7 @@ _LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void __cxa_bad_cast(void) { #endif } -_LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void __cxa_bad_typeid(void) { +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void) { #ifndef _LIBCXXABI_NO_EXCEPTIONS throw std::bad_typeid(); #else @@ -32,7 +32,7 @@ _LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void __cxa_bad_typeid(void) { #endif } -_LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_throw_bad_array_new_length(void) { #ifndef _LIBCXXABI_NO_EXCEPTIONS throw std::bad_array_new_length(); diff --git a/lib/libcxxabi/src/cxa_default_handlers.cpp b/lib/libcxxabi/src/cxa_default_handlers.cpp index 09350e7721a..f2e36b4bc83 100644 --- a/lib/libcxxabi/src/cxa_default_handlers.cpp +++ b/lib/libcxxabi/src/cxa_default_handlers.cpp @@ -12,8 +12,8 @@ #include <stdexcept> #include <new> #include <exception> +#include <cstdlib> #include "abort_message.h" -#include "config.h" // For __sync_swap #include "cxxabi.h" #include "cxa_handlers.hpp" #include "cxa_exception.hpp" @@ -22,7 +22,7 @@ static const char* cause = "uncaught"; __attribute__((noreturn)) -static void default_terminate_handler() +static void demangling_terminate_handler() { // If there might be an uncaught exception using namespace __cxxabiv1; @@ -78,12 +78,19 @@ static void default_terminate_handler() } __attribute__((noreturn)) -static void default_unexpected_handler() +static void demangling_unexpected_handler() { cause = "unexpected"; std::terminate(); } +#if !defined(LIBCXXABI_SILENT_TERMINATE) +static std::terminate_handler default_terminate_handler = demangling_terminate_handler; +static std::terminate_handler default_unexpected_handler = demangling_unexpected_handler; +#else +static std::terminate_handler default_terminate_handler = std::abort; +static std::terminate_handler default_unexpected_handler = std::terminate; +#endif // // Global variables that hold the pointers to the current handler diff --git a/lib/libcxxabi/src/cxa_demangle.cpp b/lib/libcxxabi/src/cxa_demangle.cpp index 25aa741299a..9cfd99607bf 100644 --- a/lib/libcxxabi/src/cxa_demangle.cpp +++ b/lib/libcxxabi/src/cxa_demangle.cpp @@ -7,15 +7,20 @@ // //===----------------------------------------------------------------------===// -#define _LIBCPP_EXTERN_TEMPLATE(...) +// FIXME: (possibly) incomplete list of features that clang mangles that this +// file does not yet support: +// - enable_if attribute +// - decomposition declarations +// - C++ modules TS + #define _LIBCPP_NO_EXCEPTIONS #include "__cxxabi_config.h" #include <vector> #include <algorithm> -#include <string> #include <numeric> +#include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> @@ -42,75 +47,1685 @@ enum success }; -template <class C> - const char* parse_type(const char* first, const char* last, C& db); -template <class C> - const char* parse_encoding(const char* first, const char* last, C& db); -template <class C> - const char* parse_name(const char* first, const char* last, C& db, - bool* ends_with_template_args = 0); -template <class C> - const char* parse_expression(const char* first, const char* last, C& db); -template <class C> - const char* parse_template_args(const char* first, const char* last, C& db); -template <class C> - const char* parse_operator_name(const char* first, const char* last, C& db); -template <class C> - const char* parse_unqualified_name(const char* first, const char* last, C& db); -template <class C> - const char* parse_decltype(const char* first, const char* last, C& db); - -template <class C> -void -print_stack(const C& db) +class StringView { + const char *First; + const char *Last; + +public: + 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() : First(nullptr), Last(nullptr) {} + + StringView substr(size_t From, size_t To) { + if (To >= size()) + To = size() - 1; + if (From >= size()) + From = size() - 1; + return StringView(First + From, First + To); + } + + StringView dropFront(size_t N) const { + if (N >= size()) + N = size() - 1; + return StringView(First + N, Last); + } + + bool startsWith(StringView Str) const { + if (Str.size() > size()) + return false; + return std::equal(Str.begin(), Str.end(), begin()); + } + + 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 operator==(const StringView &LHS, const StringView &RHS) { + return LHS.size() == RHS.size() && + std::equal(LHS.begin(), LHS.end(), RHS.begin()); +} + +// Stream that AST nodes write their string representation into after the AST +// has been parsed. +class OutputStream { + char *Buffer; + size_t CurrentPosition; + size_t BufferCapacity; + + // Ensure there is at least n more positions in buffer. + void grow(size_t N) { + if (N + CurrentPosition >= BufferCapacity) { + BufferCapacity *= 2; + if (BufferCapacity < N + CurrentPosition) + BufferCapacity = N + CurrentPosition; + Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity)); + } + } + +public: + OutputStream(char *StartBuf, size_t Size) + : Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {} + + OutputStream &operator+=(StringView R) { + size_t Size = R.size(); + if (Size == 0) + return *this; + grow(Size); + memmove(Buffer + CurrentPosition, R.begin(), Size); + CurrentPosition += Size; + return *this; + } + + OutputStream &operator+=(char C) { + grow(1); + Buffer[CurrentPosition++] = C; + return *this; + } + + // Offset of position in buffer, used for building stream_string_view. + typedef unsigned StreamPosition; + + // StringView into a stream, used for caching the ast nodes. + class StreamStringView { + StreamPosition First, Last; + + friend class OutputStream; + + public: + StreamStringView() : First(0), Last(0) {} + + StreamStringView(StreamPosition First_, StreamPosition Last_) + : First(First_), Last(Last_) {} + + bool empty() const { return First == Last; } + }; + + OutputStream &operator+=(StreamStringView &s) { + size_t Sz = static_cast<size_t>(s.Last - s.First); + if (Sz == 0) + return *this; + grow(Sz); + memmove(Buffer + CurrentPosition, Buffer + s.First, Sz); + CurrentPosition += Sz; + return *this; + } + + StreamPosition getCurrentPosition() const { + return static_cast<StreamPosition>(CurrentPosition); + } + + StreamStringView makeStringViewFromPastPosition(StreamPosition Pos) { + return StreamStringView(Pos, getCurrentPosition()); + } + + char back() const { + return CurrentPosition ? Buffer[CurrentPosition - 1] : '\0'; + } + + bool empty() const { return CurrentPosition == 0; } + + char *getBuffer() { return Buffer; } + char *getBufferEnd() { return Buffer + CurrentPosition - 1; } + size_t getBufferCapacity() { return BufferCapacity; } +}; + +// 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 { + KDotSuffix, + KVendorExtQualType, + KQualType, + KConversionOperatorType, + KPostfixQualifiedType, + KNameType, + KAbiTagAttr, + KObjCProtoName, + KPointerType, + KLValueReferenceType, + KRValueReferenceType, + KPointerToMemberType, + KArrayType, + KFunctionType, + KTopLevelFunctionDecl, + KFunctionQualType, + KFunctionRefQualType, + KLiteralOperator, + KSpecialName, + KCtorVtableSpecialName, + KQualifiedName, + KEmptyName, + KVectorType, + KTemplateParams, + KNameWithTemplateArgs, + KGlobalQualifiedName, + KStdQualifiedName, + KExpandedSpecialSubstitution, + KSpecialSubstitution, + KCtorDtorName, + KDtorName, + KUnnamedTypeName, + KLambdaTypeName, + KExpr, + }; + + const Kind K; + +private: + // If this Node has any RHS part, potentally many Nodes further down. + const unsigned HasRHSComponent : 1; + const unsigned HasFunction : 1; + const unsigned HasArray : 1; + +public: + Node(Kind K_, bool HasRHS_ = false, bool HasFunction_ = false, + bool HasArray_ = false) + : K(K_), HasRHSComponent(HasRHS_), HasFunction(HasFunction_), + HasArray(HasArray_) {} + + bool hasRHSComponent() const { return HasRHSComponent; } + bool hasArray() const { return HasArray; } + bool hasFunction() const { return HasFunction; } + + void print(OutputStream &s) const { + printLeft(s); + if (hasRHSComponent()) + printRight(s); + } + + // Print the "left" side of this Node into OutputStream. + virtual void printLeft(OutputStream &) const = 0; + + // Print the "right". This distinction is necessary to represent C++ types + // that appear on the RHS of their subtype, such as arrays or functions. + // Since most types don't have such a component, provide a default + // implemenation. + virtual void printRight(OutputStream &) const {} + + virtual StringView getBaseName() const { return StringView(); } + + // Silence compiler warnings, this dtor will never be called. + virtual ~Node() = default; +}; + +class NodeArray { + Node **Elements; + size_t NumElements; + +public: + NodeArray() : NumElements(0) {} + NodeArray(Node **Elements_, size_t NumElements_) + : Elements(Elements_), NumElements(NumElements_) {} + + bool empty() const { return NumElements == 0; } + size_t size() const { return NumElements; } + + void printWithSeperator(OutputStream &S, StringView Seperator) const { + for (size_t Idx = 0; Idx != NumElements; ++Idx) { + if (Idx) + S += Seperator; + Elements[Idx]->print(S); + } + } +}; + +class DotSuffix final : public Node { + const Node *Prefix; + const StringView Suffix; + +public: + DotSuffix(Node *Prefix_, StringView Suffix_) + : Node(KDotSuffix), Prefix(Prefix_), Suffix(Suffix_) {} + + void printLeft(OutputStream &s) const override { + Prefix->print(s); + s += " ("; + s += Suffix; + s += ")"; + } +}; + +class VendorExtQualType final : public Node { + const Node *Ext; + const Node *Ty; + +public: + VendorExtQualType(Node *Ext_, Node *Ty_) + : Node(KVendorExtQualType), Ext(Ext_), Ty(Ty_) {} + + void printLeft(OutputStream &S) const override { + Ext->print(S); + S += " "; + Ty->printLeft(S); + } + + void printRight(OutputStream &S) const override { Ty->printRight(S); } +}; + +enum Qualifiers { + QualNone = 0, + QualConst = 0x1, + QualVolatile = 0x2, + QualRestrict = 0x4, +}; + +void addQualifiers(Qualifiers &Q1, Qualifiers Q2) { + Q1 = static_cast<Qualifiers>(Q1 | Q2); +} + +class QualType : public Node { +protected: + const Qualifiers Quals; + const Node *Child; + + void printQuals(OutputStream &S) const { + if (Quals & QualConst) + S += " const"; + if (Quals & QualVolatile) + S += " volatile"; + if (Quals & QualRestrict) + S += " restrict"; + } + +public: + QualType(Node *Child_, Qualifiers Quals_) + : Node(KQualType, Child_->hasRHSComponent(), Child_->hasFunction(), + Child_->hasArray()), + Quals(Quals_), Child(Child_) {} + + QualType(Node::Kind ChildKind_, Node *Child_, Qualifiers Quals_) + : Node(ChildKind_, Child_->hasRHSComponent(), Child_->hasFunction(), + Child_->hasArray()), + Quals(Quals_), Child(Child_) {} + + void printLeft(OutputStream &S) const override { + Child->printLeft(S); + printQuals(S); + } + + void printRight(OutputStream &S) const override { Child->printRight(S); } +}; + +class ConversionOperatorType final : public Node { + const Node *Ty; + +public: + ConversionOperatorType(Node *Ty_) : Node(KConversionOperatorType), Ty(Ty_) {} + + void printLeft(OutputStream &S) const override { + S += "operator "; + Ty->print(S); + } +}; + +class PostfixQualifiedType final : public Node { + const Node *Ty; + const StringView Postfix; + +public: + PostfixQualifiedType(Node *Ty_, StringView Postfix_) + : Node(KPostfixQualifiedType), Ty(Ty_), Postfix(Postfix_) {} + + void printLeft(OutputStream &s) const override { + Ty->printLeft(s); + s += Postfix; + } + + void printRight(OutputStream &S) const override { Ty->printRight(S); } +}; + +class NameType final : public Node { + const StringView Name; + +public: + NameType(StringView Name_) : Node(KNameType), Name(Name_) {} + + StringView getName() const { return Name; } + StringView getBaseName() const override { return Name; } + + void printLeft(OutputStream &s) const override { s += Name; } +}; + +class AbiTagAttr final : public Node { + const Node* Base; + StringView Tag; +public: + AbiTagAttr(const Node* Base_, StringView Tag_) + : Node(KAbiTagAttr), Base(Base_), Tag(Tag_) {} + + void printLeft(OutputStream &S) const override { + Base->printLeft(S); + S += "[abi:"; + S += Tag; + S += "]"; + } +}; + +class ObjCProtoName : public Node { + Node *Ty; + Node *Protocol; + + friend class PointerType; + +public: + ObjCProtoName(Node *Ty_, Node *Protocol_) + : Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {} + + bool isObjCObject() const { + return Ty->K == KNameType && + static_cast<NameType *>(Ty)->getName() == "objc_object"; + } + + void printLeft(OutputStream &S) const override { + Ty->printLeft(S); + S += "<"; + Protocol->printLeft(S); + S += ">"; + } +}; + +class PointerType final : public Node { + const Node *Pointee; + +public: + PointerType(Node *Pointee_) + : Node(KPointerType, Pointee_->hasRHSComponent()), Pointee(Pointee_) {} + + void printLeft(OutputStream &s) const override { + // We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>. + if (Pointee->K != KObjCProtoName || + !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) { + Pointee->printLeft(s); + if (Pointee->hasArray()) + s += " "; + if (Pointee->hasArray() || Pointee->hasFunction()) + s += "("; + s += "*"; + } else { + const auto *objcProto = static_cast<const ObjCProtoName *>(Pointee); + s += "id<"; + objcProto->Protocol->print(s); + s += ">"; + } + } + + void printRight(OutputStream &s) const override { + if (Pointee->K != KObjCProtoName || + !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) { + if (Pointee->hasArray() || Pointee->hasFunction()) + s += ")"; + Pointee->printRight(s); + } + } +}; + +class LValueReferenceType final : public Node { + const Node *Pointee; + +public: + LValueReferenceType(Node *Pointee_) + : Node(KLValueReferenceType, Pointee_->hasRHSComponent()), + Pointee(Pointee_) {} + + void printLeft(OutputStream &s) const override { + Pointee->printLeft(s); + if (Pointee->hasArray()) + s += " "; + if (Pointee->hasArray() || Pointee->hasFunction()) + s += "(&"; + else + s += "&"; + } + void printRight(OutputStream &s) const override { + if (Pointee->hasArray() || Pointee->hasFunction()) + s += ")"; + Pointee->printRight(s); + } +}; + +class RValueReferenceType final : public Node { + const Node *Pointee; + +public: + RValueReferenceType(Node *Pointee_) + : Node(KRValueReferenceType, Pointee_->hasRHSComponent()), + Pointee(Pointee_) {} + + void printLeft(OutputStream &s) const override { + Pointee->printLeft(s); + if (Pointee->hasArray()) + s += " "; + if (Pointee->hasArray() || Pointee->hasFunction()) + s += "(&&"; + else + s += "&&"; + } + + void printRight(OutputStream &s) const override { + if (Pointee->hasArray() || Pointee->hasFunction()) + s += ")"; + Pointee->printRight(s); + } +}; + +class PointerToMemberType final : public Node { + const Node *ClassType; + const Node *MemberType; + +public: + PointerToMemberType(Node *ClassType_, Node *MemberType_) + : Node(KPointerToMemberType, MemberType_->hasRHSComponent()), + ClassType(ClassType_), MemberType(MemberType_) {} + + void printLeft(OutputStream &s) const override { + MemberType->printLeft(s); + if (MemberType->hasArray() || MemberType->hasFunction()) + s += "("; + else + s += " "; + ClassType->print(s); + s += "::*"; + } + + void printRight(OutputStream &s) const override { + if (MemberType->hasArray() || MemberType->hasFunction()) + s += ")"; + MemberType->printRight(s); + } +}; + +class NodeOrString { + const void *First; + const void *Second; + +public: + /* implicit */ NodeOrString(StringView Str) { + const char *FirstChar = Str.begin(); + const char *SecondChar = Str.end(); + if (SecondChar == nullptr) { + assert(FirstChar == SecondChar); + ++FirstChar, ++SecondChar; + } + First = static_cast<const void *>(FirstChar); + Second = static_cast<const void *>(SecondChar); + } + + /* implicit */ NodeOrString(Node *N) + : First(static_cast<const void *>(N)), Second(nullptr) {} + NodeOrString() : First(nullptr), Second(nullptr) {} + + bool isString() const { return Second && First; } + bool isNode() const { return First && !Second; } + bool isEmpty() const { return !First && !Second; } + + StringView asString() const { + assert(isString()); + return StringView(static_cast<const char *>(First), + static_cast<const char *>(Second)); + } + + const Node *asNode() const { + assert(isNode()); + return static_cast<const Node *>(First); + } +}; + +class ArrayType final : public Node { + Node *Base; + NodeOrString Dimension; + +public: + ArrayType(Node *Base_, NodeOrString Dimension_) + : Node(KArrayType, true, false, true), Base(Base_), Dimension(Dimension_) {} + + // Incomplete array type. + ArrayType(Node *Base_) : Node(KArrayType, true, false, true), Base(Base_) {} + + void printLeft(OutputStream &S) const override { Base->printLeft(S); } + + void printRight(OutputStream &S) const override { + if (S.back() != ']') + S += " "; + S += "["; + if (Dimension.isString()) + S += Dimension.asString(); + else if (Dimension.isNode()) + Dimension.asNode()->print(S); + S += "]"; + Base->printRight(S); + } +}; + +class FunctionType final : public Node { + Node *Ret; + NodeArray Params; + +public: + FunctionType(Node *Ret_, NodeArray Params_) + : Node(KFunctionType, true, true), Ret(Ret_), Params(Params_) {} + + // Handle C++'s ... quirky decl grammer by using the left & right + // distinction. Consider: + // int (*f(float))(char) {} + // f is a function that takes a float and returns a pointer to a function + // that takes a char and returns an int. If we're trying to print f, start + // by printing out the return types's left, then print our parameters, then + // finally print right of the return type. + void printLeft(OutputStream &S) const override { + Ret->printLeft(S); + S += " "; + } + + void printRight(OutputStream &S) const override { + S += "("; + Params.printWithSeperator(S, ", "); + S += ")"; + Ret->printRight(S); + } +}; + +class TopLevelFunctionDecl final : public Node { + const Node *Ret; + const Node *Name; + NodeArray Params; + +public: + TopLevelFunctionDecl(Node *Ret_, Node *Name_, NodeArray Params_) + : Node(KTopLevelFunctionDecl, true, true), Ret(Ret_), Name(Name_), + Params(Params_) {} + + void printLeft(OutputStream &S) const override { + if (Ret) { + Ret->printLeft(S); + if (!Ret->hasRHSComponent()) + S += " "; + } + Name->print(S); + } + + void printRight(OutputStream &S) const override { + S += "("; + Params.printWithSeperator(S, ", "); + S += ")"; + if (Ret) + Ret->printRight(S); + } +}; + +enum FunctionRefQual : unsigned char { + FrefQualNone, + FrefQualLValue, + FrefQualRValue, +}; + +class FunctionRefQualType : public Node { + Node *Fn; + FunctionRefQual Quals; + + friend class FunctionQualType; + +public: + FunctionRefQualType(Node *Fn_, FunctionRefQual Quals_) + : Node(KFunctionRefQualType, true, true), Fn(Fn_), Quals(Quals_) {} + + void printQuals(OutputStream &S) const { + if (Quals == FrefQualLValue) + S += " &"; + else + S += " &&"; + } + + void printLeft(OutputStream &S) const override { Fn->printLeft(S); } + + void printRight(OutputStream &S) const override { + Fn->printRight(S); + printQuals(S); + } +}; + +class FunctionQualType final : public QualType { +public: + FunctionQualType(Node *Child_, Qualifiers Quals_) + : QualType(KFunctionQualType, Child_, Quals_) {} + + void printLeft(OutputStream &S) const override { Child->printLeft(S); } + + void printRight(OutputStream &S) const override { + if (Child->K == KFunctionRefQualType) { + auto *RefQuals = static_cast<const FunctionRefQualType *>(Child); + RefQuals->Fn->printRight(S); + printQuals(S); + RefQuals->printQuals(S); + } else { + Child->printRight(S); + printQuals(S); + } + } +}; + +class LiteralOperator : public Node { + const Node *OpName; + +public: + LiteralOperator(Node *OpName_) : Node(KLiteralOperator), OpName(OpName_) {} + + void printLeft(OutputStream &S) const override { + S += "operator\"\" "; + OpName->print(S); + } +}; + +class SpecialName final : public Node { + const StringView Special; + const Node *Child; + +public: + SpecialName(StringView Special_, Node *Child_) + : Node(KSpecialName), Special(Special_), Child(Child_) {} + + void printLeft(OutputStream &S) const override { + S += Special; + Child->print(S); + } +}; + +class CtorVtableSpecialName final : public Node { + const Node *FirstType; + const Node *SecondType; + +public: + CtorVtableSpecialName(Node *FirstType_, Node *SecondType_) + : Node(KCtorVtableSpecialName), FirstType(FirstType_), + SecondType(SecondType_) {} + + void printLeft(OutputStream &S) const override { + S += "construction vtable for "; + FirstType->print(S); + S += "-in-"; + SecondType->print(S); + } +}; + +class QualifiedName final : public Node { + // qualifier::name + const Node *Qualifier; + const Node *Name; + + mutable OutputStream::StreamStringView Cache; + +public: + QualifiedName(Node *Qualifier_, Node *Name_) + : Node(KQualifiedName), Qualifier(Qualifier_), Name(Name_) {} + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputStream &S) const override { + if (!Cache.empty()) { + S += Cache; + return; + } + + OutputStream::StreamPosition Start = S.getCurrentPosition(); + if (Qualifier->K != KEmptyName) { + Qualifier->print(S); + S += "::"; + } + Name->print(S); + Cache = S.makeStringViewFromPastPosition(Start); + } +}; + +class EmptyName : public Node { +public: + EmptyName() : Node(KEmptyName) {} + void printLeft(OutputStream &) const override {} +}; + +class VectorType final : public Node { + const Node *BaseType; + const NodeOrString Dimension; + const bool IsPixel; + +public: + VectorType(NodeOrString Dimension_) + : Node(KVectorType), BaseType(nullptr), Dimension(Dimension_), + IsPixel(true) {} + VectorType(Node *BaseType_, NodeOrString Dimension_) + : Node(KVectorType), BaseType(BaseType_), Dimension(Dimension_), + IsPixel(false) {} + + void printLeft(OutputStream &S) const override { + if (IsPixel) { + S += "pixel vector["; + S += Dimension.asString(); + S += "]"; + } else { + BaseType->print(S); + S += " vector["; + if (Dimension.isNode()) + Dimension.asNode()->print(S); + else if (Dimension.isString()) + S += Dimension.asString(); + S += "]"; + } + } +}; + +class TemplateParams final : public Node { + NodeArray Params; + + mutable OutputStream::StreamStringView Cache; + +public: + TemplateParams(NodeArray Params_) : Node(KTemplateParams), Params(Params_) {} + + void printLeft(OutputStream &S) const override { + if (!Cache.empty()) { + S += Cache; + return; + } + + OutputStream::StreamPosition Start = S.getCurrentPosition(); + + S += "<"; + Params.printWithSeperator(S, ", "); + if (S.back() == '>') + S += " "; + S += ">"; + + Cache = S.makeStringViewFromPastPosition(Start); + } +}; + +class NameWithTemplateArgs final : public Node { + // name<template_args> + Node *Name; + Node *TemplateArgs; + +public: + NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_) + : Node(KNameWithTemplateArgs), Name(Name_), TemplateArgs(TemplateArgs_) {} + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputStream &S) const override { + Name->print(S); + TemplateArgs->print(S); + } +}; + +class GlobalQualifiedName final : public Node { + Node *Child; + +public: + GlobalQualifiedName(Node *Child_) : Node(KGlobalQualifiedName), Child(Child_) {} + + StringView getBaseName() const override { return Child->getBaseName(); } + + void printLeft(OutputStream &S) const override { + S += "::"; + Child->print(S); + } +}; + +class StdQualifiedName final : public Node { + Node *Child; + +public: + StdQualifiedName(Node *Child_) : Node(KStdQualifiedName), Child(Child_) {} + + StringView getBaseName() const override { return Child->getBaseName(); } + + void printLeft(OutputStream &S) const override { + S += "std::"; + Child->print(S); + } +}; + +enum class SpecialSubKind { + allocator, + basic_string, + string, + istream, + ostream, + iostream, +}; + +class ExpandedSpecialSubstitution final : public Node { + SpecialSubKind SSK; + +public: + ExpandedSpecialSubstitution(SpecialSubKind SSK_) + : Node(KExpandedSpecialSubstitution), SSK(SSK_) {} + + 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"); + } + _LIBCPP_UNREACHABLE(); + } + + void printLeft(OutputStream &S) const override { + switch (SSK) { + case SpecialSubKind::allocator: + S += "std::basic_string<char, std::char_traits<char>, " + "std::allocator<char> >"; + break; + case SpecialSubKind::basic_string: + case SpecialSubKind::string: + S += "std::basic_string<char, std::char_traits<char>, " + "std::allocator<char> >"; + break; + case SpecialSubKind::istream: + S += "std::basic_istream<char, std::char_traits<char> >"; + break; + case SpecialSubKind::ostream: + S += "std::basic_ostream<char, std::char_traits<char> >"; + break; + case SpecialSubKind::iostream: + S += "std::basic_iostream<char, std::char_traits<char> >"; + break; + } + } +}; + +class SpecialSubstitution final : public Node { +public: + SpecialSubKind SSK; + + SpecialSubstitution(SpecialSubKind SSK_) + : Node(KSpecialSubstitution), SSK(SSK_) {} + + StringView getBaseName() const override { + switch (SSK) { + case SpecialSubKind::allocator: + return StringView("allocator"); + case SpecialSubKind::basic_string: + return StringView("basic_string"); + case SpecialSubKind::string: + return StringView("string"); + case SpecialSubKind::istream: + return StringView("istream"); + case SpecialSubKind::ostream: + return StringView("ostream"); + case SpecialSubKind::iostream: + return StringView("iostream"); + } + _LIBCPP_UNREACHABLE(); + } + + void printLeft(OutputStream &S) const override { + switch (SSK) { + case SpecialSubKind::allocator: + S += "std::allocator"; + break; + case SpecialSubKind::basic_string: + S += "std::basic_string"; + break; + case SpecialSubKind::string: + S += "std::string"; + break; + case SpecialSubKind::istream: + S += "std::istream"; + break; + case SpecialSubKind::ostream: + S += "std::ostream"; + break; + case SpecialSubKind::iostream: + S += "std::iostream"; + break; + } + } +}; + +class CtorDtorName final : public Node { + const Node *Basename; + const bool IsDtor; + +public: + CtorDtorName(Node *Basename_, bool IsDtor_) + : Node(KCtorDtorName), Basename(Basename_), IsDtor(IsDtor_) {} + + void printLeft(OutputStream &S) const override { + if (IsDtor) + S += "~"; + S += Basename->getBaseName(); + } +}; + +class DtorName : public Node { + const Node *Base; + +public: + DtorName(Node *Base_) : Node(KDtorName), Base(Base_) {} + + void printLeft(OutputStream &S) const override { + S += "~"; + Base->printLeft(S); + } +}; + +class UnnamedTypeName : public Node { + const StringView Count; + +public: + UnnamedTypeName(StringView Count_) : Node(KUnnamedTypeName), Count(Count_) {} + + void printLeft(OutputStream &S) const override { + S += "'unnamed"; + S += Count; + S += "\'"; + } +}; + +class LambdaTypeName : public Node { + NodeArray Params; + StringView Count; + +public: + LambdaTypeName(NodeArray Params_, StringView Count_) + : Node(KLambdaTypeName), Params(Params_), Count(Count_) {} + + void printLeft(OutputStream &S) const override { + S += "\'lambda"; + S += Count; + S += "\'("; + Params.printWithSeperator(S, ", "); + S += ")"; + } +}; + +// -- Expression Nodes -- + +struct Expr : public Node { + Expr() : Node(KExpr) {} +}; + +class BinaryExpr : public Expr { + const Node *LHS; + const StringView InfixOperator; + const Node *RHS; + +public: + BinaryExpr(Node *LHS_, StringView InfixOperator_, Node *RHS_) + : LHS(LHS_), InfixOperator(InfixOperator_), RHS(RHS_) {} + + void printLeft(OutputStream &S) const override { + // might be a template argument expression, then we need to disambiguate + // with parens. + if (InfixOperator == ">") + S += "("; + + S += "("; + LHS->print(S); + S += ") "; + S += InfixOperator; + S += " ("; + RHS->print(S); + S += ")"; + + if (InfixOperator == ">") + S += ")"; + } +}; + +class ArraySubscriptExpr : public Expr { + const Node *Op1; + const Node *Op2; + +public: + ArraySubscriptExpr(Node *Op1_, Node *Op2_) : Op1(Op1_), Op2(Op2_) {} + + void printLeft(OutputStream &S) const override { + S += "("; + Op1->print(S); + S += ")["; + Op2->print(S); + S += "]"; + } +}; + +class PostfixExpr : public Expr { + const Node *Child; + const StringView Operand; + +public: + PostfixExpr(Node *Child_, StringView Operand_) + : Child(Child_), Operand(Operand_) {} + + void printLeft(OutputStream &S) const override { + S += "("; + Child->print(S); + S += ")"; + S += Operand; + } +}; + +class ConditionalExpr : public Expr { + const Node *Cond; + const Node *Then; + const Node *Else; + +public: + ConditionalExpr(Node *Cond_, Node *Then_, Node *Else_) + : Cond(Cond_), Then(Then_), Else(Else_) {} + + void printLeft(OutputStream &S) const override { + S += "("; + Cond->print(S); + S += ") ? ("; + Then->print(S); + S += ") : ("; + Else->print(S); + S += ")"; + } +}; + +class MemberExpr : public Expr { + const Node *LHS; + const StringView Kind; + const Node *RHS; + +public: + MemberExpr(Node *LHS_, StringView Kind_, Node *RHS_) + : LHS(LHS_), Kind(Kind_), RHS(RHS_) {} + + void printLeft(OutputStream &S) const override { + LHS->print(S); + S += Kind; + RHS->print(S); + } +}; + +class EnclosingExpr : public Expr { + const StringView Prefix; + const Node *Infix; + const StringView Postfix; + +public: + EnclosingExpr(StringView Prefix_, Node *Infix_, StringView Postfix_) + : Prefix(Prefix_), Infix(Infix_), Postfix(Postfix_) {} + + void printLeft(OutputStream &S) const override { + S += Prefix; + Infix->print(S); + S += Postfix; + } +}; + +class CastExpr : public Expr { + // cast_kind<to>(from) + const StringView CastKind; + const Node *To; + const Node *From; + +public: + CastExpr(StringView CastKind_, Node *To_, Node *From_) + : CastKind(CastKind_), To(To_), From(From_) {} + + void printLeft(OutputStream &S) const override { + S += CastKind; + S += "<"; + To->printLeft(S); + S += ">("; + From->printLeft(S); + S += ")"; + } +}; + +class SizeofParamPackExpr : public Expr { + NodeArray Args; + +public: + SizeofParamPackExpr(NodeArray Args_) : Args(Args_) {} + + void printLeft(OutputStream &S) const override { + S += "sizeof...("; + Args.printWithSeperator(S, ", "); + S += ")"; + } +}; + +class CallExpr : public Expr { + const Node *Callee; + NodeArray Args; + +public: + CallExpr(Node *Callee_, NodeArray Args_) : Callee(Callee_), Args(Args_) {} + + void printLeft(OutputStream &S) const override { + Callee->print(S); + S += "("; + Args.printWithSeperator(S, ", "); + S += ")"; + } +}; + +class NewExpr : public Expr { + // 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_) + : ExprList(ExprList_), Type(Type_), InitList(InitList_), IsGlobal(IsGlobal_), + IsArray(IsArray_) {} + + void printLeft(OutputStream &S) const override { + if (IsGlobal) + S += "::operator "; + S += "new"; + if (IsArray) + S += "[]"; + if (!ExprList.empty()) { + S += "("; + ExprList.printWithSeperator(S, ", "); + S += ")"; + } + Type->print(S); + if (!InitList.empty()) { + S += "("; + InitList.printWithSeperator(S, ", "); + S += ")"; + } + } +}; + +class DeleteExpr : public Expr { + Node *Op; + bool IsGlobal; + bool IsArray; + +public: + DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_) + : Op(Op_), IsGlobal(IsGlobal_), IsArray(IsArray_) {} + + void printLeft(OutputStream &S) const override { + if (IsGlobal) + S += "::"; + S += "delete"; + if (IsArray) + S += "[] "; + Op->print(S); + } +}; + +class PrefixExpr : public Expr { + StringView Prefix; + Node *Child; + +public: + PrefixExpr(StringView Prefix_, Node *Child_) : Prefix(Prefix_), Child(Child_) {} + + void printLeft(OutputStream &S) const override { + S += Prefix; + S += "("; + Child->print(S); + S += ")"; + } +}; + +class FunctionParam : public Expr { + StringView Number; + +public: + FunctionParam(StringView Number_) : Number(Number_) {} + + void printLeft(OutputStream &S) const override { + S += "fp"; + S += Number; + } +}; + +class ExprList : public Expr { + NodeArray SubExprs; + +public: + ExprList(NodeArray SubExprs_) : SubExprs(SubExprs_) {} + + void printLeft(OutputStream &S) const override { + S += "("; + SubExprs.printWithSeperator(S, ", "); + S += ")"; + } +}; + +class ConversionExpr : public Expr { + NodeArray Expressions; + NodeArray Types; + +public: + ConversionExpr(NodeArray Expressions_, NodeArray Types_) + : Expressions(Expressions_), Types(Types_) {} + + void printLeft(OutputStream &S) const override { + S += "("; + Expressions.printWithSeperator(S, ", "); + S += ")("; + Types.printWithSeperator(S, ", "); + S += ")"; + } +}; + +class ThrowExpr : public Expr { + const Node *Op; + +public: + ThrowExpr(Node *Op_) : Op(Op_) {} + + void printLeft(OutputStream &S) const override { + S += "throw "; + Op->print(S); + } +}; + +class BoolExpr : public Expr { + bool Value; + +public: + BoolExpr(bool Value_) : Value(Value_) {} + + void printLeft(OutputStream &S) const override { + S += Value ? StringView("true") : StringView("false"); + } +}; + +class IntegerCastExpr : public Expr { + // ty(integer) + Node *Ty; + StringView Integer; + +public: + IntegerCastExpr(Node *Ty_, StringView Integer_) : Ty(Ty_), Integer(Integer_) {} + + void printLeft(OutputStream &S) const override { + S += "("; + Ty->print(S); + S += ")"; + S += Integer; + } +}; + +class IntegerExpr : public Expr { + StringView Type; + StringView Value; + +public: + IntegerExpr(StringView Type_, StringView Value_) : Type(Type_), Value(Value_) {} + + void printLeft(OutputStream &S) const override { + if (Type.size() > 3) { + S += "("; + S += Type; + S += ")"; + } + + if (Value[0] == 'n') { + S += "-"; + S += Value.dropFront(1); + } else + S += Value; + + if (Type.size() <= 3) + S += Type; + } +}; + +template <class Float> struct FloatData; + +template <class Float> class FloatExpr : public Expr { + const StringView Contents; + +public: + FloatExpr(StringView Contents_) : Contents(Contents_) {} + + void printLeft(OutputStream &s) 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); + s += StringView(num, num + n); + } + } +}; + +class BumpPointerAllocator { + struct BlockMeta { + BlockMeta* Next; + size_t Current; + }; + + static constexpr size_t AllocSize = 4096; + static constexpr size_t UsableAllocSize = AllocSize - sizeof(BlockMeta); + + alignas(16) char InitialBuffer[AllocSize]; + BlockMeta* BlockList = nullptr; + + void grow() { + char* NewMeta = new char[AllocSize]; + BlockList = new (NewMeta) BlockMeta{BlockList, 0}; + } + + void* allocateMassive(size_t NBytes) { + NBytes += sizeof(BlockMeta); + BlockMeta* NewMeta = reinterpret_cast<BlockMeta*>(new char[NBytes]); + 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); + } + + ~BumpPointerAllocator() { + while (BlockList) { + BlockMeta* Tmp = BlockList; + BlockList = BlockList->Next; + if (reinterpret_cast<char*>(Tmp) != InitialBuffer) + delete[] reinterpret_cast<char*>(Tmp); + } + } +}; + +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; + T* Last; + T* Cap; + T Inline[N]; + + 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))); + std::copy(First, Last, Tmp); + First = Tmp; + } else + First = static_cast<T*>(std::realloc(First, NewCap * sizeof(T))); + Last = First + S; + Cap = First + NewCap; + } + +public: + PODSmallVector() : First(Inline), Last(First), Cap(Inline + N) {} + + PODSmallVector(const PODSmallVector&) = delete; + PODSmallVector& operator=(const PODSmallVector&) = delete; + + PODSmallVector(PODSmallVector&& Other) : PODSmallVector() { + if (Other.isInline()) { + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return; + } + + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + } + + PODSmallVector& operator=(PODSmallVector&& Other) { + if (Other.isInline()) { + if (!isInline()) { + std::free(First); + clearInline(); + } + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return *this; + } + + if (isInline()) { + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + return *this; + } + + std::swap(First, Other.First); + std::swap(Last, Other.Last); + std::swap(Cap, Other.Cap); + Other.clear(); + return *this; + } + + void push_back(const T& Elem) { + if (Last == Cap) + reserve(size() * 2); + *Last++ = Elem; + } + + void pop_back() { + assert(Last != First && "Popping empty vector!"); + --Last; + } + + void dropBack(size_t Index) { + assert(Index <= size() && "dropBack() can't expand!"); + Last = First + Index; + } + + T* begin() { return First; } + T* end() { return Last; } + + bool empty() const { return First == Last; } + size_t size() const { return static_cast<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); + } +}; + +// Substitution table. This type is used to track the substitutions that are +// known by the parser. +template <size_t Size> +class SubstitutionTable { + // Substitutions hold the actual entries in the table, and PackIndices tells + // us which entries are members of which pack. For example, if the + // substitutions we're tracking are: {int, {float, FooBar}, char}, with + // {float, FooBar} being a parameter pack, we represent the substitutions as: + // Substitutions: int, float, FooBar, char + // PackIndices: 0, 1, 3 + // So, PackIndicies[I] holds the offset of the begin of the Ith pack, and + // PackIndices[I + 1] holds the offset of the end. + PODSmallVector<Node*, Size> Substitutions; + PODSmallVector<unsigned, Size> PackIndices; + +public: + // Add a substitution that represents a single name to the table. This is + // modeled as a parameter pack with just one element. + void pushSubstitution(Node* Entry) { + pushPack(); + pushSubstitutionIntoPack(Entry); + } + + // Add a new empty pack to the table. Subsequent calls to + // pushSubstitutionIntoPack() will add to this pack. + void pushPack() { + PackIndices.push_back(static_cast<unsigned>(Substitutions.size())); + } + void pushSubstitutionIntoPack(Node* Entry) { + assert(!PackIndices.empty() && "No pack to push substitution into!"); + Substitutions.push_back(Entry); + } + + // Remove the last pack from the table. + void popPack() { + unsigned Last = PackIndices.back(); + PackIndices.pop_back(); + Substitutions.dropBack(Last); + } + + // For use in a range-for loop. + struct NodeRange { + Node** First; + Node** Last; + Node** begin() { return First; } + Node** end() { return Last; } + }; + + // Retrieve the Nth substitution. This is represented as a range, as the + // substitution could be referring to a parameter pack. + NodeRange nthSubstitution(size_t N) { + assert(PackIndices[N] <= Substitutions.size()); + // The Nth parameter pack starts at offset PackIndices[N], and ends at + // PackIndices[N + 1]. + Node** Begin = Substitutions.begin() + PackIndices[N]; + Node** End; + if (N + 1 != PackIndices.size()) { + assert(PackIndices[N + 1] <= Substitutions.size()); + End = Substitutions.begin() + PackIndices[N + 1]; + } else + End = Substitutions.end(); + assert(Begin <= End); + return NodeRange{Begin, End}; + } + + size_t size() const { return PackIndices.size(); } + bool empty() const { return PackIndices.empty(); } + void clear() { + Substitutions.clear(); + PackIndices.clear(); + } +}; + +struct Db { - fprintf(stderr, "---------\n"); - fprintf(stderr, "names:\n"); - for (auto& s : db.names) - fprintf(stderr, "{%s#%s}\n", s.first.c_str(), s.second.c_str()); - int i = -1; - fprintf(stderr, "subs:\n"); - for (auto& v : db.subs) + // Name stack, this is used by the parser to hold temporary names that were + // parsed. The parser colapses 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 42nd entry in this table. + SubstitutionTable<32> Subs; + + // 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. + SubstitutionTable<4> TemplateParams; + + Qualifiers CV = QualNone; + FunctionRefQual RefQuals = FrefQualNone; + unsigned EncodingDepth = 0; + bool ParsedCtorDtorCV = false; + bool TagTemplates = true; + bool FixForwardReferences = false; + bool TryToParseTemplateArgs = true; + + BumpPointerAllocator ASTAllocator; + + template <class T, class... Args> T* make(Args&& ...args) { - if (i >= 0) - fprintf(stderr, "S%i_ = {", i); - else - fprintf(stderr, "S_ = {"); - for (auto& s : v) - fprintf(stderr, "{%s#%s}", s.first.c_str(), s.second.c_str()); - fprintf(stderr, "}\n"); - ++i; + return new (ASTAllocator.allocate(sizeof(T))) + T(std::forward<Args>(args)...); } - fprintf(stderr, "template_param:\n"); - for (auto& t : db.template_param) + + template <class It> NodeArray makeNodeArray(It begin, It end) { - fprintf(stderr, "--\n"); - i = -1; - for (auto& v : t) - { - if (i >= 0) - fprintf(stderr, "T%i_ = {", i); - else - fprintf(stderr, "T_ = {"); - for (auto& s : v) - fprintf(stderr, "{%s#%s}", s.first.c_str(), s.second.c_str()); - fprintf(stderr, "}\n"); - ++i; - } + size_t sz = static_cast<size_t>(end - begin); + void* mem = ASTAllocator.allocate(sizeof(Node*) * sz); + Node** data = new (mem) Node*[sz]; + std::copy(begin, end, data); + return NodeArray(data, sz); } - fprintf(stderr, "---------\n\n"); -} -template <class C> -void -print_state(const char* msg, const char* first, const char* last, const C& db) -{ - fprintf(stderr, "%s: ", msg); - for (; first != last; ++first) - fprintf(stderr, "%c", *first); - fprintf(stderr, "\n"); - print_stack(db); -} + NodeArray popTrailingNodeArray(size_t FromPosition) + { + assert(FromPosition <= Names.size()); + NodeArray res = makeNodeArray( + Names.begin() + (long)FromPosition, Names.end()); + Names.dropBack(FromPosition); + return res; + } +}; + +const char* parse_type(const char* first, const char* last, Db& db); +const char* parse_encoding(const char* first, const char* last, Db& db); +const char* parse_name(const char* first, const char* last, Db& db, + bool* ends_with_template_args = 0); +const char* parse_expression(const char* first, const char* last, Db& db); +const char* parse_template_args(const char* first, const char* last, Db& db); +const char* parse_operator_name(const char* first, const char* last, Db& db); +const char* parse_unqualified_name(const char* first, const char* last, Db& db); +const char* parse_decltype(const char* first, const char* last, Db& db); // <number> ::= [n] <non-negative decimal integer> @@ -140,30 +1755,30 @@ parse_number(const char* first, const char* last) } template <class Float> -struct float_data; +struct FloatData; template <> -struct float_data<float> +struct FloatData<float> { static const size_t mangled_size = 8; static const size_t max_demangled_size = 24; static constexpr const char* spec = "%af"; }; -constexpr const char* float_data<float>::spec; +constexpr const char* FloatData<float>::spec; template <> -struct float_data<double> +struct FloatData<double> { static const size_t mangled_size = 16; static const size_t max_demangled_size = 32; static constexpr const char* spec = "%a"; }; -constexpr const char* float_data<double>::spec; +constexpr const char* FloatData<double>::spec; template <> -struct float_data<long double> +struct FloatData<long double> { #if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \ defined(__wasm__) @@ -177,55 +1792,34 @@ struct float_data<long double> static constexpr const char* spec = "%LaL"; }; -constexpr const char* float_data<long double>::spec; +constexpr const char* FloatData<long double>::spec; -template <class Float, class C> +template <class Float> const char* -parse_floating_number(const char* first, const char* last, C& db) +parse_floating_number(const char* first, const char* last, Db& db) { - const size_t N = float_data<Float>::mangled_size; - if (static_cast<std::size_t>(last - first) > N) + const size_t N = FloatData<Float>::mangled_size; + if (static_cast<std::size_t>(last - first) <= N) + return first; + last = first + N; + const char* t = first; + for (; t != last; ++t) { - last = first + N; - union - { - Float value; - char buf[sizeof(Float)]; - }; - const char* t = first; - char* e = buf; - for (; t != last; ++t, ++e) - { - if (!isxdigit(*t)) - return first; - 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 (*t == 'E') - { -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - std::reverse(buf, e); -#endif - char num[float_data<Float>::max_demangled_size] = {0}; - int n = snprintf(num, sizeof(num), float_data<Float>::spec, value); - if (static_cast<std::size_t>(n) >= sizeof(num)) - return first; - db.names.push_back(typename C::String(num, static_cast<std::size_t>(n))); - first = t+1; - } + if (!isxdigit(*t)) + return first; + } + if (*t == 'E') + { + db.Names.push_back( + db.make<FloatExpr<Float>>(StringView(first, t))); + first = t + 1; } return first; } -// <source-name> ::= <positive length number> <identifier> - -template <class C> +// <positive length number> ::= [0-9]* const char* -parse_source_name(const char* first, const char* last, C& db) +parse_positive_integer(const char* first, const char* last, size_t* out) { if (first != last) { @@ -240,15 +1834,53 @@ parse_source_name(const char* first, const char* last, C& db) if (++t == last) return first; } - if (static_cast<size_t>(last - t) >= n) - { - typename C::String r(t, n); - if (r.substr(0, 10) == "_GLOBAL__N") - db.names.push_back("(anonymous namespace)"); - else - db.names.push_back(std::move(r)); - first = t + n; - } + *out = n; + first = t; + } + } + return first; +} + +// extension +// <abi-tag-seq> ::= <abi-tag>* +// <abi-tag> ::= B <positive length number> <identifier> +const char* +parse_abi_tag_seq(const char* first, const char* last, Db& db) +{ + while (first != last && *first == 'B' && first+1 != last) + { + size_t length; + const char* t = parse_positive_integer(first+1, last, &length); + if (t == first+1) + return first; + if (static_cast<size_t>(last - t) < length || db.Names.empty()) + return first; + db.Names.back() = db.make<AbiTagAttr>( + db.Names.back(), StringView(t, t + length)); + first = t + length; + } + return first; +} + +// <source-name> ::= <positive length number> <identifier> [<abi-tag-seq>] +const char* +parse_source_name(const char* first, const char* last, Db& db) +{ + if (first != last) + { + size_t length; + const char* t = parse_positive_integer(first, last, &length); + if (t == first) + return first; + if (static_cast<size_t>(last - t) >= length) + { + StringView r(t, t + length); + if (r.substr(0, 10) == "_GLOBAL__N") + db.Names.push_back(db.make<NameType>("(anonymous namespace)")); + else + db.Names.push_back(db.make<NameType>(r)); + first = t + length; + first = parse_abi_tag_seq(first, last, db); } } return first; @@ -265,9 +1897,8 @@ parse_source_name(const char* first, const char* last, C& db) // <substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> > // <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> > -template <class C> const char* -parse_substitution(const char* first, const char* last, C& db) +parse_substitution(const char* first, const char* last, Db& db) { if (last - first >= 2) { @@ -276,34 +1907,39 @@ parse_substitution(const char* first, const char* last, C& db) switch (first[1]) { case 'a': - db.names.push_back("std::allocator"); + db.Names.push_back( + db.make<SpecialSubstitution>( + SpecialSubKind::allocator)); first += 2; break; case 'b': - db.names.push_back("std::basic_string"); + db.Names.push_back( + db.make<SpecialSubstitution>(SpecialSubKind::basic_string)); first += 2; break; case 's': - db.names.push_back("std::string"); + db.Names.push_back( + db.make<SpecialSubstitution>( + SpecialSubKind::string)); first += 2; break; case 'i': - db.names.push_back("std::istream"); + db.Names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::istream)); first += 2; break; case 'o': - db.names.push_back("std::ostream"); + db.Names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::ostream)); first += 2; break; case 'd': - db.names.push_back("std::iostream"); + db.Names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::iostream)); first += 2; break; case '_': - if (!db.subs.empty()) + if (!db.Subs.empty()) { - for (const auto& n : db.subs.front()) - db.names.push_back(n); + for (Node* n : db.Subs.nthSubstitution(0)) + db.Names.push_back(n); first += 2; } break; @@ -327,10 +1963,10 @@ parse_substitution(const char* first, const char* last, C& db) if (t == last || *t != '_') return first; ++sub; - if (sub < db.subs.size()) + if (sub < db.Subs.size()) { - for (const auto& n : db.subs[sub]) - db.names.push_back(n); + for (Node* n : db.Subs.nthSubstitution(sub)) + db.Names.push_back(n); first = t+1; } } @@ -373,96 +2009,95 @@ parse_substitution(const char* first, const char* last, C& db) // ::= Dn # std::nullptr_t (i.e., decltype(nullptr)) // ::= u <source-name> # vendor extended type -template <class C> const char* -parse_builtin_type(const char* first, const char* last, C& db) +parse_builtin_type(const char* first, const char* last, Db& db) { if (first != last) { switch (*first) { case 'v': - db.names.push_back("void"); + db.Names.push_back(db.make<NameType>("void")); ++first; break; case 'w': - db.names.push_back("wchar_t"); + db.Names.push_back(db.make<NameType>("wchar_t")); ++first; break; case 'b': - db.names.push_back("bool"); + db.Names.push_back(db.make<NameType>("bool")); ++first; break; case 'c': - db.names.push_back("char"); + db.Names.push_back(db.make<NameType>("char")); ++first; break; case 'a': - db.names.push_back("signed char"); + db.Names.push_back(db.make<NameType>("signed char")); ++first; break; case 'h': - db.names.push_back("unsigned char"); + db.Names.push_back(db.make<NameType>("unsigned char")); ++first; break; case 's': - db.names.push_back("short"); + db.Names.push_back(db.make<NameType>("short")); ++first; break; case 't': - db.names.push_back("unsigned short"); + db.Names.push_back(db.make<NameType>("unsigned short")); ++first; break; case 'i': - db.names.push_back("int"); + db.Names.push_back(db.make<NameType>("int")); ++first; break; case 'j': - db.names.push_back("unsigned int"); + db.Names.push_back(db.make<NameType>("unsigned int")); ++first; break; case 'l': - db.names.push_back("long"); + db.Names.push_back(db.make<NameType>("long")); ++first; break; case 'm': - db.names.push_back("unsigned long"); + db.Names.push_back(db.make<NameType>("unsigned long")); ++first; break; case 'x': - db.names.push_back("long long"); + db.Names.push_back(db.make<NameType>("long long")); ++first; break; case 'y': - db.names.push_back("unsigned long long"); + db.Names.push_back(db.make<NameType>("unsigned long long")); ++first; break; case 'n': - db.names.push_back("__int128"); + db.Names.push_back(db.make<NameType>("__int128")); ++first; break; case 'o': - db.names.push_back("unsigned __int128"); + db.Names.push_back(db.make<NameType>("unsigned __int128")); ++first; break; case 'f': - db.names.push_back("float"); + db.Names.push_back(db.make<NameType>("float")); ++first; break; case 'd': - db.names.push_back("double"); + db.Names.push_back(db.make<NameType>("double")); ++first; break; case 'e': - db.names.push_back("long double"); + db.Names.push_back(db.make<NameType>("long double")); ++first; break; case 'g': - db.names.push_back("__float128"); + db.Names.push_back(db.make<NameType>("__float128")); ++first; break; case 'z': - db.names.push_back("..."); + db.Names.push_back(db.make<NameType>("...")); ++first; break; case 'u': @@ -478,39 +2113,39 @@ parse_builtin_type(const char* first, const char* last, C& db) switch (first[1]) { case 'd': - db.names.push_back("decimal64"); + db.Names.push_back(db.make<NameType>("decimal64")); first += 2; break; case 'e': - db.names.push_back("decimal128"); + db.Names.push_back(db.make<NameType>("decimal128")); first += 2; break; case 'f': - db.names.push_back("decimal32"); + db.Names.push_back(db.make<NameType>("decimal32")); first += 2; break; case 'h': - db.names.push_back("decimal16"); + db.Names.push_back(db.make<NameType>("decimal16")); first += 2; break; case 'i': - db.names.push_back("char32_t"); + db.Names.push_back(db.make<NameType>("char32_t")); first += 2; break; case 's': - db.names.push_back("char16_t"); + db.Names.push_back(db.make<NameType>("char16_t")); first += 2; break; case 'a': - db.names.push_back("auto"); + db.Names.push_back(db.make<NameType>("auto")); first += 2; break; case 'c': - db.names.push_back("decltype(auto)"); + db.Names.push_back(db.make<NameType>("decltype(auto)")); first += 2; break; case 'n': - db.names.push_back("std::nullptr_t"); + db.Names.push_back(db.make<NameType>("std::nullptr_t")); first += 2; break; } @@ -521,27 +2156,27 @@ parse_builtin_type(const char* first, const char* last, C& db) return first; } -// <CV-qualifiers> ::= [r] [V] [K] +// <CV-Qualifiers> ::= [r] [V] [K] const char* -parse_cv_qualifiers(const char* first, const char* last, unsigned& cv) +parse_cv_qualifiers(const char* first, const char* last, Qualifiers& cv) { - cv = 0; + cv = QualNone; if (first != last) { if (*first == 'r') { - cv |= 4; + addQualifiers(cv, QualRestrict); ++first; } if (*first == 'V') { - cv |= 2; + addQualifiers(cv, QualVolatile); ++first; } if (*first == 'K') { - cv |= 1; + addQualifiers(cv, QualConst); ++first; } } @@ -551,9 +2186,8 @@ parse_cv_qualifiers(const char* first, const char* last, unsigned& cv) // <template-param> ::= T_ # first template parameter // ::= T <parameter-2 non-negative number> _ -template <class C> const char* -parse_template_param(const char* first, const char* last, C& db) +parse_template_param(const char* first, const char* last, Db& db) { if (last - first >= 2) { @@ -561,19 +2195,17 @@ parse_template_param(const char* first, const char* last, C& db) { if (first[1] == '_') { - if (db.template_param.empty()) - return first; - if (!db.template_param.back().empty()) + if (!db.TemplateParams.empty()) { - for (auto& t : db.template_param.back().front()) - db.names.push_back(t); + for (Node *t : db.TemplateParams.nthSubstitution(0)) + db.Names.push_back(t); first += 2; } else { - db.names.push_back("T_"); + db.Names.push_back(db.make<NameType>("T_")); first += 2; - db.fix_forward_references = true; + db.FixForwardReferences = true; } } else if (isdigit(first[1])) @@ -585,20 +2217,21 @@ parse_template_param(const char* first, const char* last, C& db) sub *= 10; sub += static_cast<size_t>(*t - '0'); } - if (t == last || *t != '_' || db.template_param.empty()) + if (t == last || *t != '_') return first; ++sub; - if (sub < db.template_param.back().size()) + if (sub < db.TemplateParams.size()) { - for (auto& temp : db.template_param.back()[sub]) - db.names.push_back(temp); + for (Node *temp : db.TemplateParams.nthSubstitution(sub)) + db.Names.push_back(temp); first = t+1; } else { - db.names.push_back(typename C::String(first, t+1)); + db.Names.push_back( + db.make<NameType>(StringView(first, t + 1))); first = t+1; - db.fix_forward_references = true; + db.FixForwardReferences = true; } } } @@ -608,9 +2241,8 @@ parse_template_param(const char* first, const char* last, C& db) // cc <type> <expression> # const_cast<type> (expression) -template <class C> const char* -parse_const_cast_expr(const char* first, const char* last, C& db) +parse_const_cast_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 'c' && first[1] == 'c') { @@ -620,13 +2252,14 @@ parse_const_cast_expr(const char* first, const char* last, C& db) const char* t1 = parse_expression(t, last, db); if (t1 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto expr = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + auto from_expr = db.Names.back(); + db.Names.pop_back(); + if (db.Names.empty()) return first; - db.names.back() = "const_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + db.Names.back() = db.make<CastExpr>( + "const_cast", db.Names.back(), from_expr); first = t1; } } @@ -636,9 +2269,8 @@ parse_const_cast_expr(const char* first, const char* last, C& db) // dc <type> <expression> # dynamic_cast<type> (expression) -template <class C> const char* -parse_dynamic_cast_expr(const char* first, const char* last, C& db) +parse_dynamic_cast_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 'd' && first[1] == 'c') { @@ -648,13 +2280,14 @@ parse_dynamic_cast_expr(const char* first, const char* last, C& db) const char* t1 = parse_expression(t, last, db); if (t1 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto expr = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + auto from_expr = db.Names.back(); + db.Names.pop_back(); + if (db.Names.empty()) return first; - db.names.back() = "dynamic_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + db.Names.back() = db.make<CastExpr>( + "dynamic_cast", db.Names.back(), from_expr); first = t1; } } @@ -664,9 +2297,8 @@ parse_dynamic_cast_expr(const char* first, const char* last, C& db) // rc <type> <expression> # reinterpret_cast<type> (expression) -template <class C> const char* -parse_reinterpret_cast_expr(const char* first, const char* last, C& db) +parse_reinterpret_cast_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 'r' && first[1] == 'c') { @@ -676,13 +2308,14 @@ parse_reinterpret_cast_expr(const char* first, const char* last, C& db) const char* t1 = parse_expression(t, last, db); if (t1 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto expr = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + auto from_expr = db.Names.back(); + db.Names.pop_back(); + if (db.Names.empty()) return first; - db.names.back() = "reinterpret_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + db.Names.back() = db.make<CastExpr>( + "reinterpret_cast", db.Names.back(), from_expr); first = t1; } } @@ -692,9 +2325,8 @@ parse_reinterpret_cast_expr(const char* first, const char* last, C& db) // sc <type> <expression> # static_cast<type> (expression) -template <class C> const char* -parse_static_cast_expr(const char* first, const char* last, C& db) +parse_static_cast_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 's' && first[1] == 'c') { @@ -704,11 +2336,12 @@ parse_static_cast_expr(const char* first, const char* last, C& db) const char* t1 = parse_expression(t, last, db); if (t1 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto expr = db.names.back().move_full(); - db.names.pop_back(); - db.names.back() = "static_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + auto from_expr = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = db.make<CastExpr>( + "static_cast", db.Names.back(), from_expr); first = t1; } } @@ -718,9 +2351,8 @@ parse_static_cast_expr(const char* first, const char* last, C& db) // sp <expression> # pack expansion -template <class C> const char* -parse_pack_expansion(const char* first, const char* last, C& db) +parse_pack_expansion(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 's' && first[1] == 'p') { @@ -733,18 +2365,18 @@ parse_pack_expansion(const char* first, const char* last, C& db) // st <type> # sizeof (a type) -template <class C> const char* -parse_sizeof_type_expr(const char* first, const char* last, C& db) +parse_sizeof_type_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 's' && first[1] == 't') { const char* t = parse_type(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; + db.Names.back() = db.make<EnclosingExpr>( + "sizeof (", db.Names.back(), ")"); first = t; } } @@ -753,18 +2385,18 @@ parse_sizeof_type_expr(const char* first, const char* last, C& db) // sz <expr> # sizeof (a expression) -template <class C> const char* -parse_sizeof_expr_expr(const char* first, const char* last, C& db) +parse_sizeof_expr_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 's' && first[1] == 'z') { const char* t = parse_expression(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; + db.Names.back() = db.make<EnclosingExpr>( + "sizeof (", db.Names.back(), ")"); first = t; } } @@ -773,60 +2405,50 @@ parse_sizeof_expr_expr(const char* first, const char* last, C& db) // sZ <template-param> # size of a parameter pack -template <class C> const char* -parse_sizeof_param_pack_expr(const char* first, const char* last, C& db) +parse_sizeof_param_pack_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'T') { - size_t k0 = db.names.size(); + size_t k0 = db.Names.size(); const char* t = parse_template_param(first+2, last, db); - size_t k1 = db.names.size(); - if (t != first+2) + size_t k1 = db.Names.size(); + if (t != first+2 && k0 <= k1) { - typename C::String tmp("sizeof...("); - size_t k = k0; - if (k != k1) - { - tmp += db.names[k].move_full(); - for (++k; k != k1; ++k) - tmp += ", " + db.names[k].move_full(); - } - tmp += ")"; - for (; k1 != k0; --k1) - db.names.pop_back(); - db.names.push_back(std::move(tmp)); + Node* sizeof_expr = db.make<SizeofParamPackExpr>( + db.popTrailingNodeArray(k0)); + db.Names.push_back(sizeof_expr); first = t; } } return first; } -// <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 +// <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 -template <class C> const char* -parse_function_param(const char* first, const char* last, C& db) +parse_function_param(const char* first, const char* last, Db& db) { if (last - first >= 3 && *first == 'f') { if (first[1] == 'p') { - unsigned cv; + Qualifiers cv; const char* t = parse_cv_qualifiers(first+2, last, cv); const char* t1 = parse_number(t, last); if (t1 != last && *t1 == '_') { - db.names.push_back("fp" + typename C::String(t, t1)); + db.Names.push_back( + db.make<FunctionParam>(StringView(t, t1))); first = t1+1; } } else if (first[1] == 'L') { - unsigned cv; + Qualifiers cv; const char* t0 = parse_number(first+2, last); if (t0 != last && *t0 == 'p') { @@ -835,7 +2457,8 @@ parse_function_param(const char* first, const char* last, C& db) const char* t1 = parse_number(t, last); if (t1 != last && *t1 == '_') { - db.names.push_back("fp" + typename C::String(t, t1)); + db.Names.push_back( + db.make<FunctionParam>(StringView(t, t1))); first = t1+1; } } @@ -846,18 +2469,18 @@ parse_function_param(const char* first, const char* last, C& db) // sZ <function-param> # size of a function parameter pack -template <class C> const char* -parse_sizeof_function_param_pack_expr(const char* first, const char* last, C& db) +parse_sizeof_function_param_pack_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'f') { const char* t = parse_function_param(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back() = "sizeof...(" + db.names.back().move_full() + ")"; + db.Names.back() = db.make<EnclosingExpr>( + "sizeof...(", db.Names.back(), ")"); first = t; } } @@ -867,9 +2490,8 @@ parse_sizeof_function_param_pack_expr(const char* first, const char* last, C& db // te <expression> # typeid (expression) // ti <type> # typeid (type) -template <class C> const char* -parse_typeid_expr(const char* first, const char* last, C& db) +parse_typeid_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 't' && (first[1] == 'e' || first[1] == 'i')) { @@ -880,9 +2502,10 @@ parse_typeid_expr(const char* first, const char* last, C& db) t = parse_type(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back() = "typeid(" + db.names.back().move_full() + ")"; + db.Names.back() = db.make<EnclosingExpr>( + "typeid(", db.Names.back(), ")"); first = t; } } @@ -891,18 +2514,17 @@ parse_typeid_expr(const char* first, const char* last, C& db) // tw <expression> # throw expression -template <class C> const char* -parse_throw_expr(const char* first, const char* last, C& db) +parse_throw_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 't' && first[1] == 'w') { const char* t = parse_expression(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back() = "throw " + db.names.back().move_full(); + db.Names.back() = db.make<ThrowExpr>(db.Names.back()); first = t; } } @@ -911,9 +2533,8 @@ parse_throw_expr(const char* first, const char* last, C& db) // ds <expression> <expression> # expr.*expr -template <class C> const char* -parse_dot_star_expr(const char* first, const char* last, C& db) +parse_dot_star_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 'd' && first[1] == 's') { @@ -923,11 +2544,12 @@ parse_dot_star_expr(const char* first, const char* last, C& db) const char* t1 = parse_expression(t, last, db); if (t1 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto expr = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += ".*" + expr; + auto rhs_expr = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = db.make<MemberExpr>( + db.Names.back(), ".*", rhs_expr); first = t1; } } @@ -937,9 +2559,8 @@ parse_dot_star_expr(const char* first, const char* last, C& db) // <simple-id> ::= <source-name> [ <template-args> ] -template <class C> const char* -parse_simple_id(const char* first, const char* last, C& db) +parse_simple_id(const char* first, const char* last, Db& db) { if (first != last) { @@ -949,11 +2570,12 @@ parse_simple_id(const char* first, const char* last, C& db) const char* t1 = parse_template_args(t, last, db); if (t1 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto args = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += std::move(args); + auto args = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = + db.make<NameWithTemplateArgs>(db.Names.back(), args); } first = t1; } @@ -967,9 +2589,8 @@ parse_simple_id(const char* first, const char* last, C& db) // ::= <decltype> // ::= <substitution> -template <class C> const char* -parse_unresolved_type(const char* first, const char* last, C& db) +parse_unresolved_type(const char* first, const char* last, Db& db) { if (first != last) { @@ -978,18 +2599,18 @@ parse_unresolved_type(const char* first, const char* last, C& db) { case 'T': { - size_t k0 = db.names.size(); + size_t k0 = db.Names.size(); t = parse_template_param(first, last, db); - size_t k1 = db.names.size(); + size_t k1 = db.Names.size(); if (t != first && k1 == k0 + 1) { - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Subs.pushSubstitution(db.Names.back()); first = t; } else { for (; k1 != k0; --k1) - db.names.pop_back(); + db.Names.pop_back(); } break; } @@ -997,9 +2618,9 @@ parse_unresolved_type(const char* first, const char* last, C& db) t = parse_decltype(first, last, db); if (t != first) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Subs.pushSubstitution(db.Names.back()); first = t; } break; @@ -1014,10 +2635,11 @@ parse_unresolved_type(const char* first, const char* last, C& db) t = parse_unqualified_name(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "std::"); - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Names.back() = + db.make<StdQualifiedName>(db.Names.back()); + db.Subs.pushSubstitution(db.Names.back()); first = t; } } @@ -1031,9 +2653,8 @@ parse_unresolved_type(const char* first, const char* last, C& db) // <destructor-name> ::= <unresolved-type> # e.g., ~T or ~decltype(f()) // ::= <simple-id> # e.g., ~A<2*N> -template <class C> const char* -parse_destructor_name(const char* first, const char* last, C& db) +parse_destructor_name(const char* first, const char* last, Db& db) { if (first != last) { @@ -1042,9 +2663,9 @@ parse_destructor_name(const char* first, const char* last, C& db) t = parse_simple_id(first, last, db); if (t != first) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "~"); + db.Names.back() = db.make<DtorName>(db.Names.back()); first = t; } } @@ -1059,9 +2680,8 @@ parse_destructor_name(const char* first, const char* last, C& db) // ::= dn <destructor-name> # destructor or pseudo-destructor; // # e.g. ~X or ~X<N-1> -template <class C> const char* -parse_base_unresolved_name(const char* first, const char* last, C& db) +parse_base_unresolved_name(const char* first, const char* last, Db& db) { if (last - first >= 2) { @@ -1075,11 +2695,13 @@ parse_base_unresolved_name(const char* first, const char* last, C& db) first = parse_template_args(t, last, db); if (first != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto args = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += std::move(args); + auto args = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = + db.make<NameWithTemplateArgs>( + db.Names.back(), args); } } } @@ -1101,11 +2723,13 @@ parse_base_unresolved_name(const char* first, const char* last, C& db) first = parse_template_args(t, last, db); if (first != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto args = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += std::move(args); + auto args = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = + db.make<NameWithTemplateArgs>( + db.Names.back(), args); } } } @@ -1118,9 +2742,8 @@ parse_base_unresolved_name(const char* first, const char* last, C& db) // <unresolved-qualifier-level> ::= <simple-id> -template <class C> const char* -parse_unresolved_qualifier_level(const char* first, const char* last, C& db) +parse_unresolved_qualifier_level(const char* first, const char* last, Db& db) { return parse_simple_id(first, last, db); } @@ -1135,9 +2758,8 @@ parse_unresolved_qualifier_level(const char* first, const char* last, C& db) // # T::N::x /decltype(p)::N::x // (ignored) ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name> -template <class C> const char* -parse_unresolved_name(const char* first, const char* last, C& db) +parse_unresolved_name(const char* first, const char* last, Db& db) { if (last - first > 2) { @@ -1153,9 +2775,10 @@ parse_unresolved_name(const char* first, const char* last, C& db) { if (global) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "::"); + db.Names.back() = + db.make<GlobalQualifiedName>(db.Names.back()); } first = t2; } @@ -1171,41 +2794,44 @@ parse_unresolved_name(const char* first, const char* last, C& db) t1 = parse_template_args(t, last, db); if (t1 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto args = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += std::move(args); + auto args = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = db.make<NameWithTemplateArgs>( + db.Names.back(), args); t = t1; if (t == last) { - db.names.pop_back(); + db.Names.pop_back(); return first; } } while (*t != 'E') { t1 = parse_unresolved_qualifier_level(t, last, db); - if (t1 == t || t1 == last || db.names.size() < 2) + if (t1 == t || t1 == last || db.Names.size() < 2) return first; - auto s = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += "::" + std::move(s); + auto s = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = + db.make<QualifiedName>(db.Names.back(), s); t = t1; } ++t; t1 = parse_base_unresolved_name(t, last, db); if (t1 == t) { - if (!db.names.empty()) - db.names.pop_back(); + if (!db.Names.empty()) + db.Names.pop_back(); return first; } - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto s = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += "::" + std::move(s); + auto s = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = + db.make<QualifiedName>(db.Names.back(), s); first = t1; } else @@ -1218,25 +2844,28 @@ parse_unresolved_name(const char* first, const char* last, C& db) t1 = parse_template_args(t, last, db); if (t1 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto args = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += std::move(args); + auto args = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = + db.make<NameWithTemplateArgs>( + db.Names.back(), args); t = t1; } t1 = parse_base_unresolved_name(t, last, db); if (t1 == t) { - if (!db.names.empty()) - db.names.pop_back(); + if (!db.Names.empty()) + db.Names.pop_back(); return first; } - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto s = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += "::" + std::move(s); + auto s = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = + db.make<QualifiedName>(db.Names.back(), s); first = t1; } else @@ -1247,33 +2876,37 @@ parse_unresolved_name(const char* first, const char* last, C& db) t = t1; if (global) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "::"); + db.Names.back() = + db.make<GlobalQualifiedName>( + db.Names.back()); } while (*t != 'E') { t1 = parse_unresolved_qualifier_level(t, last, db); - if (t1 == t || t1 == last || db.names.size() < 2) + if (t1 == t || t1 == last || db.Names.size() < 2) return first; - auto s = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += "::" + std::move(s); + auto s = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = db.make<QualifiedName>( + db.Names.back(), s); t = t1; } ++t; t1 = parse_base_unresolved_name(t, last, db); if (t1 == t) { - if (!db.names.empty()) - db.names.pop_back(); + if (!db.Names.empty()) + db.Names.pop_back(); return first; } - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto s = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += "::" + std::move(s); + auto s = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = + db.make<QualifiedName>(db.Names.back(), s); first = t1; } } @@ -1284,9 +2917,8 @@ parse_unresolved_name(const char* first, const char* last, C& db) // dt <expression> <unresolved-name> # expr.name -template <class C> const char* -parse_dot_expr(const char* first, const char* last, C& db) +parse_dot_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 'd' && first[1] == 't') { @@ -1296,13 +2928,13 @@ parse_dot_expr(const char* first, const char* last, C& db) const char* t1 = parse_unresolved_name(t, last, db); if (t1 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto name = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + auto name = db.Names.back(); + db.Names.pop_back(); + if (db.Names.empty()) return first; - db.names.back().first += "." + name; + db.Names.back() = db.make<MemberExpr>(db.Names.back(), ".", name); first = t1; } } @@ -1312,51 +2944,31 @@ parse_dot_expr(const char* first, const char* last, C& db) // cl <expression>+ E # call -template <class C> const char* -parse_call_expr(const char* first, const char* last, C& db) +parse_call_expr(const char* first, const char* last, Db& db) { if (last - first >= 4 && first[0] == 'c' && first[1] == 'l') { const char* t = parse_expression(first+2, last, db); - if (t != first+2) + if (t == last || t == first + 2 || db.Names.empty()) + return first; + Node* callee = db.Names.back(); + db.Names.pop_back(); + size_t args_begin = db.Names.size(); + while (*t != 'E') { - if (t == last) - return first; - if (db.names.empty()) - return first; - db.names.back().first += db.names.back().second; - db.names.back().second = typename C::String(); - db.names.back().first.append("("); - bool first_expr = true; - while (*t != 'E') - { - const char* t1 = parse_expression(t, last, db); - if (t1 == t || t1 == last) - return first; - if (db.names.empty()) - return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - if (!tmp.empty()) - { - if (db.names.empty()) - return first; - if (!first_expr) - { - db.names.back().first.append(", "); - first_expr = false; - } - db.names.back().first.append(tmp); - } - t = t1; - } - ++t; - if (db.names.empty()) + const char* t1 = parse_expression(t, last, db); + if (t1 == last || t1 == t) return first; - db.names.back().first.append(")"); - first = t; + t = t1; } + if (db.Names.size() < args_begin) + return first; + ++t; + CallExpr* the_call = db.make<CallExpr>( + callee, db.popTrailingNodeArray(args_begin)); + db.Names.push_back(the_call); + first = t; } return first; } @@ -1367,9 +2979,8 @@ parse_call_expr(const char* first, const char* last, C& db) // [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) // <initializer> ::= pi <expression>* E # parenthesized initialization -template <class C> const char* -parse_new_expr(const char* first, const char* last, C& db) +parse_new_expr(const char* first, const char* last, Db& db) { if (last - first >= 4) { @@ -1386,31 +2997,18 @@ parse_new_expr(const char* first, const char* last, C& db) t += 2; if (t == last) return first; - bool has_expr_list = false; - bool first_expr = true; + size_t first_expr_in_list = db.Names.size(); + NodeArray ExprList, init_list; while (*t != '_') { const char* t1 = parse_expression(t, last, db); if (t1 == t || t1 == last) return first; - has_expr_list = true; - if (!first_expr) - { - if (db.names.empty()) - return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - if (!tmp.empty()) - { - if (db.names.empty()) - return first; - db.names.back().first.append(", "); - db.names.back().first.append(tmp); - first_expr = false; - } - } t = t1; } + if (first_expr_in_list > db.Names.size()) + return first; + ExprList = db.popTrailingNodeArray(first_expr_in_list); ++t; const char* t1 = parse_type(t, last, db); if (t1 == t || t1 == last) @@ -1421,65 +3019,25 @@ parse_new_expr(const char* first, const char* last, C& db) { t += 2; has_init = true; - first_expr = true; + size_t init_list_begin = db.Names.size(); while (*t != 'E') { t1 = parse_expression(t, last, db); if (t1 == t || t1 == last) return first; - if (!first_expr) - { - if (db.names.empty()) - return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - if (!tmp.empty()) - { - if (db.names.empty()) - return first; - db.names.back().first.append(", "); - db.names.back().first.append(tmp); - first_expr = false; - } - } t = t1; } - } - if (*t != 'E') - return first; - typename C::String init_list; - if (has_init) - { - if (db.names.empty()) + if (init_list_begin > db.Names.size()) return first; - init_list = db.names.back().move_full(); - db.names.pop_back(); + init_list = db.popTrailingNodeArray(init_list_begin); } - if (db.names.empty()) + if (*t != 'E' || db.Names.empty()) return first; - auto type = db.names.back().move_full(); - db.names.pop_back(); - typename C::String expr_list; - if (has_expr_list) - { - if (db.names.empty()) - return first; - expr_list = db.names.back().move_full(); - db.names.pop_back(); - } - typename C::String r; - if (parsed_gs) - r = "::"; - if (is_array) - r += "[] "; - else - r += " "; - if (has_expr_list) - r += "(" + expr_list + ") "; - r += type; - if (has_init) - r += " (" + init_list + ")"; - db.names.push_back(std::move(r)); + auto type = db.Names.back(); + db.Names.pop_back(); + db.Names.push_back( + db.make<NewExpr>(ExprList, type, init_list, + parsed_gs, is_array)); first = t+1; } } @@ -1489,18 +3047,19 @@ parse_new_expr(const char* first, const char* last, C& db) // cv <type> <expression> # conversion with one argument // cv <type> _ <expression>* E # conversion with a different number of arguments -template <class C> const char* -parse_conversion_expr(const char* first, const char* last, C& db) +parse_conversion_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 'c' && first[1] == 'v') { - bool try_to_parse_template_args = db.try_to_parse_template_args; - db.try_to_parse_template_args = false; + bool TryToParseTemplateArgs = db.TryToParseTemplateArgs; + db.TryToParseTemplateArgs = false; + size_t type_begin = db.Names.size(); const char* t = parse_type(first+2, last, db); - db.try_to_parse_template_args = try_to_parse_template_args; + db.TryToParseTemplateArgs = TryToParseTemplateArgs; if (t != first+2 && t != last) { + size_t expr_list_begin = db.Names.size(); if (*t != '_') { const char* t1 = parse_expression(t, last, db); @@ -1513,41 +3072,30 @@ parse_conversion_expr(const char* first, const char* last, C& db) ++t; if (t == last) return first; - if (*t == 'E') - db.names.emplace_back(); - else + if (*t != 'E') { - bool first_expr = true; while (*t != 'E') { const char* t1 = parse_expression(t, last, db); if (t1 == t || t1 == last) return first; - if (!first_expr) - { - if (db.names.empty()) - return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - if (!tmp.empty()) - { - if (db.names.empty()) - return first; - db.names.back().first.append(", "); - db.names.back().first.append(tmp); - first_expr = false; - } - } t = t1; } } ++t; } - if (db.names.size() < 2) + if (db.Names.size() < expr_list_begin || + type_begin > expr_list_begin) return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - db.names.back() = "(" + db.names.back().move_full() + ")(" + tmp + ")"; + NodeArray expressions = db.makeNodeArray( + db.Names.begin() + (long)expr_list_begin, db.Names.end()); + NodeArray types = db.makeNodeArray( + db.Names.begin() + (long)type_begin, + db.Names.begin() + (long)expr_list_begin); + auto* conv_expr = db.make<ConversionExpr>( + types, expressions); + db.Names.dropBack(type_begin); + db.Names.push_back(conv_expr); first = t; } } @@ -1556,9 +3104,8 @@ parse_conversion_expr(const char* first, const char* last, C& db) // pt <expression> <expression> # expr->name -template <class C> const char* -parse_arrow_expr(const char* first, const char* last, C& db) +parse_arrow_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 'p' && first[1] == 't') { @@ -1568,12 +3115,12 @@ parse_arrow_expr(const char* first, const char* last, C& db) const char* t1 = parse_expression(t, last, db); if (t1 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += "->"; - db.names.back().first += tmp; + auto tmp = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = db.make<MemberExpr>( + db.Names.back(), "->", tmp); first = t1; } } @@ -1586,9 +3133,8 @@ parse_arrow_expr(const char* first, const char* last, C& db) // <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] E -template <class C> const char* -parse_function_type(const char* first, const char* last, C& db) +parse_function_type(const char* first, const char* last, Db& db) { if (first != last && *first == 'F') { @@ -1602,16 +3148,19 @@ parse_function_type(const char* first, const char* last, C& db) return first; } const char* t1 = parse_type(t, last, db); - if (t1 != t) + if (t1 != t && !db.Names.empty()) { + Node* ret_type = db.Names.back(); + db.Names.pop_back(); + size_t params_begin = db.Names.size(); t = t1; - typename C::String sig("("); - int ref_qual = 0; + FunctionRefQual RefQuals = FrefQualNone; while (true) { if (t == last) { - db.names.pop_back(); + if (!db.Names.empty()) + db.Names.pop_back(); return first; } if (*t == 'E') @@ -1626,45 +3175,30 @@ parse_function_type(const char* first, const char* last, C& db) } if (*t == 'R' && t+1 != last && t[1] == 'E') { - ref_qual = 1; + RefQuals = FrefQualLValue; ++t; continue; } if (*t == 'O' && t+1 != last && t[1] == 'E') { - ref_qual = 2; + RefQuals = FrefQualRValue; ++t; continue; } - size_t k0 = db.names.size(); + size_t k0 = db.Names.size(); t1 = parse_type(t, last, db); - size_t k1 = db.names.size(); - if (t1 == t || t1 == last) + size_t k1 = db.Names.size(); + if (t1 == t || t1 == last || k1 < k0) return first; - for (size_t k = k0; k < k1; ++k) - { - if (sig.size() > 1) - sig += ", "; - sig += db.names[k].move_full(); - } - for (size_t k = k0; k < k1; ++k) - db.names.pop_back(); t = t1; } - sig += ")"; - switch (ref_qual) - { - case 1: - sig += " &"; - break; - case 2: - sig += " &&"; - break; - } - if (db.names.empty()) + if (db.Names.empty() || params_begin > db.Names.size()) return first; - db.names.back().first += " "; - db.names.back().second.insert(0, sig); + Node* fty = db.make<FunctionType>( + ret_type, db.popTrailingNodeArray(params_begin)); + if (RefQuals) + fty = db.make<FunctionRefQualType>(fty, RefQuals); + db.Names.push_back(fty); first = t; } } @@ -1674,9 +3208,8 @@ parse_function_type(const char* first, const char* last, C& db) // <pointer-to-member-type> ::= M <class type> <member type> -template <class C> const char* -parse_pointer_to_member_type(const char* first, const char* last, C& db) +parse_pointer_to_member_type(const char* first, const char* last, Db& db) { if (first != last && *first == 'M') { @@ -1686,21 +3219,13 @@ parse_pointer_to_member_type(const char* first, const char* last, C& db) const char* t2 = parse_type(t, last, db); if (t2 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto func = std::move(db.names.back()); - db.names.pop_back(); - auto class_type = std::move(db.names.back()); - if (!func.second.empty() && func.second.front() == '(') - { - db.names.back().first = std::move(func.first) + "(" + class_type.move_full() + "::*"; - db.names.back().second = ")" + std::move(func.second); - } - else - { - db.names.back().first = std::move(func.first) + " " + class_type.move_full() + "::*"; - db.names.back().second = std::move(func.second); - } + auto func = std::move(db.Names.back()); + db.Names.pop_back(); + auto ClassType = std::move(db.Names.back()); + db.Names.back() = + db.make<PointerToMemberType>(ClassType, func); first = t2; } } @@ -1711,9 +3236,8 @@ parse_pointer_to_member_type(const char* first, const char* last, C& db) // <array-type> ::= A <positive dimension number> _ <element type> // ::= A [<dimension expression>] _ <element type> -template <class C> const char* -parse_array_type(const char* first, const char* last, C& db) +parse_array_type(const char* first, const char* last, Db& db) { if (first != last && *first == 'A' && first+1 != last) { @@ -1722,11 +3246,9 @@ parse_array_type(const char* first, const char* last, C& db) const char* t = parse_type(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - if (db.names.back().second.substr(0, 2) == " [") - db.names.back().second.erase(0, 1); - db.names.back().second.insert(0, " []"); + db.Names.back() = db.make<ArrayType>(db.Names.back()); first = t; } } @@ -1738,11 +3260,11 @@ parse_array_type(const char* first, const char* last, C& db) const char* t2 = parse_type(t+1, last, db); if (t2 != t+1) { - if (db.names.empty()) + if (db.Names.empty()) return first; - if (db.names.back().second.substr(0, 2) == " [") - db.names.back().second.erase(0, 1); - db.names.back().second.insert(0, " [" + typename C::String(first+1, t) + "]"); + db.Names.back() = + db.make<ArrayType>(db.Names.back(), + StringView(first + 1, t)); first = t2; } } @@ -1755,15 +3277,13 @@ parse_array_type(const char* first, const char* last, C& db) const char* t2 = parse_type(++t, last, db); if (t2 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto type = std::move(db.names.back()); - db.names.pop_back(); - auto expr = std::move(db.names.back()); - db.names.back().first = std::move(type.first); - if (type.second.substr(0, 2) == " [") - type.second.erase(0, 1); - db.names.back().second = " [" + expr.move_full() + "]" + std::move(type.second); + auto base_type = std::move(db.Names.back()); + db.Names.pop_back(); + auto dimension_expr = std::move(db.Names.back()); + db.Names.back() = + db.make<ArrayType>(base_type, dimension_expr); first = t2; } } @@ -1775,9 +3295,8 @@ parse_array_type(const char* first, const char* last, C& db) // <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 <class C> const char* -parse_decltype(const char* first, const char* last, C& db) +parse_decltype(const char* first, const char* last, Db& db) { if (last - first >= 4 && first[0] == 'D') { @@ -1789,9 +3308,10 @@ parse_decltype(const char* first, const char* last, C& db) const char* t = parse_expression(first+2, last, db); if (t != first+2 && t != last && *t == 'E') { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back() = "decltype(" + db.names.back().move_full() + ")"; + db.Names.back() = db.make<EnclosingExpr>( + "decltype(", db.Names.back(), ")"); first = t+1; } } @@ -1808,9 +3328,8 @@ parse_decltype(const char* first, const char* last, C& db) // <extended element type> ::= <element type> // ::= p # AltiVec vector pixel -template <class C> const char* -parse_vector_type(const char* first, const char* last, C& db) +parse_vector_type(const char* first, const char* last, Db& db) { if (last - first > 3 && first[0] == 'D' && first[1] == 'v') { @@ -1828,33 +3347,36 @@ parse_vector_type(const char* first, const char* last, C& db) const char* t1 = parse_type(t, last, db); if (t1 != t) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first += " vector[" + typename C::String(num, sz) + "]"; + db.Names.back() = + db.make<VectorType>(db.Names.back(), + StringView(num, num + sz)); first = t1; } } else { ++t; - db.names.push_back("pixel vector[" + typename C::String(num, sz) + "]"); + db.Names.push_back( + db.make<VectorType>(StringView(num, num + sz))); first = t; } } } else { - typename C::String num; + Node* num = nullptr; const char* t1 = first+2; if (*t1 != '_') { const char* t = parse_expression(t1, last, db); if (t != t1) { - if (db.names.empty()) + if (db.Names.empty()) return first; - num = db.names.back().move_full(); - db.names.pop_back(); + num = db.Names.back(); + db.Names.pop_back(); t1 = t; } } @@ -1863,11 +3385,17 @@ parse_vector_type(const char* first, const char* last, C& db) const char* t = parse_type(t1, last, db); if (t != t1) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first += " vector[" + num + "]"; + if (num) + db.Names.back() = + db.make<VectorType>(db.Names.back(), num); + else + db.Names.back() = + db.make<VectorType>(db.Names.back(), StringView()); first = t; - } + } else if (num) + db.Names.push_back(num); } } } @@ -1883,7 +3411,7 @@ parse_vector_type(const char* first, const char* last, C& db) // ::= <template-template-param> <template-args> // ::= <decltype> // ::= <substitution> -// ::= <CV-qualifiers> <type> +// ::= <CV-Qualifiers> <type> // ::= P <type> # pointer-to // ::= R <type> # reference-to // ::= O <type> # rvalue reference-to (C++0x) @@ -1897,9 +3425,8 @@ parse_vector_type(const char* first, const char* last, C& db) // <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 <class C> const char* -parse_type(const char* first, const char* last, C& db) +parse_type(const char* first, const char* last, Db& db) { if (first != last) { @@ -1909,51 +3436,30 @@ parse_type(const char* first, const char* last, C& db) case 'V': case 'K': { - unsigned cv = 0; + Qualifiers cv = QualNone; const char* t = parse_cv_qualifiers(first, last, cv); if (t != first) { bool is_function = *t == 'F'; - size_t k0 = db.names.size(); + size_t k0 = db.Names.size(); const char* t1 = parse_type(t, last, db); - size_t k1 = db.names.size(); + size_t k1 = db.Names.size(); if (t1 != t) { if (is_function) - db.subs.pop_back(); - db.subs.emplace_back(db.names.get_allocator()); + db.Subs.popPack(); + db.Subs.pushPack(); for (size_t k = k0; k < k1; ++k) { - if (is_function) - { - size_t p = db.names[k].second.size(); - if (db.names[k].second[p-2] == '&') - p -= 3; - else if (db.names[k].second.back() == '&') - p -= 2; - if (cv & 1) - { - db.names[k].second.insert(p, " const"); - p += 6; - } - if (cv & 2) - { - db.names[k].second.insert(p, " volatile"); - p += 9; - } - if (cv & 4) - db.names[k].second.insert(p, " restrict"); - } - else - { - if (cv & 1) - db.names[k].first.append(" const"); - if (cv & 2) - db.names[k].first.append(" volatile"); - if (cv & 4) - db.names[k].first.append(" restrict"); + if (cv) { + if (is_function) + db.Names[k] = db.make<FunctionQualType>( + db.Names[k], cv); + else + db.Names[k] = + db.make<QualType>(db.Names[k], cv); } - db.subs.back().push_back(db.names[k]); + db.Subs.pushSubstitutionIntoPack(db.Names[k]); } first = t1; } @@ -1975,77 +3481,69 @@ parse_type(const char* first, const char* last, C& db) t = parse_array_type(first, last, db); if (t != first) { - if (db.names.empty()) + if (db.Names.empty()) return first; first = t; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Subs.pushSubstitution(db.Names.back()); } break; case 'C': t = parse_type(first+1, last, db); if (t != first+1) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.append(" complex"); + db.Names.back() = db.make<PostfixQualifiedType>( + db.Names.back(), " complex"); first = t; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Subs.pushSubstitution(db.Names.back()); } break; case 'F': t = parse_function_type(first, last, db); if (t != first) { - if (db.names.empty()) + if (db.Names.empty()) return first; first = t; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Subs.pushSubstitution(db.Names.back()); } break; case 'G': t = parse_type(first+1, last, db); if (t != first+1) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.append(" imaginary"); + db.Names.back() = db.make<PostfixQualifiedType>( + db.Names.back(), " imaginary"); first = t; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Subs.pushSubstitution(db.Names.back()); } break; case 'M': t = parse_pointer_to_member_type(first, last, db); if (t != first) { - if (db.names.empty()) + if (db.Names.empty()) return first; first = t; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Subs.pushSubstitution(db.Names.back()); } break; case 'O': { - size_t k0 = db.names.size(); + size_t k0 = db.Names.size(); t = parse_type(first+1, last, db); - size_t k1 = db.names.size(); + size_t k1 = db.Names.size(); if (t != first+1) { - db.subs.emplace_back(db.names.get_allocator()); + db.Subs.pushPack(); for (size_t k = k0; k < k1; ++k) { - if (db.names[k].second.substr(0, 2) == " [") - { - db.names[k].first += " ("; - db.names[k].second.insert(0, ")"); - } - else if (!db.names[k].second.empty() && - db.names[k].second.front() == '(') - { - db.names[k].first += "("; - db.names[k].second.insert(0, ")"); - } - db.names[k].first.append("&&"); - db.subs.back().push_back(db.names[k]); + db.Names[k] = + db.make<RValueReferenceType>(db.Names[k]); + db.Subs.pushSubstitutionIntoPack(db.Names[k]); } first = t; } @@ -2053,34 +3551,16 @@ parse_type(const char* first, const char* last, C& db) } case 'P': { - size_t k0 = db.names.size(); + size_t k0 = db.Names.size(); t = parse_type(first+1, last, db); - size_t k1 = db.names.size(); + size_t k1 = db.Names.size(); if (t != first+1) { - db.subs.emplace_back(db.names.get_allocator()); + db.Subs.pushPack(); for (size_t k = k0; k < k1; ++k) { - if (db.names[k].second.substr(0, 2) == " [") - { - db.names[k].first += " ("; - db.names[k].second.insert(0, ")"); - } - else if (!db.names[k].second.empty() && - db.names[k].second.front() == '(') - { - db.names[k].first += "("; - db.names[k].second.insert(0, ")"); - } - if (first[1] != 'U' || db.names[k].first.substr(0, 12) != "objc_object<") - { - db.names[k].first.append("*"); - } - else - { - db.names[k].first.replace(0, 11, "id"); - } - db.subs.back().push_back(db.names[k]); + db.Names[k] = db.make<PointerType>(db.Names[k]); + db.Subs.pushSubstitutionIntoPack(db.Names[k]); } first = t; } @@ -2088,27 +3568,17 @@ parse_type(const char* first, const char* last, C& db) } case 'R': { - size_t k0 = db.names.size(); + size_t k0 = db.Names.size(); t = parse_type(first+1, last, db); - size_t k1 = db.names.size(); + size_t k1 = db.Names.size(); if (t != first+1) { - db.subs.emplace_back(db.names.get_allocator()); + db.Subs.pushPack(); for (size_t k = k0; k < k1; ++k) { - if (db.names[k].second.substr(0, 2) == " [") - { - db.names[k].first += " ("; - db.names[k].second.insert(0, ")"); - } - else if (!db.names[k].second.empty() && - db.names[k].second.front() == '(') - { - db.names[k].first += "("; - db.names[k].second.insert(0, ")"); - } - db.names[k].first.append("&"); - db.subs.back().push_back(db.names[k]); + db.Names[k] = + db.make<LValueReferenceType>(db.Names[k]); + db.Subs.pushSubstitutionIntoPack(db.Names[k]); } first = t; } @@ -2116,23 +3586,25 @@ parse_type(const char* first, const char* last, C& db) } case 'T': { - size_t k0 = db.names.size(); + size_t k0 = db.Names.size(); t = parse_template_param(first, last, db); - size_t k1 = db.names.size(); + size_t k1 = db.Names.size(); if (t != first) { - db.subs.emplace_back(db.names.get_allocator()); + db.Subs.pushPack(); for (size_t k = k0; k < k1; ++k) - db.subs.back().push_back(db.names[k]); - if (db.try_to_parse_template_args && k1 == k0+1) + db.Subs.pushSubstitutionIntoPack(db.Names[k]); + if (db.TryToParseTemplateArgs && k1 == k0+1) { const char* t1 = parse_template_args(t, last, db); if (t1 != t) { - auto args = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += std::move(args); - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + auto args = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = db.make< + NameWithTemplateArgs>( + db.Names.back(), args); + db.Subs.pushSubstitution(db.Names.back()); t = t1; } } @@ -2149,29 +3621,30 @@ parse_type(const char* first, const char* last, C& db) const char* t2 = parse_type(t, last, db); if (t2 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto type = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.back().first.substr(0, 9) != "objcproto") + auto type = db.Names.back(); + db.Names.pop_back(); + if (db.Names.back()->K != Node::KNameType || + !static_cast<NameType*>(db.Names.back())->getName().startsWith("objcproto")) { - db.names.back() = type + " " + db.names.back().move_full(); + db.Names.back() = db.make<VendorExtQualType>(type, db.Names.back()); } else { - auto proto = db.names.back().move_full(); - db.names.pop_back(); - t = parse_source_name(proto.data() + 9, proto.data() + proto.size(), db); - if (t != proto.data() + 9) + auto* proto = static_cast<NameType*>(db.Names.back()); + db.Names.pop_back(); + t = parse_source_name(proto->getName().begin() + 9, proto->getName().end(), db); + if (t != proto->getName().begin() + 9) { - db.names.back() = type + "<" + db.names.back().move_full() + ">"; + db.Names.back() = db.make<ObjCProtoName>(type, db.Names.back()); } else { - db.names.push_back(type + " " + proto); + db.Names.push_back(db.make<VendorExtQualType>(type, proto)); } } - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Subs.pushSubstitution(db.Names.back()); first = t2; } } @@ -2183,9 +3656,9 @@ parse_type(const char* first, const char* last, C& db) t = parse_name(first, last, db); if (t != first) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Subs.pushSubstitution(db.Names.back()); first = t; } } @@ -2197,17 +3670,22 @@ parse_type(const char* first, const char* last, C& db) first = t; // Parsed a substitution. If the substitution is a // <template-param> it might be followed by <template-args>. - t = parse_template_args(first, last, db); - if (t != first) + if (db.TryToParseTemplateArgs) { - if (db.names.size() < 2) - return first; - auto template_args = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first += template_args; - // Need to create substitution for <template-template-param> <template-args> - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); - first = t; + t = parse_template_args(first, last, db); + if (t != first) + { + if (db.Names.size() < 2) + return first; + auto template_args = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = db.make< + NameWithTemplateArgs>( + db.Names.back(), template_args); + // Need to create substitution for <template-template-param> <template-args> + db.Subs.pushSubstitution(db.Names.back()); + first = t; + } } } } @@ -2219,14 +3697,14 @@ parse_type(const char* first, const char* last, C& db) { case 'p': { - size_t k0 = db.names.size(); + size_t k0 = db.Names.size(); t = parse_type(first+2, last, db); - size_t k1 = db.names.size(); + size_t k1 = db.Names.size(); if (t != first+2) { - db.subs.emplace_back(db.names.get_allocator()); + db.Subs.pushPack(); for (size_t k = k0; k < k1; ++k) - db.subs.back().push_back(db.names[k]); + db.Subs.pushSubstitutionIntoPack(db.Names[k]); first = t; return first; } @@ -2237,9 +3715,9 @@ parse_type(const char* first, const char* last, C& db) t = parse_decltype(first, last, db); if (t != first) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Subs.pushSubstitution(db.Names.back()); first = t; return first; } @@ -2248,16 +3726,16 @@ parse_type(const char* first, const char* last, C& db) t = parse_vector_type(first, last, db); if (t != first) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Subs.pushSubstitution(db.Names.back()); first = t; return first; } break; } } - // drop through + _LIBCPP_FALLTHROUGH(); default: // must check for builtin-types before class-enum-types to avoid // ambiguities with operator-names @@ -2271,9 +3749,9 @@ parse_type(const char* first, const char* last, C& db) t = parse_name(first, last, db); if (t != first) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Subs.pushSubstitution(db.Names.back()); first = t; } } @@ -2338,11 +3816,11 @@ parse_type(const char* first, const char* last, C& db) // ::= rs # >> // ::= rS # >>= // ::= v <digit> <source-name> # vendor extended operator - -template <class C> +// extension ::= <operator-name> <abi-tag-seq> const char* -parse_operator_name(const char* first, const char* last, C& db) +parse_operator_name(const char* first, const char* last, Db& db) { + const char* original_first = first; if (last - first >= 2) { switch (first[0]) @@ -2351,20 +3829,20 @@ parse_operator_name(const char* first, const char* last, C& db) switch (first[1]) { case 'a': - db.names.push_back("operator&&"); + db.Names.push_back(db.make<NameType>("operator&&")); first += 2; break; case 'd': case 'n': - db.names.push_back("operator&"); + db.Names.push_back(db.make<NameType>("operator&")); first += 2; break; case 'N': - db.names.push_back("operator&="); + db.Names.push_back(db.make<NameType>("operator&=")); first += 2; break; case 'S': - db.names.push_back("operator="); + db.Names.push_back(db.make<NameType>("operator=")); first += 2; break; } @@ -2373,29 +3851,30 @@ parse_operator_name(const char* first, const char* last, C& db) switch (first[1]) { case 'l': - db.names.push_back("operator()"); + db.Names.push_back(db.make<NameType>("operator()")); first += 2; break; case 'm': - db.names.push_back("operator,"); + db.Names.push_back(db.make<NameType>("operator,")); first += 2; break; case 'o': - db.names.push_back("operator~"); + db.Names.push_back(db.make<NameType>("operator~")); first += 2; break; case 'v': { - bool try_to_parse_template_args = db.try_to_parse_template_args; - db.try_to_parse_template_args = false; + bool TryToParseTemplateArgs = db.TryToParseTemplateArgs; + db.TryToParseTemplateArgs = false; const char* t = parse_type(first+2, last, db); - db.try_to_parse_template_args = try_to_parse_template_args; + db.TryToParseTemplateArgs = TryToParseTemplateArgs; if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "operator "); - db.parsed_ctor_dtor_cv = true; + db.Names.back() = + db.make<ConversionOperatorType>(db.Names.back()); + db.ParsedCtorDtorCV = true; first = t; } } @@ -2406,23 +3885,23 @@ parse_operator_name(const char* first, const char* last, C& db) switch (first[1]) { case 'a': - db.names.push_back("operator delete[]"); + db.Names.push_back(db.make<NameType>("operator delete[]")); first += 2; break; case 'e': - db.names.push_back("operator*"); + db.Names.push_back(db.make<NameType>("operator*")); first += 2; break; case 'l': - db.names.push_back("operator delete"); + db.Names.push_back(db.make<NameType>("operator delete")); first += 2; break; case 'v': - db.names.push_back("operator/"); + db.Names.push_back(db.make<NameType>("operator/")); first += 2; break; case 'V': - db.names.push_back("operator/="); + db.Names.push_back(db.make<NameType>("operator/=")); first += 2; break; } @@ -2431,15 +3910,15 @@ parse_operator_name(const char* first, const char* last, C& db) switch (first[1]) { case 'o': - db.names.push_back("operator^"); + db.Names.push_back(db.make<NameType>("operator^")); first += 2; break; case 'O': - db.names.push_back("operator^="); + db.Names.push_back(db.make<NameType>("operator^=")); first += 2; break; case 'q': - db.names.push_back("operator=="); + db.Names.push_back(db.make<NameType>("operator==")); first += 2; break; } @@ -2448,11 +3927,11 @@ parse_operator_name(const char* first, const char* last, C& db) switch (first[1]) { case 'e': - db.names.push_back("operator>="); + db.Names.push_back(db.make<NameType>("operator>=")); first += 2; break; case 't': - db.names.push_back("operator>"); + db.Names.push_back(db.make<NameType>("operator>")); first += 2; break; } @@ -2460,7 +3939,7 @@ parse_operator_name(const char* first, const char* last, C& db) case 'i': if (first[1] == 'x') { - db.names.push_back("operator[]"); + db.Names.push_back(db.make<NameType>("operator[]")); first += 2; } break; @@ -2468,7 +3947,7 @@ parse_operator_name(const char* first, const char* last, C& db) switch (first[1]) { case 'e': - db.names.push_back("operator<="); + db.Names.push_back(db.make<NameType>("operator<=")); first += 2; break; case 'i': @@ -2476,23 +3955,24 @@ parse_operator_name(const char* first, const char* last, C& db) const char* t = parse_source_name(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "operator\"\" "); + db.Names.back() = + db.make<LiteralOperator>(db.Names.back()); first = t; } } break; case 's': - db.names.push_back("operator<<"); + db.Names.push_back(db.make<NameType>("operator<<")); first += 2; break; case 'S': - db.names.push_back("operator<<="); + db.Names.push_back(db.make<NameType>("operator<<=")); first += 2; break; case 't': - db.names.push_back("operator<"); + db.Names.push_back(db.make<NameType>("operator<")); first += 2; break; } @@ -2501,23 +3981,23 @@ parse_operator_name(const char* first, const char* last, C& db) switch (first[1]) { case 'i': - db.names.push_back("operator-"); + db.Names.push_back(db.make<NameType>("operator-")); first += 2; break; case 'I': - db.names.push_back("operator-="); + db.Names.push_back(db.make<NameType>("operator-=")); first += 2; break; case 'l': - db.names.push_back("operator*"); + db.Names.push_back(db.make<NameType>("operator*")); first += 2; break; case 'L': - db.names.push_back("operator*="); + db.Names.push_back(db.make<NameType>("operator*=")); first += 2; break; case 'm': - db.names.push_back("operator--"); + db.Names.push_back(db.make<NameType>("operator--")); first += 2; break; } @@ -2526,23 +4006,23 @@ parse_operator_name(const char* first, const char* last, C& db) switch (first[1]) { case 'a': - db.names.push_back("operator new[]"); + db.Names.push_back(db.make<NameType>("operator new[]")); first += 2; break; case 'e': - db.names.push_back("operator!="); + db.Names.push_back(db.make<NameType>("operator!=")); first += 2; break; case 'g': - db.names.push_back("operator-"); + db.Names.push_back(db.make<NameType>("operator-")); first += 2; break; case 't': - db.names.push_back("operator!"); + db.Names.push_back(db.make<NameType>("operator!")); first += 2; break; case 'w': - db.names.push_back("operator new"); + db.Names.push_back(db.make<NameType>("operator new")); first += 2; break; } @@ -2551,15 +4031,15 @@ parse_operator_name(const char* first, const char* last, C& db) switch (first[1]) { case 'o': - db.names.push_back("operator||"); + db.Names.push_back(db.make<NameType>("operator||")); first += 2; break; case 'r': - db.names.push_back("operator|"); + db.Names.push_back(db.make<NameType>("operator|")); first += 2; break; case 'R': - db.names.push_back("operator|="); + db.Names.push_back(db.make<NameType>("operator|=")); first += 2; break; } @@ -2568,27 +4048,27 @@ parse_operator_name(const char* first, const char* last, C& db) switch (first[1]) { case 'm': - db.names.push_back("operator->*"); + db.Names.push_back(db.make<NameType>("operator->*")); first += 2; break; case 'l': - db.names.push_back("operator+"); + db.Names.push_back(db.make<NameType>("operator+")); first += 2; break; case 'L': - db.names.push_back("operator+="); + db.Names.push_back(db.make<NameType>("operator+=")); first += 2; break; case 'p': - db.names.push_back("operator++"); + db.Names.push_back(db.make<NameType>("operator++")); first += 2; break; case 's': - db.names.push_back("operator+"); + db.Names.push_back(db.make<NameType>("operator+")); first += 2; break; case 't': - db.names.push_back("operator->"); + db.Names.push_back(db.make<NameType>("operator->")); first += 2; break; } @@ -2596,7 +4076,7 @@ parse_operator_name(const char* first, const char* last, C& db) case 'q': if (first[1] == 'u') { - db.names.push_back("operator?"); + db.Names.push_back(db.make<NameType>("operator?")); first += 2; } break; @@ -2604,19 +4084,19 @@ parse_operator_name(const char* first, const char* last, C& db) switch (first[1]) { case 'm': - db.names.push_back("operator%"); + db.Names.push_back(db.make<NameType>("operator%")); first += 2; break; case 'M': - db.names.push_back("operator%="); + db.Names.push_back(db.make<NameType>("operator%=")); first += 2; break; case 's': - db.names.push_back("operator>>"); + db.Names.push_back(db.make<NameType>("operator>>")); first += 2; break; case 'S': - db.names.push_back("operator>>="); + db.Names.push_back(db.make<NameType>("operator>>=")); first += 2; break; } @@ -2627,37 +4107,31 @@ parse_operator_name(const char* first, const char* last, C& db) const char* t = parse_source_name(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "operator "); + db.Names.back() = + db.make<ConversionOperatorType>(db.Names.back()); first = t; } } break; } } + + if (original_first != first) + first = parse_abi_tag_seq(first, last, db); + return first; } -template <class C> const char* -parse_integer_literal(const char* first, const char* last, const typename C::String& lit, C& db) +parse_integer_literal(const char* first, const char* last, StringView lit, Db& db) { const char* t = parse_number(first, last); if (t != first && t != last && *t == 'E') { - if (lit.size() > 3) - db.names.push_back("(" + lit + ")"); - else - db.names.emplace_back(); - if (*first == 'n') - { - db.names.back().first += '-'; - ++first; - } - db.names.back().first.append(first, t); - if (lit.size() <= 3) - db.names.back().first += lit; + db.Names.push_back( + db.make<IntegerExpr>(lit, StringView(first, t))); first = t+1; } return first; @@ -2670,9 +4144,8 @@ parse_integer_literal(const char* first, const char* last, const typename C::Str // ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000) // ::= L <mangled-name> E # external name -template <class C> const char* -parse_expr_primary(const char* first, const char* last, C& db) +parse_expr_primary(const char* first, const char* last, Db& db) { if (last - first >= 4 && *first == 'L') { @@ -2691,11 +4164,11 @@ parse_expr_primary(const char* first, const char* last, C& db) switch (first[2]) { case '0': - db.names.push_back("false"); + db.Names.push_back(db.make<BoolExpr>(0)); first += 4; break; case '1': - db.names.push_back("true"); + db.Names.push_back(db.make<BoolExpr>(1)); first += 4; break; } @@ -2838,9 +4311,10 @@ parse_expr_primary(const char* first, const char* last, C& db) ; if (n != t && n != last && *n == 'E') { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back() = "(" + db.names.back().move_full() + ")" + typename C::String(t, n); + db.Names.back() = db.make<IntegerCastExpr>( + db.Names.back(), StringView(t, n)); first = n+1; break; } @@ -2857,65 +4331,22 @@ parse_expr_primary(const char* first, const char* last, C& db) return first; } -template <class String> -String -base_name(String& s) +Node* maybe_change_special_sub_name(Node* inp, Db& db) { - if (s.empty()) - return s; - if (s == "std::string") - { - s = "std::basic_string<char, std::char_traits<char>, std::allocator<char> >"; - return "basic_string"; - } - if (s == "std::istream") - { - s = "std::basic_istream<char, std::char_traits<char> >"; - return "basic_istream"; - } - if (s == "std::ostream") + if (inp->K != Node::KSpecialSubstitution) + return inp; + auto Kind = static_cast<SpecialSubstitution*>(inp)->SSK; + switch (Kind) { - s = "std::basic_ostream<char, std::char_traits<char> >"; - return "basic_ostream"; - } - if (s == "std::iostream") - { - s = "std::basic_iostream<char, std::char_traits<char> >"; - return "basic_iostream"; - } - const char* const pf = s.data(); - const char* pe = pf + s.size(); - if (pe[-1] == '>') - { - unsigned c = 1; - while (true) - { - if (--pe == pf) - return String(); - if (pe[-1] == '<') - { - if (--c == 0) - { - --pe; - break; - } - } - else if (pe[-1] == '>') - ++c; - } - } - if (pe - pf <= 1) - return String(); - const char* p0 = pe - 1; - for (; p0 != pf; --p0) - { - if (*p0 == ':') - { - ++p0; - break; - } + case SpecialSubKind::string: + case SpecialSubKind::istream: + case SpecialSubKind::ostream: + case SpecialSubKind::iostream: + return db.make<ExpandedSpecialSubstitution>(Kind); + default: + break; } - return String(p0, pe); + return inp; } // <ctor-dtor-name> ::= C1 # complete object constructor @@ -2926,12 +4357,11 @@ base_name(String& s) // ::= D1 # complete object destructor // ::= D2 # base object destructor // extension ::= D5 # ? - -template <class C> +// extension ::= <ctor-dtor-name> <abi-tag-seq> const char* -parse_ctor_dtor_name(const char* first, const char* last, C& db) +parse_ctor_dtor_name(const char* first, const char* last, Db& db) { - if (last-first >= 2 && !db.names.empty()) + if (last-first >= 2 && !db.Names.empty()) { switch (first[0]) { @@ -2942,11 +4372,15 @@ parse_ctor_dtor_name(const char* first, const char* last, C& db) case '2': case '3': case '5': - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.push_back(base_name(db.names.back().first)); + db.Names.back() = + maybe_change_special_sub_name(db.Names.back(), db); + db.Names.push_back( + db.make<CtorDtorName>(db.Names.back(), false)); first += 2; - db.parsed_ctor_dtor_cv = true; + first = parse_abi_tag_seq(first, last, db); + db.ParsedCtorDtorCV = true; break; } break; @@ -2957,11 +4391,13 @@ parse_ctor_dtor_name(const char* first, const char* last, C& db) case '1': case '2': case '5': - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.push_back("~" + base_name(db.names.back().first)); + db.Names.push_back( + db.make<CtorDtorName>(db.Names.back(), true)); first += 2; - db.parsed_ctor_dtor_cv = true; + first = parse_abi_tag_seq(first, last, db); + db.ParsedCtorDtorCV = true; break; } break; @@ -2970,16 +4406,14 @@ parse_ctor_dtor_name(const char* first, const char* last, C& db) return first; } -// <unnamed-type-name> ::= Ut [ <nonnegative number> ] _ +// <unnamed-type-name> ::= Ut [<nonnegative number>] _ [<abi-tag-seq>] // ::= <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 <class C> const char* -parse_unnamed_type_name(const char* first, const char* last, C& db) +parse_unnamed_type_name(const char* first, const char* last, Db& db) { if (last - first > 2 && first[0] == 'U') { @@ -2988,101 +4422,64 @@ parse_unnamed_type_name(const char* first, const char* last, C& db) { case 't': { - db.names.push_back(typename C::String("'unnamed")); const char* t0 = first+2; if (t0 == last) - { - db.names.pop_back(); return first; - } + StringView count; if (std::isdigit(*t0)) { const char* t1 = t0 + 1; while (t1 != last && std::isdigit(*t1)) ++t1; - db.names.back().first.append(t0, t1); + count = StringView(t0, t1); t0 = t1; } - db.names.back().first.push_back('\''); if (t0 == last || *t0 != '_') - { - db.names.pop_back(); return first; - } + db.Names.push_back(db.make<UnnamedTypeName>(count)); first = t0 + 1; + first = parse_abi_tag_seq(first, last, db); } break; case 'l': { - db.names.push_back(typename C::String("'lambda'(")); + size_t begin_pos = db.Names.size(); const char* t0 = first+2; + NodeArray lambda_params; if (first[2] == 'v') { - db.names.back().first += ')'; ++t0; } else { - const char* t1 = parse_type(t0, last, db); - if (t1 == t0) - { - if(!db.names.empty()) - db.names.pop_back(); - return first; - } - if (db.names.size() < 2) - return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - db.names.back().first.append(tmp); - t0 = t1; while (true) { - t1 = parse_type(t0, last, db); + const char* t1 = parse_type(t0, last, db); if (t1 == t0) break; - if (db.names.size() < 2) - return first; - tmp = db.names.back().move_full(); - db.names.pop_back(); - if (!tmp.empty()) - { - db.names.back().first.append(", "); - db.names.back().first.append(tmp); - } t0 = t1; } - if(db.names.empty()) - return first; - db.names.back().first.append(")"); + if (db.Names.size() < begin_pos) + return first; + lambda_params = db.popTrailingNodeArray(begin_pos); } if (t0 == last || *t0 != 'E') - { - if(!db.names.empty()) - db.names.pop_back(); return first; - } ++t0; if (t0 == last) - { - if(!db.names.empty()) - db.names.pop_back(); return first; - } + StringView count; if (std::isdigit(*t0)) { const char* t1 = t0 + 1; while (t1 != last && std::isdigit(*t1)) ++t1; - db.names.back().first.insert(db.names.back().first.begin()+7, t0, t1); + count = StringView(t0, t1); t0 = t1; } if (t0 == last || *t0 != '_') - { - if(!db.names.empty()) - db.names.pop_back(); return first; - } + db.Names.push_back(db.make<LambdaTypeName>(lambda_params, count)); first = t0 + 1; } break; @@ -3096,9 +4493,8 @@ parse_unnamed_type_name(const char* first, const char* last, C& db) // ::= <source-name> // ::= <unnamed-type-name> -template <class C> const char* -parse_unqualified_name(const char* first, const char* last, C& db) +parse_unqualified_name(const char* first, const char* last, Db& db) { if (first != last) { @@ -3143,9 +4539,8 @@ parse_unqualified_name(const char* first, const char* last, C& db) // ::= St <unqualified-name> # ::std:: // extension ::= StL<unqualified-name> -template <class C> const char* -parse_unscoped_name(const char* first, const char* last, C& db) +parse_unscoped_name(const char* first, const char* last, Db& db) { if (last - first >= 2) { @@ -3163,9 +4558,10 @@ parse_unscoped_name(const char* first, const char* last, C& db) { if (St) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "std::"); + db.Names.back() = + db.make<StdQualifiedName>(db.Names.back()); } first = t1; } @@ -3175,18 +4571,18 @@ parse_unscoped_name(const char* first, const char* last, C& db) // at <type> # alignof (a type) -template <class C> const char* -parse_alignof_type(const char* first, const char* last, C& db) +parse_alignof_type(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 'a' && first[1] == 't') { const char* t = parse_type(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; + db.Names.back() = + db.make<EnclosingExpr>("alignof (", db.Names.back(), ")"); first = t; } } @@ -3195,57 +4591,55 @@ parse_alignof_type(const char* first, const char* last, C& db) // az <expression> # alignof (a expression) -template <class C> const char* -parse_alignof_expr(const char* first, const char* last, C& db) +parse_alignof_expr(const char* first, const char* last, Db& db) { if (last - first >= 3 && first[0] == 'a' && first[1] == 'z') { const char* t = parse_expression(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; + db.Names.back() = + db.make<EnclosingExpr>("alignof (", db.Names.back(), ")"); first = t; } } return first; } -template <class C> const char* -parse_noexcept_expression(const char* first, const char* last, C& db) +parse_noexcept_expression(const char* first, const char* last, Db& db) { const char* t1 = parse_expression(first, last, db); if (t1 != first) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first = "noexcept (" + db.names.back().move_full() + ")"; + db.Names.back() = + db.make<EnclosingExpr>("noexcept (", db.Names.back(), ")"); first = t1; } return first; } -template <class C> const char* -parse_prefix_expression(const char* first, const char* last, const typename C::String& op, C& db) +parse_prefix_expression(const char* first, const char* last, StringView op, Db& db) { const char* t1 = parse_expression(first, last, db); if (t1 != first) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first = op + "(" + db.names.back().move_full() + ")"; + db.Names.back() = db.make<PrefixExpr>(op, db.Names.back()); first = t1; } return first; } -template <class C> const char* -parse_binary_expression(const char* first, const char* last, const typename C::String& op, C& db) +parse_binary_expression(const char* first, const char* last, StringView op, Db& db) { const char* t1 = parse_expression(first, last, db); if (t1 != first) @@ -3253,22 +4647,14 @@ parse_binary_expression(const char* first, const char* last, const typename C::S const char* t2 = parse_expression(t1, last, db); if (t2 != t1) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto op2 = db.names.back().move_full(); - db.names.pop_back(); - auto op1 = db.names.back().move_full(); - auto& nm = db.names.back().first; - nm.clear(); - if (op == ">") - nm += '('; - nm += "(" + op1 + ") " + op + " (" + op2 + ")"; - if (op == ">") - nm += ')'; + auto op2 = db.Names.back(); + db.Names.pop_back(); + auto op1 = db.Names.back(); + db.Names.back() = db.make<BinaryExpr>(op1, op, op2); first = t2; } - else if(!db.names.empty()) - db.names.pop_back(); } return first; } @@ -3313,9 +4699,8 @@ parse_binary_expression(const char* first, const char* last, const typename C::S // # objectless nonstatic member reference // ::= <expr-primary> -template <class C> const char* -parse_expression(const char* first, const char* last, C& db) +parse_expression(const char* first, const char* last, Db& db) { if (last - first >= 2) { @@ -3405,10 +4790,10 @@ parse_expression(const char* first, const char* last, C& db) const char* t1 = parse_expression(t+2, last, db); if (t1 != t+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + - "delete[] " + db.names.back().move_full(); + db.Names.back() = db.make<DeleteExpr>( + db.Names.back(), parsed_gs, /*is_array=*/true); first = t1; } } @@ -3426,10 +4811,10 @@ parse_expression(const char* first, const char* last, C& db) const char* t1 = parse_expression(t+2, last, db); if (t1 != t+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + - "delete " + db.names.back().move_full(); + db.Names.back() = db.make<DeleteExpr>( + db.Names.back(), parsed_gs, /*is_array=*/false); first = t1; } } @@ -3498,16 +4883,17 @@ parse_expression(const char* first, const char* last, C& db) const char* t2 = parse_expression(t1, last, db); if (t2 != t1) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto op2 = db.names.back().move_full(); - db.names.pop_back(); - auto op1 = db.names.back().move_full(); - db.names.back() = "(" + op1 + ")[" + op2 + "]"; + auto op2 = db.Names.back(); + db.Names.pop_back(); + auto op1 = db.Names.back(); + db.Names.back() = + db.make<ArraySubscriptExpr>(op1, op2); first = t2; } - else if (!db.names.empty()) - db.names.pop_back(); + else if (!db.Names.empty()) + db.Names.pop_back(); } } break; @@ -3571,9 +4957,10 @@ parse_expression(const char* first, const char* last, C& db) const char* t1 = parse_expression(first+2, last, db); if (t1 != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back() = "(" + db.names.back().move_full() + ")--"; + db.Names.back() = + db.make<PostfixExpr>(db.Names.back(), "--"); first = t1; } } @@ -3661,9 +5048,10 @@ parse_expression(const char* first, const char* last, C& db) const char* t1 = parse_expression(first+2, last, db); if (t1 != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back() = "(" + db.names.back().move_full() + ")++"; + db.Names.back() = + db.make<PostfixExpr>(db.Names.back(), "++"); first = t1; } } @@ -3690,26 +5078,27 @@ parse_expression(const char* first, const char* last, C& db) const char* t3 = parse_expression(t2, last, db); if (t3 != t2) { - if (db.names.size() < 3) + if (db.Names.size() < 3) return first; - auto op3 = db.names.back().move_full(); - db.names.pop_back(); - auto op2 = db.names.back().move_full(); - db.names.pop_back(); - auto op1 = db.names.back().move_full(); - db.names.back() = "(" + op1 + ") ? (" + op2 + ") : (" + op3 + ")"; + auto op3 = db.Names.back(); + db.Names.pop_back(); + auto op2 = db.Names.back(); + db.Names.pop_back(); + auto op1 = db.Names.back(); + db.Names.back() = + db.make<ConditionalExpr>(op1, op2, op3); first = t3; } else { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - db.names.pop_back(); - db.names.pop_back(); + db.Names.pop_back(); + db.Names.pop_back(); } } - else if (!db.names.empty()) - db.names.pop_back(); + else if (!db.Names.empty()) + db.Names.pop_back(); } } break; @@ -3782,7 +5171,7 @@ parse_expression(const char* first, const char* last, C& db) first = parse_typeid_expr(first, last, db); break; case 'r': - db.names.push_back("throw"); + db.Names.push_back(db.make<NameType>("throw")); first += 2; break; case 'w': @@ -3811,9 +5200,8 @@ parse_expression(const char* first, const char* last, C& db) // ::= J <template-arg>* E # argument pack // ::= LZ <encoding> E # extension -template <class C> const char* -parse_template_arg(const char* first, const char* last, C& db) +parse_template_arg(const char* first, const char* last, Db& db) { if (first != last) { @@ -3864,57 +5252,52 @@ parse_template_arg(const char* first, const char* last, C& db) // <template-args> ::= I <template-arg>* E // extension, the abi says <template-arg>+ -template <class C> const char* -parse_template_args(const char* first, const char* last, C& db) +parse_template_args(const char* first, const char* last, Db& db) { if (last - first >= 2 && *first == 'I') { - if (db.tag_templates) - db.template_param.back().clear(); + if (db.TagTemplates) + db.TemplateParams.clear(); const char* t = first+1; - typename C::String args("<"); + size_t begin_idx = db.Names.size(); while (*t != 'E') { - if (db.tag_templates) - db.template_param.emplace_back(db.names.get_allocator()); - size_t k0 = db.names.size(); - const char* t1 = parse_template_arg(t, last, db); - size_t k1 = db.names.size(); - if (db.tag_templates) - db.template_param.pop_back(); - if (t1 == t || t1 == last) - return first; - if (db.tag_templates) + if (db.TagTemplates) { - db.template_param.back().emplace_back(db.names.get_allocator()); + auto TmpParams = std::move(db.TemplateParams); + size_t k0 = db.Names.size(); + const char* t1 = parse_template_arg(t, last, db); + size_t k1 = db.Names.size(); + db.TemplateParams = std::move(TmpParams); + + if (t1 == t || t1 == last || k0 > k1) + return first; + db.TemplateParams.pushPack(); for (size_t k = k0; k < k1; ++k) - db.template_param.back().back().push_back(db.names[k]); - } - for (size_t k = k0; k < k1; ++k) - { - if (args.size() > 1) - args += ", "; - args += db.names[k].move_full(); + db.TemplateParams.pushSubstitutionIntoPack(db.Names[k]); + t = t1; + continue; } - for (; k1 > k0; --k1) - if (!db.names.empty()) - db.names.pop_back(); + size_t k0 = db.Names.size(); + const char* t1 = parse_template_arg(t, last, db); + size_t k1 = db.Names.size(); + if (t1 == t || t1 == last || k0 > k1) + return first; t = t1; } + if (begin_idx > db.Names.size()) + return first; first = t + 1; - if (args.back() != '>') - args += ">"; - else - args += " >"; - db.names.push_back(std::move(args)); - + TemplateParams* tp = db.make<TemplateParams>( + db.popTrailingNodeArray(begin_idx)); + db.Names.push_back(tp); } return first; } -// <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E -// ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E +// <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> @@ -3929,39 +5312,35 @@ parse_template_args(const char* first, const char* last, C& db) // ::= <template-param> // ::= <substitution> -template <class C> const char* -parse_nested_name(const char* first, const char* last, C& db, +parse_nested_name(const char* first, const char* last, Db& db, bool* ends_with_template_args) { if (first != last && *first == 'N') { - unsigned cv; + Qualifiers cv; const char* t0 = parse_cv_qualifiers(first+1, last, cv); if (t0 == last) return first; - db.ref = 0; + db.RefQuals = FrefQualNone; if (*t0 == 'R') { - db.ref = 1; + db.RefQuals = FrefQualLValue; ++t0; } else if (*t0 == 'O') { - db.ref = 2; + db.RefQuals = FrefQualRValue; ++t0; } - db.names.emplace_back(); + db.Names.push_back(db.make<EmptyName>()); if (last - t0 >= 2 && t0[0] == 'S' && t0[1] == 't') { t0 += 2; - db.names.back().first = "std"; + db.Names.back() = db.make<NameType>("std"); } if (t0 == last) - { - db.names.pop_back(); return first; - } bool pop_subs = false; bool component_ends_with_template_args = false; while (*t0 != 'E') @@ -3976,17 +5355,18 @@ parse_nested_name(const char* first, const char* last, C& db, t1 = parse_substitution(t0, last, db); if (t1 != t0 && t1 != last) { - auto name = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + if (db.Names.size() < 2) return first; - if (!db.names.back().first.empty()) + auto name = db.Names.back(); + db.Names.pop_back(); + if (db.Names.back()->K != Node::KEmptyName) { - db.names.back().first += "::" + name; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Names.back() = db.make<QualifiedName>( + db.Names.back(), name); + db.Subs.pushSubstitution(db.Names.back()); } else - db.names.back().first = name; + db.Names.back() = name; pop_subs = true; t0 = t1; } @@ -3997,15 +5377,16 @@ parse_nested_name(const char* first, const char* last, C& db, t1 = parse_template_param(t0, last, db); if (t1 != t0 && t1 != last) { - auto name = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + if (db.Names.size() < 2) return first; - if (!db.names.back().first.empty()) - db.names.back().first += "::" + name; + auto name = db.Names.back(); + db.Names.pop_back(); + if (db.Names.back()->K != Node::KEmptyName) + db.Names.back() = + db.make<QualifiedName>(db.Names.back(), name); else - db.names.back().first = name; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Names.back() = name; + db.Subs.pushSubstitution(db.Names.back()); pop_subs = true; t0 = t1; } @@ -4018,15 +5399,16 @@ parse_nested_name(const char* first, const char* last, C& db, t1 = parse_decltype(t0, last, db); if (t1 != t0 && t1 != last) { - auto name = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + if (db.Names.size() < 2) return first; - if (!db.names.back().first.empty()) - db.names.back().first += "::" + name; + auto name = db.Names.back(); + db.Names.pop_back(); + if (db.Names.back()->K != Node::KEmptyName) + db.Names.back() = + db.make<QualifiedName>(db.Names.back(), name); else - db.names.back().first = name; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Names.back() = name; + db.Subs.pushSubstitution(db.Names.back()); pop_subs = true; t0 = t1; } @@ -4037,12 +5419,13 @@ parse_nested_name(const char* first, const char* last, C& db, t1 = parse_template_args(t0, last, db); if (t1 != t0 && t1 != last) { - auto name = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + if (db.Names.size() < 2) return first; - db.names.back().first += name; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + auto name = db.Names.back(); + db.Names.pop_back(); + db.Names.back() = db.make<NameWithTemplateArgs>( + db.Names.back(), name); + db.Subs.pushSubstitution(db.Names.back()); t0 = t1; component_ends_with_template_args = true; } @@ -4058,15 +5441,16 @@ parse_nested_name(const char* first, const char* last, C& db, t1 = parse_unqualified_name(t0, last, db); if (t1 != t0 && t1 != last) { - auto name = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + if (db.Names.size() < 2) return first; - if (!db.names.back().first.empty()) - db.names.back().first += "::" + name; + auto name = db.Names.back(); + db.Names.pop_back(); + if (db.Names.back()->K != Node::KEmptyName) + db.Names.back() = + db.make<QualifiedName>(db.Names.back(), name); else - db.names.back().first = name; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Names.back() = name; + db.Subs.pushSubstitution(db.Names.back()); pop_subs = true; t0 = t1; } @@ -4075,9 +5459,9 @@ parse_nested_name(const char* first, const char* last, C& db, } } first = t0 + 1; - db.cv = cv; - if (pop_subs && !db.subs.empty()) - db.subs.pop_back(); + db.CV = cv; + if (pop_subs && !db.Subs.empty()) + db.Subs.popPack(); if (ends_with_template_args) *ends_with_template_args = component_ends_with_template_args; } @@ -4126,9 +5510,8 @@ parse_discriminator(const char* first, const char* last) // := Z <function encoding> E s [<discriminator>] // := Z <function encoding> Ed [ <parameter number> ] _ <entity name> -template <class C> const char* -parse_local_name(const char* first, const char* last, C& db, +parse_local_name(const char* first, const char* last, Db& db, bool* ends_with_template_args) { if (first != last && *first == 'Z') @@ -4140,9 +5523,10 @@ parse_local_name(const char* first, const char* last, C& db, { case 's': first = parse_discriminator(t+1, last); - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.append("::string literal"); + db.Names.back() = db.make<QualifiedName>( + db.Names.back(), db.make<NameType>("string literal")); break; case 'd': if (++t != last) @@ -4155,18 +5539,18 @@ parse_local_name(const char* first, const char* last, C& db, ends_with_template_args); if (t1 != t) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto name = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + auto name = db.Names.back(); + db.Names.pop_back(); + if (db.Names.empty()) return first; - db.names.back().first.append("::"); - db.names.back().first.append(name); + db.Names.back() = + db.make<QualifiedName>(db.Names.back(), name); first = t1; } - else if (!db.names.empty()) - db.names.pop_back(); + else if (!db.Names.empty()) + db.Names.pop_back(); } } break; @@ -4178,17 +5562,17 @@ parse_local_name(const char* first, const char* last, C& db, { // parse but ignore discriminator first = parse_discriminator(t1, last); - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto name = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + auto name = db.Names.back(); + db.Names.pop_back(); + if (db.Names.empty()) return first; - db.names.back().first.append("::"); - db.names.back().first.append(name); + db.Names.back() = + db.make<QualifiedName>(db.Names.back(), name); } - else if (!db.names.empty()) - db.names.pop_back(); + else if (!db.Names.empty()) + db.Names.pop_back(); } break; } @@ -4205,9 +5589,8 @@ parse_local_name(const char* first, const char* last, C& db, // <unscoped-template-name> ::= <unscoped-name> // ::= <substitution> -template <class C> const char* -parse_name(const char* first, const char* last, C& db, +parse_name(const char* first, const char* last, Db& db, bool* ends_with_template_args) { if (last - first >= 2) @@ -4241,20 +5624,22 @@ parse_name(const char* first, const char* last, C& db, { if (t1 != last && *t1 == 'I') // <unscoped-template-name> <template-args> { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + db.Subs.pushSubstitution(db.Names.back()); t0 = t1; t1 = parse_template_args(t0, last, db); if (t1 != t0) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + auto tmp = db.Names.back(); + db.Names.pop_back(); + if (db.Names.empty()) return first; - db.names.back().first += tmp; + db.Names.back() = + db.make<NameWithTemplateArgs>( + db.Names.back(), tmp); first = t1; if (ends_with_template_args) *ends_with_template_args = true; @@ -4272,13 +5657,15 @@ parse_name(const char* first, const char* last, C& db, t1 = parse_template_args(t0, last, db); if (t1 != t0) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + auto tmp = db.Names.back(); + db.Names.pop_back(); + if (db.Names.empty()) return first; - db.names.back().first += tmp; + db.Names.back() = + db.make<NameWithTemplateArgs>( + db.Names.back(), tmp); first = t1; if (ends_with_template_args) *ends_with_template_args = true; @@ -4343,12 +5730,13 @@ parse_call_offset(const char* first, const char* last) // # base is the nominal target function of thunk // ::= GV <object name> # Guard variable for one-time initialization // # No <type> +// ::= TW <object name> # Thread-local wrapper +// ::= TH <object name> # Thread-local initialization // extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first // extension ::= GR <object name> # reference temporary for object -template <class C> const char* -parse_special_name(const char* first, const char* last, C& db) +parse_special_name(const char* first, const char* last, Db& db) { if (last - first > 2) { @@ -4363,9 +5751,10 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_type(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "vtable for "); + db.Names.back() = + db.make<SpecialName>("vtable for ", db.Names.back()); first = t; } break; @@ -4374,9 +5763,10 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_type(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "VTT for "); + db.Names.back() = + db.make<SpecialName>("VTT for ", db.Names.back()); first = t; } break; @@ -4385,9 +5775,10 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_type(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "typeinfo for "); + db.Names.back() = + db.make<SpecialName>("typeinfo for ", db.Names.back()); first = t; } break; @@ -4396,9 +5787,10 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_type(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "typeinfo name for "); + db.Names.back() = + db.make<SpecialName>("typeinfo name for ", db.Names.back()); first = t; } break; @@ -4414,9 +5806,11 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_encoding(t1, last, db); if (t != t1) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "covariant return thunk to "); + db.Names.back() = + db.make<SpecialName>("covariant return thunk to ", + db.Names.back()); first = t; } } @@ -4432,20 +5826,44 @@ parse_special_name(const char* first, const char* last, C& db) const char* t1 = parse_type(++t0, last, db); if (t1 != t0) { - if (db.names.size() < 2) + if (db.Names.size() < 2) return first; - auto left = db.names.back().move_full(); - db.names.pop_back(); - if (db.names.empty()) + auto left = db.Names.back(); + db.Names.pop_back(); + if (db.Names.empty()) return first; - db.names.back().first = "construction vtable for " + - std::move(left) + "-in-" + - db.names.back().move_full(); + db.Names.back() = db.make<CtorVtableSpecialName>( + left, db.Names.back()); first = t1; } } } break; + case 'W': + // TW <object name> # Thread-local wrapper + t = parse_name(first + 2, last, db); + if (t != first + 2) + { + if (db.Names.empty()) + return first; + db.Names.back() = + db.make<SpecialName>("thread-local wrapper routine for ", + db.Names.back()); + first = t; + } + break; + case 'H': + //TH <object name> # Thread-local initialization + t = parse_name(first + 2, last, db); + if (t != first + 2) + { + if (db.Names.empty()) + return first; + db.Names.back() = db.make<SpecialName>( + "thread-local initialization routine for ", db.Names.back()); + first = t; + } + break; default: // T <call-offset> <base encoding> { @@ -4455,16 +5873,20 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_encoding(t0, last, db); if (t != t0) { - if (db.names.empty()) + if (db.Names.empty()) return first; if (first[1] == 'v') { - db.names.back().first.insert(0, "virtual thunk to "); + db.Names.back() = + db.make<SpecialName>("virtual thunk to ", + db.Names.back()); first = t; } else { - db.names.back().first.insert(0, "non-virtual thunk to "); + db.Names.back() = + db.make<SpecialName>("non-virtual thunk to ", + db.Names.back()); first = t; } } @@ -4480,9 +5902,10 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_name(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "guard variable for "); + db.Names.back() = + db.make<SpecialName>("guard variable for ", db.Names.back()); first = t; } break; @@ -4491,9 +5914,11 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_name(first+2, last, db); if (t != first+2) { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "reference temporary for "); + db.Names.back() = + db.make<SpecialName>("reference temporary for ", + db.Names.back()); first = t; } break; @@ -4528,17 +5953,18 @@ public: // ::= <data name> // ::= <special-name> -template <class C> const char* -parse_encoding(const char* first, const char* last, C& db) +parse_encoding(const char* first, const char* last, Db& db) { if (first != last) { - save_value<decltype(db.encoding_depth)> su(db.encoding_depth); - ++db.encoding_depth; - save_value<decltype(db.tag_templates)> sb(db.tag_templates); - if (db.encoding_depth > 1) - db.tag_templates = true; + save_value<decltype(db.EncodingDepth)> su(db.EncodingDepth); + ++db.EncodingDepth; + save_value<decltype(db.TagTemplates)> sb(db.TagTemplates); + if (db.EncodingDepth > 1) + db.TagTemplates = true; + save_value<decltype(db.ParsedCtorDtorCV)> sp(db.ParsedCtorDtorCV); + db.ParsedCtorDtorCV = false; switch (*first) { case 'G': @@ -4550,96 +5976,72 @@ parse_encoding(const char* first, const char* last, C& db) bool ends_with_template_args = false; const char* t = parse_name(first, last, db, &ends_with_template_args); - unsigned cv = db.cv; - unsigned ref = db.ref; + if (db.Names.empty()) + return first; + Qualifiers cv = db.CV; + FunctionRefQual ref = db.RefQuals; if (t != first) { if (t != last && *t != 'E' && *t != '.') { - save_value<bool> sb2(db.tag_templates); - db.tag_templates = false; + save_value<bool> sb2(db.TagTemplates); + db.TagTemplates = false; const char* t2; - typename C::String ret2; - if (db.names.empty()) + if (db.Names.empty()) return first; - const typename C::String& nm = db.names.back().first; - if (nm.empty()) + if (!db.Names.back()) return first; - if (!db.parsed_ctor_dtor_cv && ends_with_template_args) + Node* return_type = nullptr; + if (!db.ParsedCtorDtorCV && ends_with_template_args) { t2 = parse_type(t, last, db); if (t2 == t) return first; - if (db.names.size() < 2) - return first; - auto ret1 = std::move(db.names.back().first); - ret2 = std::move(db.names.back().second); - if (ret2.empty()) - ret1 += ' '; - db.names.pop_back(); - if (db.names.empty()) + if (db.Names.size() < 1) return first; - - db.names.back().first.insert(0, ret1); + return_type = db.Names.back(); + db.Names.pop_back(); t = t2; } - db.names.back().first += '('; + + Node* result = nullptr; + if (t != last && *t == 'v') { ++t; + if (db.Names.empty()) + return first; + Node* name = db.Names.back(); + db.Names.pop_back(); + result = db.make<TopLevelFunctionDecl>( + return_type, name, NodeArray()); } else { - bool first_arg = true; + size_t params_begin = db.Names.size(); while (true) { - size_t k0 = db.names.size(); t2 = parse_type(t, last, db); - size_t k1 = db.names.size(); if (t2 == t) break; - if (k1 > k0) - { - typename C::String tmp; - for (size_t k = k0; k < k1; ++k) - { - if (!tmp.empty()) - tmp += ", "; - tmp += db.names[k].move_full(); - } - for (size_t k = k0; k < k1; ++k) { - if (db.names.empty()) - return first; - db.names.pop_back(); - } - if (!tmp.empty()) - { - if (db.names.empty()) - return first; - if (!first_arg) - db.names.back().first += ", "; - else - first_arg = false; - db.names.back().first += tmp; - } - } t = t2; } + if (db.Names.size() < params_begin) + return first; + NodeArray params = + db.popTrailingNodeArray(params_begin); + if (db.Names.empty()) + return first; + Node* name = db.Names.back(); + db.Names.pop_back(); + result = db.make<TopLevelFunctionDecl>( + return_type, name, params); } - if (db.names.empty()) - return first; - db.names.back().first += ')'; - if (cv & 1) - db.names.back().first.append(" const"); - if (cv & 2) - db.names.back().first.append(" volatile"); - if (cv & 4) - db.names.back().first.append(" restrict"); - if (ref == 1) - db.names.back().first.append(" &"); - else if (ref == 2) - db.names.back().first.append(" &&"); - db.names.back().first += ret2; + if (ref != FrefQualNone) + result = db.make<FunctionRefQualType>(result, ref); + if (cv != QualNone) + result = db.make<FunctionQualType>(result, cv); + db.Names.push_back(result); first = t; } else @@ -4656,12 +6058,12 @@ parse_encoding(const char* first, const char* last, C& db) // _block_invoke<decimal-digit>+ // _block_invoke_<decimal-digit>+ -template <class C> const char* -parse_block_invoke(const char* first, const char* last, C& db) +parse_block_invoke(const char* first, const char* last, Db& db) { if (last - first >= 13) { + // FIXME: strcmp? const char test[] = "_block_invoke"; const char* t = first; for (int i = 0; i < 13; ++i, ++t) @@ -4682,9 +6084,11 @@ parse_block_invoke(const char* first, const char* last, C& db) while (t != last && isdigit(*t)) ++t; } - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first.insert(0, "invocation function for block in "); + db.Names.back() = + db.make<SpecialName>("invocation function for block in ", + db.Names.back()); first = t; } return first; @@ -4693,15 +6097,15 @@ parse_block_invoke(const char* first, const char* last, C& db) // extension // <dot-suffix> := .<anything and everything> -template <class C> const char* -parse_dot_suffix(const char* first, const char* last, C& db) +parse_dot_suffix(const char* first, const char* last, Db& db) { if (first != last && *first == '.') { - if (db.names.empty()) + if (db.Names.empty()) return first; - db.names.back().first += " (" + typename C::String(first, last) + ")"; + db.Names.back() = + db.make<DotSuffix>(db.Names.back(), StringView(first, last)); first = last; } return first; @@ -4713,9 +6117,8 @@ parse_dot_suffix(const char* first, const char* last, C& db) // <mangled-name> ::= _Z<encoding> // ::= <type> -template <class C> void -demangle(const char* first, const char* last, C& db, int& status) +demangle(const char* first, const char* last, Db& db, int& status) { if (first >= last) { @@ -4758,216 +6161,10 @@ demangle(const char* first, const char* last, C& db, int& status) if (t != last) status = invalid_mangled_name; } - if (status == success && db.names.empty()) + if (status == success && db.Names.empty()) status = invalid_mangled_name; } -template <std::size_t N> -class arena -{ - static const std::size_t alignment = 16; - alignas(alignment) char buf_[N]; - char* ptr_; - - std::size_t - align_up(std::size_t n) noexcept - {return (n + (alignment-1)) & ~(alignment-1);} - - bool - pointer_in_buffer(char* p) noexcept - {return buf_ <= p && p <= buf_ + N;} - -public: - arena() noexcept : ptr_(buf_) {} - ~arena() {ptr_ = nullptr;} - arena(const arena&) = delete; - arena& operator=(const arena&) = delete; - - char* allocate(std::size_t n); - void deallocate(char* p, std::size_t n) noexcept; - - static constexpr std::size_t size() {return N;} - std::size_t used() const {return static_cast<std::size_t>(ptr_ - buf_);} - void reset() {ptr_ = buf_;} -}; - -template <std::size_t N> -char* -arena<N>::allocate(std::size_t n) -{ - n = align_up(n); - if (static_cast<std::size_t>(buf_ + N - ptr_) >= n) - { - char* r = ptr_; - ptr_ += n; - return r; - } - return static_cast<char*>(std::malloc(n)); -} - -template <std::size_t N> -void -arena<N>::deallocate(char* p, std::size_t n) noexcept -{ - if (pointer_in_buffer(p)) - { - n = align_up(n); - if (p + n == ptr_) - ptr_ = p; - } - else - std::free(p); -} - -template <class T, std::size_t N> -class short_alloc -{ - arena<N>& a_; -public: - typedef T value_type; - -public: - template <class _Up> struct rebind {typedef short_alloc<_Up, N> other;}; - - short_alloc(arena<N>& a) noexcept : a_(a) {} - template <class U> - short_alloc(const short_alloc<U, N>& a) noexcept - : a_(a.a_) {} - short_alloc(const short_alloc&) = default; - short_alloc& operator=(const short_alloc&) = delete; - - T* allocate(std::size_t n) - { - return reinterpret_cast<T*>(a_.allocate(n*sizeof(T))); - } - void deallocate(T* p, std::size_t n) noexcept - { - a_.deallocate(reinterpret_cast<char*>(p), n*sizeof(T)); - } - - template <class T1, std::size_t N1, class U, std::size_t M> - friend - bool - operator==(const short_alloc<T1, N1>& x, const short_alloc<U, M>& y) noexcept; - - template <class U, std::size_t M> friend class short_alloc; -}; - -template <class T, std::size_t N, class U, std::size_t M> -inline -bool -operator==(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept -{ - return N == M && &x.a_ == &y.a_; -} - -template <class T, std::size_t N, class U, std::size_t M> -inline -bool -operator!=(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept -{ - return !(x == y); -} - -template <class T> -class malloc_alloc -{ -public: - typedef T value_type; - typedef T& reference; - typedef const T& const_reference; - typedef T* pointer; - typedef const T* const_pointer; - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; - - malloc_alloc() = default; - template <class U> malloc_alloc(const malloc_alloc<U>&) noexcept {} - - T* allocate(std::size_t n) - { - return static_cast<T*>(std::malloc(n*sizeof(T))); - } - void deallocate(T* p, std::size_t) noexcept - { - std::free(p); - } - - template <class U> struct rebind { using other = malloc_alloc<U>; }; - template <class U, class... Args> - void construct(U* p, Args&&... args) - { - ::new ((void*)p) U(std::forward<Args>(args)...); - } - void destroy(T* p) - { - p->~T(); - } -}; - -template <class T, class U> -inline -bool -operator==(const malloc_alloc<T>&, const malloc_alloc<U>&) noexcept -{ - return true; -} - -template <class T, class U> -inline -bool -operator!=(const malloc_alloc<T>& x, const malloc_alloc<U>& y) noexcept -{ - return !(x == y); -} - -const size_t bs = 4 * 1024; -template <class T> using Alloc = short_alloc<T, bs>; -template <class T> using Vector = std::vector<T, Alloc<T>>; - -template <class StrT> -struct string_pair -{ - StrT first; - StrT second; - - string_pair() = default; - string_pair(StrT f) : first(std::move(f)) {} - string_pair(StrT f, StrT s) - : first(std::move(f)), second(std::move(s)) {} - template <size_t N> - string_pair(const char (&s)[N]) : first(s, N-1) {} - - size_t size() const {return first.size() + second.size();} - StrT full() const {return first + second;} - StrT move_full() {return std::move(first) + std::move(second);} -}; - -struct Db -{ - typedef std::basic_string<char, std::char_traits<char>, - malloc_alloc<char>> String; - typedef Vector<string_pair<String>> sub_type; - typedef Vector<sub_type> template_param_type; - sub_type names; - template_param_type subs; - Vector<template_param_type> template_param; - unsigned cv; - unsigned ref; - unsigned encoding_depth; - bool parsed_ctor_dtor_cv; - bool tag_templates; - bool fix_forward_references; - bool try_to_parse_template_args; - - template <size_t N> - Db(arena<N>& ar) : - names(ar), - subs(0, names, ar), - template_param(0, subs, ar) - {} -}; - } // unnamed namespace extern "C" _LIBCXXABI_FUNC_VIS char * @@ -4978,56 +6175,44 @@ __cxa_demangle(const char *mangled_name, char *buf, size_t *n, int *status) { *status = invalid_args; return nullptr; } + size_t internal_size = buf != nullptr ? *n : 0; - arena<bs> a; - Db db(a); - db.cv = 0; - db.ref = 0; - db.encoding_depth = 0; - db.parsed_ctor_dtor_cv = false; - db.tag_templates = true; - db.template_param.emplace_back(a); - db.fix_forward_references = false; - db.try_to_parse_template_args = true; + Db db; int internal_status = success; size_t len = std::strlen(mangled_name); demangle(mangled_name, mangled_name + len, db, internal_status); - if (internal_status == success && db.fix_forward_references && - !db.template_param.empty() && !db.template_param.front().empty()) + + if (internal_status == success && db.FixForwardReferences && + !db.TemplateParams.empty()) { - db.fix_forward_references = false; - db.tag_templates = false; - db.names.clear(); - db.subs.clear(); + db.FixForwardReferences = false; + db.TagTemplates = false; + db.Names.clear(); + db.Subs.clear(); demangle(mangled_name, mangled_name + len, db, internal_status); - if (db.fix_forward_references) + if (db.FixForwardReferences) internal_status = invalid_mangled_name; } + if (internal_status == success) { - size_t sz = db.names.back().size() + 1; - if (sz > internal_size) + if (!buf) { - char* newbuf = static_cast<char*>(std::realloc(buf, sz)); - if (newbuf == nullptr) - { - internal_status = memory_alloc_failure; - buf = nullptr; - } - else - { - buf = newbuf; - if (n != nullptr) - *n = sz; - } + internal_size = 1024; + buf = static_cast<char*>(std::malloc(internal_size)); } - if (buf != nullptr) + + if (buf) { - db.names.back().first += db.names.back().second; - std::memcpy(buf, db.names.back().first.data(), sz-1); - buf[sz-1] = char(0); + OutputStream s(buf, internal_size); + db.Names.back()->print(s); + s += '\0'; + if (n) *n = s.getCurrentPosition(); + buf = s.getBuffer(); } + else + internal_status = memory_alloc_failure; } else buf = nullptr; diff --git a/lib/libcxxabi/src/cxa_exception.cpp b/lib/libcxxabi/src/cxa_exception.cpp index 50d1a468cea..d5230cdc7d9 100644 --- a/lib/libcxxabi/src/cxa_exception.cpp +++ b/lib/libcxxabi/src/cxa_exception.cpp @@ -11,17 +11,17 @@ // //===----------------------------------------------------------------------===// -#include "config.h" #include "cxxabi.h" #include <exception> // for std::terminate -#include <cstdlib> // for malloc, free #include <cstring> // for memset -#if !LIBCXXABI_HAS_NO_THREADS -# include <pthread.h> // for fallback_malloc.ipp's mutexes -#endif #include "cxa_exception.hpp" #include "cxa_handlers.hpp" +#include "fallback_malloc.h" + +#if __has_feature(address_sanitizer) +extern "C" void __asan_handle_no_return(void); +#endif // +---------------------------+-----------------------------+---------------+ // | __cxa_exception | _Unwind_Exception CLNGC++\0 | thrown object | @@ -36,8 +36,6 @@ namespace __cxxabiv1 { -#pragma GCC visibility push(default) - // Utility routines static inline @@ -68,12 +66,16 @@ cxa_exception_from_exception_unwind_exception(_Unwind_Exception* unwind_exceptio return cxa_exception_from_thrown_object(unwind_exception + 1 ); } -static -inline -size_t -cxa_exception_size_from_exception_thrown_size(size_t size) -{ - return size + sizeof (__cxa_exception); +// Round s up to next multiple of a. +static inline +size_t aligned_allocation_size(size_t s, size_t a) { + return (s + a - 1) & ~(a - 1); +} + +static inline +size_t cxa_exception_size_from_exception_thrown_size(size_t size) { + return aligned_allocation_size(size + sizeof (__cxa_exception), + alignof(__cxa_exception)); } static void setExceptionClass(_Unwind_Exception* unwind_exception) { @@ -104,20 +106,6 @@ static inline int decrementHandlerCount(__cxa_exception *exception) { return --exception->handlerCount; } -#include "fallback_malloc.ipp" - -// Allocate some memory from _somewhere_ -static void *do_malloc(size_t size) { - void *ptr = std::malloc(size); - if (NULL == ptr) // if malloc fails, fall back to emergency stash - ptr = fallback_malloc(size); - return ptr; -} - -static void do_free(void *ptr) { - is_fallback_ptr(ptr) ? fallback_free(ptr) : std::free(ptr); -} - /* If reason isn't _URC_FOREIGN_EXCEPTION_CAUGHT, then the terminateHandler stored in exc is called. Otherwise the exceptionDestructor stored in @@ -137,7 +125,7 @@ exception_cleanup_func(_Unwind_Reason_Code reason, _Unwind_Exception* unwind_exc __cxa_decrement_exception_refcount(unwind_exception + 1); } -static LIBCXXABI_NORETURN void failed_throw(__cxa_exception* exception_header) { +static _LIBCXXABI_NORETURN void failed_throw(__cxa_exception* exception_header) { // Section 2.5.3 says: // * For purposes of this ABI, several things are considered exception handlers: // ** A terminate() call due to a throw. @@ -149,6 +137,28 @@ static LIBCXXABI_NORETURN void failed_throw(__cxa_exception* exception_header) { std::__terminate(exception_header->terminateHandler); } +// Return the offset of the __cxa_exception header from the start of the +// allocated buffer. If __cxa_exception's alignment is smaller than the maximum +// useful alignment for the target machine, padding has to be inserted before +// the header to ensure the thrown object that follows the header is +// sufficiently aligned. This happens if _Unwind_exception isn't double-word +// aligned (on Darwin, for example). +static size_t get_cxa_exception_offset() { + struct S { + } __attribute__((aligned)); + + // Compute the maximum alignment for the target machine. + constexpr size_t alignment = std::alignment_of<S>::value; + constexpr size_t excp_size = sizeof(__cxa_exception); + constexpr size_t aligned_size = + (excp_size + alignment - 1) / alignment * alignment; + constexpr size_t offset = aligned_size - excp_size; + static_assert((offset == 0 || + std::alignment_of<_Unwind_Exception>::value < alignment), + "offset is non-zero only if _Unwind_Exception isn't aligned"); + return offset; +} + extern "C" { // Allocate a __cxa_exception object, and zero-fill it. @@ -156,19 +166,30 @@ extern "C" { // object. Zero-fill the object. If memory can't be allocated, call // std::terminate. Return a pointer to the memory to be used for the // user's exception object. -_LIBCXXABI_FUNC_VIS void *__cxa_allocate_exception(size_t thrown_size) throw() { +void *__cxa_allocate_exception(size_t thrown_size) throw() { size_t actual_size = cxa_exception_size_from_exception_thrown_size(thrown_size); - __cxa_exception* exception_header = static_cast<__cxa_exception*>(do_malloc(actual_size)); - if (NULL == exception_header) + + // Allocate extra space before the __cxa_exception header to ensure the + // start of the thrown object is sufficiently aligned. + size_t header_offset = get_cxa_exception_offset(); + char *raw_buffer = + (char *)__aligned_malloc_with_fallback(header_offset + actual_size); + if (NULL == raw_buffer) std::terminate(); + __cxa_exception *exception_header = + static_cast<__cxa_exception *>((void *)(raw_buffer + header_offset)); std::memset(exception_header, 0, actual_size); return thrown_object_from_cxa_exception(exception_header); } // Free a __cxa_exception object allocated with __cxa_allocate_exception. -_LIBCXXABI_FUNC_VIS void __cxa_free_exception(void *thrown_object) throw() { - do_free(cxa_exception_from_thrown_object(thrown_object)); +void __cxa_free_exception(void *thrown_object) throw() { + // Compute the size of the padding before the header. + size_t header_offset = get_cxa_exception_offset(); + char *raw_buffer = + ((char *)cxa_exception_from_thrown_object(thrown_object)) - header_offset; + __aligned_free_with_fallback((void *)raw_buffer); } @@ -177,7 +198,7 @@ _LIBCXXABI_FUNC_VIS void __cxa_free_exception(void *thrown_object) throw() { // Otherwise, it will work like __cxa_allocate_exception. void * __cxa_allocate_dependent_exception () { size_t actual_size = sizeof(__cxa_dependent_exception); - void *ptr = do_malloc(actual_size); + void *ptr = __aligned_malloc_with_fallback(actual_size); if (NULL == ptr) std::terminate(); std::memset(ptr, 0, actual_size); @@ -188,7 +209,7 @@ void * __cxa_allocate_dependent_exception () { // This function shall free a dependent_exception. // It does not affect the reference count of the primary exception. void __cxa_free_dependent_exception (void * dependent_exception) { - do_free(dependent_exception); + __aligned_free_with_fallback(dependent_exception); } @@ -218,7 +239,7 @@ handler, _Unwind_RaiseException may return. In that case, __cxa_throw will call terminate, assuming that there was no handler for the exception. */ -_LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void +void __cxa_throw(void *thrown_object, std::type_info *tinfo, void (*dest)(void *)) { __cxa_eh_globals *globals = __cxa_get_globals(); __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); @@ -232,6 +253,12 @@ __cxa_throw(void *thrown_object, std::type_info *tinfo, void (*dest)(void *)) { globals->uncaughtExceptions += 1; // Not atomically, since globals are thread-local exception_header->unwindHeader.exception_cleanup = exception_cleanup_func; + +#if __has_feature(address_sanitizer) + // Inform the ASan runtime that now might be a good time to clean stuff up. + __asan_handle_no_return(); +#endif + #ifdef __USING_SJLJ_EXCEPTIONS__ _Unwind_SjLj_RaiseException(&exception_header->unwindHeader); #else @@ -251,9 +278,8 @@ The adjusted pointer is computed by the personality routine during phase 1 Requires: exception is native */ -_LIBCXXABI_FUNC_VIS void *__cxa_get_exception_ptr(void *unwind_exception) throw() { -#if LIBCXXABI_ARM_EHABI +#if defined(_LIBCXXABI_ARM_EHABI) return reinterpret_cast<void*>( static_cast<_Unwind_Control_Block*>(unwind_exception)->barrier_cache.bitpattern[0]); #else @@ -262,12 +288,11 @@ void *__cxa_get_exception_ptr(void *unwind_exception) throw() { #endif } -#if LIBCXXABI_ARM_EHABI +#if defined(_LIBCXXABI_ARM_EHABI) /* The routine to be called before the cleanup. This will save __cxa_exception in __cxa_eh_globals, so that __cxa_end_cleanup() can recover later. */ -_LIBCXXABI_FUNC_VIS bool __cxa_begin_cleanup(void *unwind_arg) throw() { _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(unwind_arg); __cxa_eh_globals* globals = __cxa_get_globals(); @@ -345,7 +370,7 @@ asm ( " bl abort\n" " .popsection" ); -#endif // LIBCXXABI_ARM_EHABI +#endif // defined(_LIBCXXABI_ARM_EHABI) /* This routine can catch foreign or native exceptions. If native, the exception @@ -405,7 +430,7 @@ __cxa_begin_catch(void* unwind_arg) throw() globals->caughtExceptions = exception_header; } globals->uncaughtExceptions -= 1; // Not atomically, since globals are thread-local -#if LIBCXXABI_ARM_EHABI +#if defined(_LIBCXXABI_ARM_EHABI) return reinterpret_cast<void*>(exception_header->unwindHeader.barrier_cache.bitpattern[0]); #else return exception_header->adjustedPtr; @@ -439,7 +464,7 @@ For a foreign exception: * If it has been rethrown, there is nothing to do. * Otherwise delete the exception and pop the catch stack to empty. */ -_LIBCXXABI_FUNC_VIS void __cxa_end_catch() { +void __cxa_end_catch() { static_assert(sizeof(__cxa_exception) == sizeof(__cxa_dependent_exception), "sizeof(__cxa_exception) must be equal to " "sizeof(__cxa_dependent_exception)"); @@ -516,7 +541,7 @@ _LIBCXXABI_FUNC_VIS void __cxa_end_catch() { // Note: exception_header may be masquerading as a __cxa_dependent_exception // and that's ok. exceptionType is there too. // However watch out for foreign exceptions. Return null for them. -_LIBCXXABI_FUNC_VIS std::type_info *__cxa_current_exception_type() { +std::type_info *__cxa_current_exception_type() { // get the current exception __cxa_eh_globals *globals = __cxa_get_globals_fast(); if (NULL == globals) @@ -541,7 +566,7 @@ If the exception is native: Note: exception_header may be masquerading as a __cxa_dependent_exception and that's ok. */ -_LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void __cxa_rethrow() { +void __cxa_rethrow() { __cxa_eh_globals* globals = __cxa_get_globals(); __cxa_exception* exception_header = globals->caughtExceptions; if (NULL == exception_header) @@ -586,7 +611,7 @@ _LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN void __cxa_rethrow() { Requires: If thrown_object is not NULL, it is a native exception. */ -_LIBCXXABI_FUNC_VIS void +void __cxa_increment_exception_refcount(void *thrown_object) throw() { if (thrown_object != NULL ) { @@ -603,7 +628,7 @@ __cxa_increment_exception_refcount(void *thrown_object) throw() { Requires: If thrown_object is not NULL, it is a native exception. */ -_LIBCXXABI_FUNC_VIS void +void __cxa_decrement_exception_refcount(void *thrown_object) throw() { if (thrown_object != NULL ) { @@ -627,7 +652,7 @@ __cxa_decrement_exception_refcount(void *thrown_object) throw() { been no exceptions thrown, ever, on this thread, we can return NULL without the need to allocate the exception-handling globals. */ -_LIBCXXABI_FUNC_VIS void *__cxa_current_primary_exception() throw() { +void *__cxa_current_primary_exception() throw() { // get the current exception __cxa_eh_globals* globals = __cxa_get_globals_fast(); if (NULL == globals) @@ -713,6 +738,4 @@ __cxa_uncaught_exceptions() throw() } // extern "C" -#pragma GCC visibility pop - } // abi diff --git a/lib/libcxxabi/src/cxa_exception.hpp b/lib/libcxxabi/src/cxa_exception.hpp index 6e68f985389..c8b0fb1678e 100644 --- a/lib/libcxxabi/src/cxa_exception.hpp +++ b/lib/libcxxabi/src/cxa_exception.hpp @@ -15,19 +15,17 @@ #define _CXA_EXCEPTION_H #include <exception> // for std::unexpected_handler and std::terminate_handler -#include <cxxabi.h> +#include "cxxabi.h" #include "unwind.h" namespace __cxxabiv1 { -#pragma GCC visibility push(hidden) - static const uint64_t kOurExceptionClass = 0x434C4E47432B2B00; // CLNGC++\0 static const uint64_t kOurDependentExceptionClass = 0x434C4E47432B2B01; // CLNGC++\1 static const uint64_t get_vendor_and_language = 0xFFFFFFFFFFFFFF00; // mask for CLNGC++ -struct __cxa_exception { -#if defined(__LP64__) || LIBCXXABI_ARM_EHABI +struct _LIBCXXABI_HIDDEN __cxa_exception { +#if defined(__LP64__) || defined(_LIBCXXABI_ARM_EHABI) // This is a new field to support C++ 0x exception_ptr. // For binary compatibility it is at the start of this // struct which is prepended to the object thrown in @@ -45,7 +43,7 @@ struct __cxa_exception { int handlerCount; -#if LIBCXXABI_ARM_EHABI +#if defined(_LIBCXXABI_ARM_EHABI) __cxa_exception* nextPropagatingException; int propagationCount; #else @@ -56,21 +54,20 @@ struct __cxa_exception { void *adjustedPtr; #endif -#if !defined(__LP64__) && !LIBCXXABI_ARM_EHABI +#if !defined(__LP64__) && !defined(_LIBCXXABI_ARM_EHABI) // This is a new field to support C++ 0x exception_ptr. // For binary compatibility it is placed where the compiler // previously adding padded to 64-bit align unwindHeader. size_t referenceCount; #endif - _Unwind_Exception unwindHeader; }; // http://sourcery.mentor.com/archives/cxx-abi-dev/msg01924.html // The layout of this structure MUST match the layout of __cxa_exception, with // primaryException instead of referenceCount. -struct __cxa_dependent_exception { -#if defined(__LP64__) || LIBCXXABI_ARM_EHABI +struct _LIBCXXABI_HIDDEN __cxa_dependent_exception { +#if defined(__LP64__) || defined(_LIBCXXABI_ARM_EHABI) void* primaryException; #endif @@ -83,7 +80,7 @@ struct __cxa_dependent_exception { int handlerCount; -#if LIBCXXABI_ARM_EHABI +#if defined(_LIBCXXABI_ARM_EHABI) __cxa_exception* nextPropagatingException; int propagationCount; #else @@ -94,31 +91,25 @@ struct __cxa_dependent_exception { void *adjustedPtr; #endif -#if !defined(__LP64__) && !LIBCXXABI_ARM_EHABI +#if !defined(__LP64__) && !defined(_LIBCXXABI_ARM_EHABI) void* primaryException; #endif - _Unwind_Exception unwindHeader; }; -struct __cxa_eh_globals { +struct _LIBCXXABI_HIDDEN __cxa_eh_globals { __cxa_exception * caughtExceptions; unsigned int uncaughtExceptions; -#if LIBCXXABI_ARM_EHABI +#if defined(_LIBCXXABI_ARM_EHABI) __cxa_exception* propagatingExceptions; #endif }; -#pragma GCC visibility pop -#pragma GCC visibility push(default) - -extern "C" __cxa_eh_globals * __cxa_get_globals (); -extern "C" __cxa_eh_globals * __cxa_get_globals_fast (); - -extern "C" void * __cxa_allocate_dependent_exception (); -extern "C" void __cxa_free_dependent_exception (void * dependent_exception); +extern "C" _LIBCXXABI_FUNC_VIS __cxa_eh_globals * __cxa_get_globals (); +extern "C" _LIBCXXABI_FUNC_VIS __cxa_eh_globals * __cxa_get_globals_fast (); -#pragma GCC visibility pop +extern "C" _LIBCXXABI_FUNC_VIS void * __cxa_allocate_dependent_exception (); +extern "C" _LIBCXXABI_FUNC_VIS void __cxa_free_dependent_exception (void * dependent_exception); } // namespace __cxxabiv1 diff --git a/lib/libcxxabi/src/cxa_exception_storage.cpp b/lib/libcxxabi/src/cxa_exception_storage.cpp index a39b6db005f..c641e0225f8 100644 --- a/lib/libcxxabi/src/cxa_exception_storage.cpp +++ b/lib/libcxxabi/src/cxa_exception_storage.cpp @@ -13,9 +13,9 @@ #include "cxa_exception.hpp" -#include "config.h" +#include <__threading_support> -#if LIBCXXABI_HAS_NO_THREADS +#if defined(_LIBCXXABI_HAS_NO_THREADS) namespace __cxxabiv1 { extern "C" { @@ -44,28 +44,27 @@ extern "C" { #else -#include <pthread.h> -#include <cstdlib> // for calloc, free #include "abort_message.h" +#include "fallback_malloc.h" -// In general, we treat all pthread errors as fatal. +// In general, we treat all threading errors as fatal. // We cannot call std::terminate() because that will in turn // call __cxa_get_globals() and cause infinite recursion. namespace __cxxabiv1 { namespace { - pthread_key_t key_; - pthread_once_t flag_ = PTHREAD_ONCE_INIT; + std::__libcpp_tls_key key_; + std::__libcpp_exec_once_flag flag_ = _LIBCPP_EXEC_ONCE_INITIALIZER; - void destruct_ (void *p) { - std::free ( p ); - if ( 0 != ::pthread_setspecific ( key_, NULL ) ) + void _LIBCPP_TLS_DESTRUCTOR_CC destruct_ (void *p) { + __free_with_fallback ( p ); + if ( 0 != std::__libcpp_tls_set ( key_, NULL ) ) abort_message("cannot zero out thread value for __cxa_get_globals()"); } void construct_ () { - if ( 0 != pthread_key_create ( &key_, destruct_ ) ) - abort_message("cannot create pthread key for __cxa_get_globals()"); + if ( 0 != std::__libcpp_tls_create ( &key_, destruct_ ) ) + abort_message("cannot create thread specific key for __cxa_get_globals()"); } } @@ -77,11 +76,11 @@ extern "C" { // If this is the first time we've been asked for these globals, create them if ( NULL == retVal ) { retVal = static_cast<__cxa_eh_globals*> - (std::calloc (1, sizeof (__cxa_eh_globals))); + (__calloc_with_fallback (1, sizeof (__cxa_eh_globals))); if ( NULL == retVal ) abort_message("cannot allocate __cxa_eh_globals"); - if ( 0 != pthread_setspecific ( key_, retVal ) ) - abort_message("pthread_setspecific failure in __cxa_get_globals()"); + if ( 0 != std::__libcpp_tls_set ( key_, retVal ) ) + abort_message("std::__libcpp_tls_set failure in __cxa_get_globals()"); } return retVal; } @@ -92,10 +91,10 @@ extern "C" { // libc++abi. __cxa_eh_globals * __cxa_get_globals_fast () { // First time through, create the key. - if (0 != pthread_once(&flag_, construct_)) - abort_message("pthread_once failure in __cxa_get_globals_fast()"); + if (0 != std::__libcpp_execute_once(&flag_, construct_)) + abort_message("execute once failure in __cxa_get_globals_fast()"); // static int init = construct_(); - return static_cast<__cxa_eh_globals*>(::pthread_getspecific(key_)); + return static_cast<__cxa_eh_globals*>(std::__libcpp_tls_get(key_)); } } diff --git a/lib/libcxxabi/src/cxa_guard.cpp b/lib/libcxxabi/src/cxa_guard.cpp index 72e868f787e..f4c2a184dc5 100644 --- a/lib/libcxxabi/src/cxa_guard.cpp +++ b/lib/libcxxabi/src/cxa_guard.cpp @@ -10,11 +10,8 @@ #include "__cxxabi_config.h" #include "abort_message.h" -#include "config.h" +#include <__threading_support> -#if !LIBCXXABI_HAS_NO_THREADS -# include <pthread.h> -#endif #include <stdint.h> /* @@ -22,9 +19,9 @@ which will turn around and try to call __cxa_guard_acquire reentrantly. For this reason, the headers of this file are as restricted as possible. Previous implementations of this code for __APPLE__ have used - pthread_mutex_lock and the abort_message utility without problem. This - implementation also uses pthread_cond_wait which has tested to not be a - problem. + std::__libcpp_mutex_lock and the abort_message utility without problem. This + implementation also uses std::__libcpp_condvar_wait which has tested + to not be a problem. */ namespace __cxxabiv1 @@ -34,39 +31,43 @@ namespace { #ifdef __arm__ - // A 32-bit, 4-byte-aligned static data value. The least significant 2 bits must // be statically initialized to 0. typedef uint32_t guard_type; -// Test the lowest bit. -inline bool is_initialized(guard_type* guard_object) { - return (*guard_object) & 1; -} - inline void set_initialized(guard_type* guard_object) { *guard_object |= 1; } - #else - typedef uint64_t guard_type; -bool is_initialized(guard_type* guard_object) { +void set_initialized(guard_type* guard_object) { char* initialized = (char*)guard_object; - return *initialized; + *initialized = 1; } +#endif -void set_initialized(guard_type* guard_object) { +#if defined(_LIBCXXABI_HAS_NO_THREADS) || (defined(__APPLE__) && !defined(__arm__)) +#ifdef __arm__ + +// Test the lowest bit. +inline bool is_initialized(guard_type* guard_object) { + return (*guard_object) & 1; +} + +#else + +bool is_initialized(guard_type* guard_object) { char* initialized = (char*)guard_object; - *initialized = 1; + return *initialized; } #endif +#endif -#if !LIBCXXABI_HAS_NO_THREADS -pthread_mutex_t guard_mut = PTHREAD_MUTEX_INITIALIZER; -pthread_cond_t guard_cv = PTHREAD_COND_INITIALIZER; +#ifndef _LIBCXXABI_HAS_NO_THREADS +std::__libcpp_mutex_t guard_mut = _LIBCPP_MUTEX_INITIALIZER; +std::__libcpp_condvar_t guard_cv = _LIBCPP_CONDVAR_INITIALIZER; #endif #if defined(__APPLE__) && !defined(__arm__) @@ -111,9 +112,10 @@ set_lock(uint64_t& x, lock_type y) typedef bool lock_type; -inline -lock_type -get_lock(uint64_t x) +#if !defined(__arm__) +static_assert(std::is_same<guard_type, uint64_t>::value, ""); + +inline lock_type get_lock(uint64_t x) { union { @@ -123,9 +125,7 @@ get_lock(uint64_t x) return f.lock[1] != 0; } -inline -void -set_lock(uint64_t& x, lock_type y) +inline void set_lock(uint64_t& x, lock_type y) { union { @@ -135,10 +135,10 @@ set_lock(uint64_t& x, lock_type y) f.lock[1] = y; x = f.guard; } +#else // defined(__arm__) +static_assert(std::is_same<guard_type, uint32_t>::value, ""); -inline -lock_type -get_lock(uint32_t x) +inline lock_type get_lock(uint32_t x) { union { @@ -148,9 +148,7 @@ get_lock(uint32_t x) return f.lock[1] != 0; } -inline -void -set_lock(uint32_t& x, lock_type y) +inline void set_lock(uint32_t& x, lock_type y) { union { @@ -161,38 +159,37 @@ set_lock(uint32_t& x, lock_type y) x = f.guard; } -#endif // __APPLE__ +#endif // !defined(__arm__) + +#endif // __APPLE__ && !__arm__ } // unnamed namespace extern "C" { -#if LIBCXXABI_HAS_NO_THREADS -_LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) { - return !is_initialized(guard_object); -} - -_LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) { - *guard_object = 0; - set_initialized(guard_object); -} - -_LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) { - *guard_object = 0; -} - -#else // !LIBCXXABI_HAS_NO_THREADS - +#ifndef _LIBCXXABI_HAS_NO_THREADS _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) { char* initialized = (char*)guard_object; - if (pthread_mutex_lock(&guard_mut)) + if (std::__libcpp_mutex_lock(&guard_mut)) abort_message("__cxa_guard_acquire failed to acquire mutex"); int result = *initialized == 0; if (result) { #if defined(__APPLE__) && !defined(__arm__) - const lock_type id = pthread_mach_thread_np(pthread_self()); + // This is a special-case pthread dependency for Mac. We can't pull this + // out into libcxx's threading API (__threading_support) because not all + // supported Mac environments provide this function (in pthread.h). To + // make it possible to build/use libcxx in those environments, we have to + // keep this pthread dependency local to libcxxabi. If there is some + // convenient way to detect precisely when pthread_mach_thread_np is + // available in a given Mac environment, it might still be possible to + // bury this dependency in __threading_support. + #ifdef _LIBCPP_HAS_THREAD_API_PTHREAD + const lock_type id = pthread_mach_thread_np(std::__libcpp_thread_get_current_id()); + #else + #error "How do I pthread_mach_thread_np()?" + #endif lock_type lock = get_lock(*guard_object); if (lock) { @@ -201,7 +198,7 @@ _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) { abort_message("__cxa_guard_acquire detected deadlock"); do { - if (pthread_cond_wait(&guard_cv, &guard_mut)) + if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut)) abort_message("__cxa_guard_acquire condition variable wait failed"); lock = get_lock(*guard_object); } while (lock); @@ -213,40 +210,55 @@ _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) { set_lock(*guard_object, id); #else // !__APPLE__ || __arm__ while (get_lock(*guard_object)) - if (pthread_cond_wait(&guard_cv, &guard_mut)) + if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut)) abort_message("__cxa_guard_acquire condition variable wait failed"); result = *initialized == 0; if (result) set_lock(*guard_object, true); #endif // !__APPLE__ || __arm__ } - if (pthread_mutex_unlock(&guard_mut)) + if (std::__libcpp_mutex_unlock(&guard_mut)) abort_message("__cxa_guard_acquire failed to release mutex"); return result; } _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) { - if (pthread_mutex_lock(&guard_mut)) + if (std::__libcpp_mutex_lock(&guard_mut)) abort_message("__cxa_guard_release failed to acquire mutex"); *guard_object = 0; set_initialized(guard_object); - if (pthread_mutex_unlock(&guard_mut)) + if (std::__libcpp_mutex_unlock(&guard_mut)) abort_message("__cxa_guard_release failed to release mutex"); - if (pthread_cond_broadcast(&guard_cv)) + if (std::__libcpp_condvar_broadcast(&guard_cv)) abort_message("__cxa_guard_release failed to broadcast condition variable"); } _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) { - if (pthread_mutex_lock(&guard_mut)) + if (std::__libcpp_mutex_lock(&guard_mut)) abort_message("__cxa_guard_abort failed to acquire mutex"); *guard_object = 0; - if (pthread_mutex_unlock(&guard_mut)) + if (std::__libcpp_mutex_unlock(&guard_mut)) abort_message("__cxa_guard_abort failed to release mutex"); - if (pthread_cond_broadcast(&guard_cv)) + if (std::__libcpp_condvar_broadcast(&guard_cv)) abort_message("__cxa_guard_abort failed to broadcast condition variable"); } -#endif // !LIBCXXABI_HAS_NO_THREADS +#else // _LIBCXXABI_HAS_NO_THREADS + +_LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) { + return !is_initialized(guard_object); +} + +_LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) { + *guard_object = 0; + set_initialized(guard_object); +} + +_LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) { + *guard_object = 0; +} + +#endif // !_LIBCXXABI_HAS_NO_THREADS } // extern "C" diff --git a/lib/libcxxabi/src/cxa_handlers.cpp b/lib/libcxxabi/src/cxa_handlers.cpp index 3f781313df8..2881a2a65fe 100644 --- a/lib/libcxxabi/src/cxa_handlers.cpp +++ b/lib/libcxxabi/src/cxa_handlers.cpp @@ -14,7 +14,6 @@ #include <new> #include <exception> #include "abort_message.h" -#include "config.h" #include "cxxabi.h" #include "cxa_handlers.hpp" #include "cxa_exception.hpp" @@ -32,7 +31,6 @@ get_unexpected() _NOEXCEPT // return __cxa_unexpected_handler.load(memory_order_acq); } -__attribute__((visibility("hidden"), noreturn)) void __unexpected(unexpected_handler func) { @@ -57,7 +55,6 @@ get_terminate() _NOEXCEPT // return __cxa_terminate_handler.load(memory_order_acq); } -__attribute__((visibility("hidden"), noreturn)) void __terminate(terminate_handler func) _NOEXCEPT { @@ -104,7 +101,9 @@ terminate() _NOEXCEPT // In the future this will become: // std::atomic<std::new_handler> __cxa_new_handler(0); -extern "C" _LIBCXXABI_DATA_VIS new_handler __cxa_new_handler = 0; +extern "C" { +new_handler __cxa_new_handler = 0; +} new_handler set_new_handler(new_handler handler) _NOEXCEPT diff --git a/lib/libcxxabi/src/cxa_handlers.hpp b/lib/libcxxabi/src/cxa_handlers.hpp index ce567ec1471..14c0e10119d 100644 --- a/lib/libcxxabi/src/cxa_handlers.hpp +++ b/lib/libcxxabi/src/cxa_handlers.hpp @@ -13,16 +13,18 @@ #ifndef _CXA_HANDLERS_H #define _CXA_HANDLERS_H +#include <__cxxabi_config.h> + #include <exception> namespace std { -__attribute__((visibility("hidden"), noreturn)) +_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN void __unexpected(unexpected_handler func); -__attribute__((visibility("hidden"), noreturn)) +_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN void __terminate(terminate_handler func) _NOEXCEPT; @@ -31,9 +33,9 @@ __terminate(terminate_handler func) _NOEXCEPT; extern "C" { -extern void (*__cxa_terminate_handler)(); -extern void (*__cxa_unexpected_handler)(); -extern void (*__cxa_new_handler)(); +_LIBCXXABI_DATA_VIS extern void (*__cxa_terminate_handler)(); +_LIBCXXABI_DATA_VIS extern void (*__cxa_unexpected_handler)(); +_LIBCXXABI_DATA_VIS extern void (*__cxa_new_handler)(); /* diff --git a/lib/libcxxabi/src/cxa_noexception.cpp b/lib/libcxxabi/src/cxa_noexception.cpp index e45ceff0165..adda552b89f 100644 --- a/lib/libcxxabi/src/cxa_noexception.cpp +++ b/lib/libcxxabi/src/cxa_noexception.cpp @@ -13,7 +13,6 @@ // Support functions for the no-exceptions libc++ library -#include "config.h" #include "cxxabi.h" #include <exception> // for std::terminate @@ -22,8 +21,6 @@ namespace __cxxabiv1 { -#pragma GCC visibility push(default) - extern "C" { void @@ -55,6 +52,4 @@ __cxa_uncaught_exceptions() throw() { return 0; } } // extern "C" -#pragma GCC visibility pop - } // abi diff --git a/lib/libcxxabi/src/cxa_personality.cpp b/lib/libcxxabi/src/cxa_personality.cpp index 85b6df2393a..7f857faf78f 100644 --- a/lib/libcxxabi/src/cxa_personality.cpp +++ b/lib/libcxxabi/src/cxa_personality.cpp @@ -18,7 +18,6 @@ #include <typeinfo> #include "__cxxabi_config.h" -#include "config.h" #include "cxa_exception.hpp" #include "cxa_handlers.hpp" #include "private_typeinfo.h" @@ -317,7 +316,7 @@ call_terminate(bool native_exception, _Unwind_Exception* unwind_exception) std::terminate(); } -#if LIBCXXABI_ARM_EHABI +#if defined(_LIBCXXABI_ARM_EHABI) static const void* read_target2_value(const void* ptr) { uintptr_t offset = *reinterpret_cast<const uintptr_t*>(ptr); @@ -328,7 +327,7 @@ static const void* read_target2_value(const void* ptr) // deferred to the linker. For bare-metal they turn into absolute // relocations. For linux they turn into GOT-REL relocations." // https://gcc.gnu.org/ml/gcc-patches/2009-08/msg00264.html -#if LIBCXXABI_BAREMETAL +#if defined(LIBCXXABI_BAREMETAL) return reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(ptr) + offset); #else @@ -348,14 +347,17 @@ get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo, call_terminate(native_exception, unwind_exception); } - assert(ttypeEncoding == DW_EH_PE_absptr && "Unexpected TTypeEncoding"); + assert(((ttypeEncoding == DW_EH_PE_absptr) || // LLVM or GCC 4.6 + (ttypeEncoding == DW_EH_PE_pcrel) || // GCC 4.7 baremetal + (ttypeEncoding == (DW_EH_PE_pcrel | DW_EH_PE_indirect))) && // GCC 4.7 linux + "Unexpected TTypeEncoding"); (void)ttypeEncoding; const uint8_t* ttypePtr = classInfo - ttypeIndex * sizeof(uintptr_t); return reinterpret_cast<const __shim_type_info *>( read_target2_value(ttypePtr)); } -#else // !LIBCXXABI_ARM_EHABI +#else // !defined(_LIBCXXABI_ARM_EHABI) static const __shim_type_info* get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo, @@ -391,7 +393,7 @@ get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo, classInfo -= ttypeIndex; return (const __shim_type_info*)readEncodedPointer(&classInfo, ttypeEncoding); } -#endif // !LIBCXXABI_ARM_EHABI +#endif // !defined(_LIBCXXABI_ARM_EHABI) /* This is checking a thrown exception type, excpType, against a possibly empty @@ -402,7 +404,7 @@ get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo, the list will catch a excpType. If any catchType in the list can catch an excpType, then this exception spec does not catch the excpType. */ -#if LIBCXXABI_ARM_EHABI +#if defined(_LIBCXXABI_ARM_EHABI) static bool exception_spec_can_catch(int64_t specIndex, const uint8_t* classInfo, @@ -415,7 +417,10 @@ exception_spec_can_catch(int64_t specIndex, const uint8_t* classInfo, call_terminate(false, unwind_exception); } - assert(ttypeEncoding == DW_EH_PE_absptr && "Unexpected TTypeEncoding"); + assert(((ttypeEncoding == DW_EH_PE_absptr) || // LLVM or GCC 4.6 + (ttypeEncoding == DW_EH_PE_pcrel) || // GCC 4.7 baremetal + (ttypeEncoding == (DW_EH_PE_pcrel | DW_EH_PE_indirect))) && // GCC 4.7 linux + "Unexpected TTypeEncoding"); (void)ttypeEncoding; // specIndex is negative of 1-based byte offset into classInfo; @@ -928,7 +933,7 @@ _UA_CLEANUP_PHASE Else a cleanup is not found: return _URC_CONTINUE_UNWIND */ -#if !LIBCXXABI_ARM_EHABI +#if !defined(_LIBCXXABI_ARM_EHABI) _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code #ifdef __USING_SJLJ_EXCEPTIONS__ __gxx_personality_sj0 @@ -1035,7 +1040,7 @@ static _Unwind_Reason_Code continue_unwind(_Unwind_Exception* unwind_exception, } // ARM register names -#if !LIBCXXABI_USE_LLVM_UNWINDER +#if !defined(LIBCXXABI_USE_LLVM_UNWINDER) static const uint32_t REG_UCB = 12; // Register to save _Unwind_Control_Block #endif static const uint32_t REG_SP = 13; @@ -1071,7 +1076,7 @@ __gxx_personality_v0(_Unwind_State state, bool native_exception = (unwind_exception->exception_class & get_vendor_and_language) == (kOurExceptionClass & get_vendor_and_language); -#if !LIBCXXABI_USE_LLVM_UNWINDER +#if !defined(LIBCXXABI_USE_LLVM_UNWINDER) // Copy the address of _Unwind_Control_Block to r12 so that // _Unwind_GetLanguageSpecificData() and _Unwind_GetRegionStart() can // return correct address. @@ -1188,7 +1193,7 @@ __cxa_call_unexpected(void* arg) u_handler = old_exception_header->unexpectedHandler; // If std::__unexpected(u_handler) rethrows the same exception, // these values get overwritten by the rethrow. So save them now: -#if LIBCXXABI_ARM_EHABI +#if defined(_LIBCXXABI_ARM_EHABI) ttypeIndex = (int64_t)(int32_t)unwind_exception->barrier_cache.bitpattern[4]; lsda = (const uint8_t*)unwind_exception->barrier_cache.bitpattern[2]; #else diff --git a/lib/libcxxabi/src/cxa_thread_atexit.cpp b/lib/libcxxabi/src/cxa_thread_atexit.cpp index 7962e28af70..49d15d6b145 100644 --- a/lib/libcxxabi/src/cxa_thread_atexit.cpp +++ b/lib/libcxxabi/src/cxa_thread_atexit.cpp @@ -7,20 +7,134 @@ // //===----------------------------------------------------------------------===// +#include "abort_message.h" #include "cxxabi.h" +#include <__threading_support> +#include <cstdlib> namespace __cxxabiv1 { + + using Dtor = void(*)(void*); + + extern "C" +#ifndef HAVE___CXA_THREAD_ATEXIT_IMPL + // A weak symbol is used to detect this function's presence in the C library + // at runtime, even if libc++ is built against an older libc + _LIBCXXABI_WEAK +#endif + int __cxa_thread_atexit_impl(Dtor, void*, void*); + +#ifndef HAVE___CXA_THREAD_ATEXIT_IMPL + +namespace { + // This implementation is used if the C library does not provide + // __cxa_thread_atexit_impl() for us. It has a number of limitations that are + // difficult to impossible to address without ..._impl(): + // + // - dso_symbol is ignored. This means that a shared library may be unloaded + // (via dlclose()) before its thread_local destructors have run. + // + // - thread_local destructors for the main thread are run by the destructor of + // a static object. This is later than expected; they should run before the + // destructors of any objects with static storage duration. + // + // - thread_local destructors on non-main threads run on the first iteration + // through the __libccpp_tls_key destructors. + // std::notify_all_at_thread_exit() and similar functions must be careful to + // wait until the second iteration to provide their intended ordering + // guarantees. + // + // Another limitation, though one shared with ..._impl(), is that any + // thread_locals that are first initialized after non-thread_local global + // destructors begin to run will not be destroyed. [basic.start.term] states + // that all thread_local destructors are sequenced before the destruction of + // objects with static storage duration, resulting in a contradiction if a + // thread_local is constructed after that point. Thus we consider such + // programs ill-formed, and don't bother to run those destructors. (If the + // program terminates abnormally after such a thread_local is constructed, + // the destructor is not expected to run and thus there is no contradiction. + // So construction still has to work.) + + struct DtorList { + Dtor dtor; + void* obj; + DtorList* next; + }; + + // The linked list of thread-local destructors to run + __thread DtorList* dtors = nullptr; + // True if the destructors are currently scheduled to run on this thread + __thread bool dtors_alive = false; + // Used to trigger destructors on thread exit; value is ignored + std::__libcpp_tls_key dtors_key; + + void run_dtors(void*) { + while (auto head = dtors) { + dtors = head->next; + head->dtor(head->obj); + std::free(head); + } + + dtors_alive = false; + } + + struct DtorsManager { + DtorsManager() { + // There is intentionally no matching std::__libcpp_tls_delete call, as + // __cxa_thread_atexit() may be called arbitrarily late (for example, from + // global destructors or atexit() handlers). + if (std::__libcpp_tls_create(&dtors_key, run_dtors) != 0) { + abort_message("std::__libcpp_tls_create() failed in __cxa_thread_atexit()"); + } + } + + ~DtorsManager() { + // std::__libcpp_tls_key destructors do not run on threads that call exit() + // (including when the main thread returns from main()), so we explicitly + // call the destructor here. This runs at exit time (potentially earlier + // if libc++abi is dlclose()'d). Any thread_locals initialized after this + // point will not be destroyed. + run_dtors(nullptr); + } + }; +} // namespace + +#endif // HAVE___CXA_THREAD_ATEXIT_IMPL + extern "C" { + _LIBCXXABI_FUNC_VIS int __cxa_thread_atexit(Dtor dtor, void* obj, void* dso_symbol) throw() { #ifdef HAVE___CXA_THREAD_ATEXIT_IMPL + return __cxa_thread_atexit_impl(dtor, obj, dso_symbol); +#else + if (__cxa_thread_atexit_impl) { + return __cxa_thread_atexit_impl(dtor, obj, dso_symbol); + } else { + // Initialize the dtors std::__libcpp_tls_key (uses __cxa_guard_*() for + // one-time initialization and __cxa_atexit() for destruction) + static DtorsManager manager; + + if (!dtors_alive) { + if (std::__libcpp_tls_set(dtors_key, &dtors_key) != 0) { + return -1; + } + dtors_alive = true; + } + + auto head = static_cast<DtorList*>(std::malloc(sizeof(DtorList))); + if (!head) { + return -1; + } -_LIBCXXABI_FUNC_VIS int __cxa_thread_atexit(void (*dtor)(void *), void *obj, - void *dso_symbol) throw() { - extern int __cxa_thread_atexit_impl(void (*)(void *), void *, void *); - return __cxa_thread_atexit_impl(dtor, obj, dso_symbol); -} + head->dtor = dtor; + head->obj = obj; + head->next = dtors; + dtors = head; -#endif // HAVE__CXA_THREAD_ATEXIT_IMPL + return 0; + } +#endif // HAVE___CXA_THREAD_ATEXIT_IMPL + } } // extern "C" } // namespace __cxxabiv1 diff --git a/lib/libcxxabi/src/cxa_unexpected.cpp b/lib/libcxxabi/src/cxa_unexpected.cpp index f6e6b6ab97e..7d4ef80c39e 100644 --- a/lib/libcxxabi/src/cxa_unexpected.cpp +++ b/lib/libcxxabi/src/cxa_unexpected.cpp @@ -8,20 +8,16 @@ //===----------------------------------------------------------------------===// #include <exception> -#include <cxxabi.h> +#include "cxxabi.h" #include "cxa_exception.hpp" namespace __cxxabiv1 { -#pragma GCC visibility push(default) - extern "C" { } -#pragma GCC visibility pop - } // namespace __cxxabiv1 diff --git a/lib/libcxxabi/src/cxa_virtual.cpp b/lib/libcxxabi/src/cxa_virtual.cpp index ac81ad39d07..f59fa7ee2c6 100644 --- a/lib/libcxxabi/src/cxa_virtual.cpp +++ b/lib/libcxxabi/src/cxa_virtual.cpp @@ -12,12 +12,12 @@ namespace __cxxabiv1 { extern "C" { -_LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_pure_virtual(void) { abort_message("Pure virtual function called!"); } -_LIBCXXABI_FUNC_VIS LIBCXXABI_NORETURN +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_deleted_virtual(void) { abort_message("Deleted virtual function called!"); } diff --git a/lib/libcxxabi/src/fallback_malloc.cpp b/lib/libcxxabi/src/fallback_malloc.cpp new file mode 100644 index 00000000000..336ad31b017 --- /dev/null +++ b/lib/libcxxabi/src/fallback_malloc.cpp @@ -0,0 +1,252 @@ +//===------------------------ fallback_malloc.cpp -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "fallback_malloc.h" + +#include <__threading_support> + +#include <cstdlib> // for malloc, calloc, free +#include <cstring> // for memset + +// A small, simple heap manager based (loosely) on +// the startup heap manager from FreeBSD, optimized for space. +// +// Manages a fixed-size memory pool, supports malloc and free only. +// No support for realloc. +// +// Allocates chunks in multiples of four bytes, with a four byte header +// for each chunk. The overhead of each chunk is kept low by keeping pointers +// as two byte offsets within the heap, rather than (4 or 8 byte) pointers. + +namespace { + +// When POSIX threads are not available, make the mutex operations a nop +#ifndef _LIBCXXABI_HAS_NO_THREADS +_LIBCPP_SAFE_STATIC +static std::__libcpp_mutex_t heap_mutex = _LIBCPP_MUTEX_INITIALIZER; +#else +static void* heap_mutex = 0; +#endif + +class mutexor { +public: +#ifndef _LIBCXXABI_HAS_NO_THREADS + mutexor(std::__libcpp_mutex_t* m) : mtx_(m) { + std::__libcpp_mutex_lock(mtx_); + } + ~mutexor() { std::__libcpp_mutex_unlock(mtx_); } +#else + mutexor(void*) {} + ~mutexor() {} +#endif +private: + mutexor(const mutexor& rhs); + mutexor& operator=(const mutexor& rhs); +#ifndef _LIBCXXABI_HAS_NO_THREADS + std::__libcpp_mutex_t* mtx_; +#endif +}; + +static const size_t HEAP_SIZE = 512; +char heap[HEAP_SIZE] __attribute__((aligned)); + +typedef unsigned short heap_offset; +typedef unsigned short heap_size; + +struct heap_node { + heap_offset next_node; // offset into heap + heap_size len; // size in units of "sizeof(heap_node)" +}; + +static const heap_node* list_end = + (heap_node*)(&heap[HEAP_SIZE]); // one past the end of the heap +static heap_node* freelist = NULL; + +heap_node* node_from_offset(const heap_offset offset) { + return (heap_node*)(heap + (offset * sizeof(heap_node))); +} + +heap_offset offset_from_node(const heap_node* ptr) { + return static_cast<heap_offset>( + static_cast<size_t>(reinterpret_cast<const char*>(ptr) - heap) / + sizeof(heap_node)); +} + +void init_heap() { + freelist = (heap_node*)heap; + freelist->next_node = offset_from_node(list_end); + freelist->len = HEAP_SIZE / sizeof(heap_node); +} + +// How big a chunk we allocate +size_t alloc_size(size_t len) { + return (len + sizeof(heap_node) - 1) / sizeof(heap_node) + 1; +} + +bool is_fallback_ptr(void* ptr) { + return ptr >= heap && ptr < (heap + HEAP_SIZE); +} + +void* fallback_malloc(size_t len) { + heap_node *p, *prev; + const size_t nelems = alloc_size(len); + mutexor mtx(&heap_mutex); + + if (NULL == freelist) + init_heap(); + + // Walk the free list, looking for a "big enough" chunk + for (p = freelist, prev = 0; p && p != list_end; + prev = p, p = node_from_offset(p->next_node)) { + + if (p->len > nelems) { // chunk is larger, shorten, and return the tail + heap_node* q; + + p->len = static_cast<heap_size>(p->len - nelems); + q = p + p->len; + q->next_node = 0; + q->len = static_cast<heap_size>(nelems); + return (void*)(q + 1); + } + + if (p->len == nelems) { // exact size match + if (prev == 0) + freelist = node_from_offset(p->next_node); + else + prev->next_node = p->next_node; + p->next_node = 0; + return (void*)(p + 1); + } + } + return NULL; // couldn't find a spot big enough +} + +// Return the start of the next block +heap_node* after(struct heap_node* p) { return p + p->len; } + +void fallback_free(void* ptr) { + struct heap_node* cp = ((struct heap_node*)ptr) - 1; // retrieve the chunk + struct heap_node *p, *prev; + + mutexor mtx(&heap_mutex); + +#ifdef DEBUG_FALLBACK_MALLOC + std::cout << "Freeing item at " << offset_from_node(cp) << " of size " + << cp->len << std::endl; +#endif + + for (p = freelist, prev = 0; p && p != list_end; + prev = p, p = node_from_offset(p->next_node)) { +#ifdef DEBUG_FALLBACK_MALLOC + std::cout << " p, cp, after (p), after(cp) " << offset_from_node(p) << ' ' + << offset_from_node(cp) << ' ' << offset_from_node(after(p)) + << ' ' << offset_from_node(after(cp)) << std::endl; +#endif + if (after(p) == cp) { +#ifdef DEBUG_FALLBACK_MALLOC + std::cout << " Appending onto chunk at " << offset_from_node(p) + << std::endl; +#endif + p->len = static_cast<heap_size>( + p->len + cp->len); // make the free heap_node larger + return; + } else if (after(cp) == p) { // there's a free heap_node right after +#ifdef DEBUG_FALLBACK_MALLOC + std::cout << " Appending free chunk at " << offset_from_node(p) + << std::endl; +#endif + cp->len = static_cast<heap_size>(cp->len + p->len); + if (prev == 0) { + freelist = cp; + cp->next_node = p->next_node; + } else + prev->next_node = offset_from_node(cp); + return; + } + } +// Nothing to merge with, add it to the start of the free list +#ifdef DEBUG_FALLBACK_MALLOC + std::cout << " Making new free list entry " << offset_from_node(cp) + << std::endl; +#endif + cp->next_node = offset_from_node(freelist); + freelist = cp; +} + +#ifdef INSTRUMENT_FALLBACK_MALLOC +size_t print_free_list() { + struct heap_node *p, *prev; + heap_size total_free = 0; + if (NULL == freelist) + init_heap(); + + for (p = freelist, prev = 0; p && p != list_end; + prev = p, p = node_from_offset(p->next_node)) { + std::cout << (prev == 0 ? "" : " ") << "Offset: " << offset_from_node(p) + << "\tsize: " << p->len << " Next: " << p->next_node << std::endl; + total_free += p->len; + } + std::cout << "Total Free space: " << total_free << std::endl; + return total_free; +} +#endif +} // end unnamed namespace + +namespace __cxxabiv1 { + +struct __attribute__((aligned)) __aligned_type {}; + +void* __aligned_malloc_with_fallback(size_t size) { +#if defined(_WIN32) + if (void* dest = _aligned_malloc(size, alignof(__aligned_type))) + return dest; +#elif defined(_LIBCPP_HAS_NO_ALIGNED_ALLOCATION) + if (void* dest = std::malloc(size)) + return dest; +#else + if (size == 0) + size = 1; + void* dest; + if (::posix_memalign(&dest, alignof(__aligned_type), size) == 0) + return dest; +#endif + return fallback_malloc(size); +} + +void* __calloc_with_fallback(size_t count, size_t size) { + void* ptr = std::calloc(count, size); + if (NULL != ptr) + return ptr; + // if calloc fails, fall back to emergency stash + ptr = fallback_malloc(size * count); + if (NULL != ptr) + std::memset(ptr, 0, size * count); + return ptr; +} + +void __aligned_free_with_fallback(void* ptr) { + if (is_fallback_ptr(ptr)) + fallback_free(ptr); + else { +#if defined(_WIN32) + ::_aligned_free(ptr); +#else + std::free(ptr); +#endif + } +} + +void __free_with_fallback(void* ptr) { + if (is_fallback_ptr(ptr)) + fallback_free(ptr); + else + std::free(ptr); +} + +} // namespace __cxxabiv1 diff --git a/lib/libcxxabi/src/fallback_malloc.h b/lib/libcxxabi/src/fallback_malloc.h new file mode 100644 index 00000000000..d6f471483a2 --- /dev/null +++ b/lib/libcxxabi/src/fallback_malloc.h @@ -0,0 +1,29 @@ +//===------------------------- fallback_malloc.h --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef _FALLBACK_MALLOC_H +#define _FALLBACK_MALLOC_H + +#include "__cxxabi_config.h" +#include <cstddef> // for size_t + +namespace __cxxabiv1 { + +// Allocate some memory from _somewhere_ +_LIBCXXABI_HIDDEN void * __aligned_malloc_with_fallback(size_t size); + +// Allocate and zero-initialize memory from _somewhere_ +_LIBCXXABI_HIDDEN void * __calloc_with_fallback(size_t count, size_t size); + +_LIBCXXABI_HIDDEN void __aligned_free_with_fallback(void *ptr); +_LIBCXXABI_HIDDEN void __free_with_fallback(void *ptr); + +} // namespace __cxxabiv1 + +#endif diff --git a/lib/libcxxabi/src/include/refstring.h b/lib/libcxxabi/src/include/refstring.h new file mode 100644 index 00000000000..bc131aeb5ae --- /dev/null +++ b/lib/libcxxabi/src/include/refstring.h @@ -0,0 +1,131 @@ +//===------------------------ __refstring ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// FIXME: This file is copied from libcxx/src/include/refstring.h. Instead of +// duplicating the file in libc++abi we should require that the libc++ sources +// are available when building libc++abi. + +#ifndef _LIBCPPABI_REFSTRING_H +#define _LIBCPPABI_REFSTRING_H + +#include <__config> +#include <stdexcept> +#include <cstddef> +#include <cstring> +#ifdef __APPLE__ +#include <dlfcn.h> +#include <mach-o/dyld.h> +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace __refstring_imp { namespace { +typedef int count_t; + +struct _Rep_base { + std::size_t len; + std::size_t cap; + count_t count; +}; + +inline _Rep_base* rep_from_data(const char *data_) noexcept { + char *data = const_cast<char *>(data_); + return reinterpret_cast<_Rep_base *>(data - sizeof(_Rep_base)); +} + +inline char * data_from_rep(_Rep_base *rep) noexcept { + char *data = reinterpret_cast<char *>(rep); + return data + sizeof(*rep); +} + +#if defined(__APPLE__) +inline +const char* compute_gcc_empty_string_storage() _NOEXCEPT +{ + void* handle = dlopen("/usr/lib/libstdc++.6.dylib", RTLD_NOLOAD); + if (handle == nullptr) + return nullptr; + void* sym = dlsym(handle, "_ZNSs4_Rep20_S_empty_rep_storageE"); + if (sym == nullptr) + return nullptr; + return data_from_rep(reinterpret_cast<_Rep_base *>(sym)); +} + +inline +const char* +get_gcc_empty_string_storage() _NOEXCEPT +{ + static const char* p = compute_gcc_empty_string_storage(); + return p; +} +#endif + +}} // namespace __refstring_imp + +using namespace __refstring_imp; + +inline +__libcpp_refstring::__libcpp_refstring(const char* msg) { + std::size_t len = strlen(msg); + _Rep_base* rep = static_cast<_Rep_base *>(::operator new(sizeof(*rep) + len + 1)); + rep->len = len; + rep->cap = len; + rep->count = 0; + char *data = data_from_rep(rep); + std::memcpy(data, msg, len + 1); + __imp_ = data; +} + +inline +__libcpp_refstring::__libcpp_refstring(const __libcpp_refstring &s) _NOEXCEPT + : __imp_(s.__imp_) +{ + if (__uses_refcount()) + __sync_add_and_fetch(&rep_from_data(__imp_)->count, 1); +} + +inline +__libcpp_refstring& __libcpp_refstring::operator=(__libcpp_refstring const& s) _NOEXCEPT { + bool adjust_old_count = __uses_refcount(); + struct _Rep_base *old_rep = rep_from_data(__imp_); + __imp_ = s.__imp_; + if (__uses_refcount()) + __sync_add_and_fetch(&rep_from_data(__imp_)->count, 1); + if (adjust_old_count) + { + if (__sync_add_and_fetch(&old_rep->count, count_t(-1)) < 0) + { + ::operator delete(old_rep); + } + } + return *this; +} + +inline +__libcpp_refstring::~__libcpp_refstring() { + if (__uses_refcount()) { + _Rep_base* rep = rep_from_data(__imp_); + if (__sync_add_and_fetch(&rep->count, count_t(-1)) < 0) { + ::operator delete(rep); + } + } +} + +inline +bool __libcpp_refstring::__uses_refcount() const { +#ifdef __APPLE__ + return __imp_ != get_gcc_empty_string_storage(); +#else + return true; +#endif +} + +_LIBCPP_END_NAMESPACE_STD + +#endif //_LIBCPPABI_REFSTRING_H diff --git a/lib/libcxxabi/src/private_typeinfo.cpp b/lib/libcxxabi/src/private_typeinfo.cpp index df03596e8b6..ef9466ef610 100644 --- a/lib/libcxxabi/src/private_typeinfo.cpp +++ b/lib/libcxxabi/src/private_typeinfo.cpp @@ -55,12 +55,7 @@ #include <string.h> #endif -namespace __cxxabiv1 -{ - -#pragma GCC visibility push(hidden) - -inline +static inline bool is_equal(const std::type_info* x, const std::type_info* y, bool use_strcmp) { @@ -73,6 +68,8 @@ is_equal(const std::type_info* x, const std::type_info* y, bool use_strcmp) #endif } +namespace __cxxabiv1 +{ // __shim_type_info @@ -171,8 +168,12 @@ __pointer_to_member_type_info::~__pointer_to_member_type_info() // catch (D2& d2) : adjustedPtr == &d2 (d2 is base class of thrown object) // catch (D2* d2) : adjustedPtr == d2 // catch (D2*& d2) : adjustedPtr == d2 -// +// // catch (...) : adjustedPtr == & of the exception +// +// If the thrown type is nullptr_t and the caught type is a pointer to +// member type, adjustedPtr points to a statically-allocated null pointer +// representation of that type. // Handles bullet 1 bool @@ -228,7 +229,7 @@ __class_type_info::can_catch(const __shim_type_info* thrown_type, if (thrown_class_type == 0) return false; // bullet 2 - __dynamic_cast_info info = {thrown_class_type, 0, this, -1, 0}; + __dynamic_cast_info info = {thrown_class_type, 0, this, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; info.number_of_dst_type = 1; thrown_class_type->has_unambiguous_public_base(&info, adjustedPtr, public_path); if (info.path_dst_ptr_to_static_ptr == public_path) @@ -337,12 +338,11 @@ __vmi_class_type_info::has_unambiguous_public_base(__dynamic_cast_info* info, } } -// Handles bullets 1 and 4 for both pointers and member pointers +// Handles bullet 1 for both pointers and member pointers bool __pbase_type_info::can_catch(const __shim_type_info* thrown_type, void*&) const { - if (is_equal(thrown_type, &typeid(std::nullptr_t), false)) return true; bool use_strcmp = this->__flags & (__incomplete_class_mask | __incomplete_mask); if (!use_strcmp) { @@ -367,7 +367,13 @@ bool __pointer_type_info::can_catch(const __shim_type_info* thrown_type, void*& adjustedPtr) const { - // bullets 1 and 4 + // bullet 4 + if (is_equal(thrown_type, &typeid(std::nullptr_t), false)) { + adjustedPtr = nullptr; + return true; + } + + // bullet 1 if (__pbase_type_info::can_catch(thrown_type, adjustedPtr)) { if (adjustedPtr != NULL) adjustedPtr = *static_cast<void**>(adjustedPtr); @@ -381,8 +387,10 @@ __pointer_type_info::can_catch(const __shim_type_info* thrown_type, // Do the dereference adjustment if (adjustedPtr != NULL) adjustedPtr = *static_cast<void**>(adjustedPtr); - // bullet 3B - if (thrown_pointer_type->__flags & ~__flags) + // bullet 3B and 3C + if (thrown_pointer_type->__flags & ~__flags & __no_remove_flags_mask) + return false; + if (__flags & ~thrown_pointer_type->__flags & __no_add_flags_mask) return false; if (is_equal(__pointee, thrown_pointer_type->__pointee, false)) return true; @@ -419,7 +427,7 @@ __pointer_type_info::can_catch(const __shim_type_info* thrown_type, dynamic_cast<const __class_type_info*>(thrown_pointer_type->__pointee); if (thrown_class_type == 0) return false; - __dynamic_cast_info info = {thrown_class_type, 0, catch_class_type, -1, 0}; + __dynamic_cast_info info = {thrown_class_type, 0, catch_class_type, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; info.number_of_dst_type = 1; thrown_class_type->has_unambiguous_public_base(&info, adjustedPtr, public_path); if (info.path_dst_ptr_to_static_ptr == public_path) @@ -468,7 +476,22 @@ bool __pointer_type_info::can_catch_nested( bool __pointer_to_member_type_info::can_catch( const __shim_type_info* thrown_type, void*& adjustedPtr) const { - // bullets 1 and 4 + // bullet 4 + if (is_equal(thrown_type, &typeid(std::nullptr_t), false)) { + // We assume that the pointer to member representation is the same for + // all pointers to data members and for all pointers to member functions. + struct X {}; + if (dynamic_cast<const __function_type_info*>(__pointee)) { + static int (X::*const null_ptr_rep)() = nullptr; + adjustedPtr = const_cast<int (X::**)()>(&null_ptr_rep); + } else { + static int X::*const null_ptr_rep = nullptr; + adjustedPtr = const_cast<int X::**>(&null_ptr_rep); + } + return true; + } + + // bullet 1 if (__pbase_type_info::can_catch(thrown_type, adjustedPtr)) return true; @@ -476,7 +499,9 @@ bool __pointer_to_member_type_info::can_catch( dynamic_cast<const __pointer_to_member_type_info*>(thrown_type); if (thrown_pointer_type == 0) return false; - if (thrown_pointer_type->__flags & ~__flags) + if (thrown_pointer_type->__flags & ~__flags & __no_remove_flags_mask) + return false; + if (__flags & ~thrown_pointer_type->__flags & __no_add_flags_mask) return false; if (!is_equal(__pointee, thrown_pointer_type->__pointee, false)) return false; @@ -510,9 +535,6 @@ bool __pointer_to_member_type_info::can_catch_nested( #pragma clang diagnostic pop #endif -#pragma GCC visibility pop -#pragma GCC visibility push(default) - #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-field-initializers" @@ -592,13 +614,11 @@ bool __pointer_to_member_type_info::can_catch_nested( // If there is a public path from (dynamic_ptr, dynamic_type) to // (static_ptr, static_type), then return dynamic_ptr. // Else return nullptr. -extern "C" -void* -__dynamic_cast(const void* static_ptr, - const __class_type_info* static_type, - const __class_type_info* dst_type, - std::ptrdiff_t src2dst_offset) -{ + +extern "C" _LIBCXXABI_FUNC_VIS void * +__dynamic_cast(const void *static_ptr, const __class_type_info *static_type, + const __class_type_info *dst_type, + std::ptrdiff_t src2dst_offset) { // Possible future optimization: Take advantage of src2dst_offset // Currently clang always sets src2dst_offset to -1 (no hint). @@ -613,7 +633,7 @@ __dynamic_cast(const void* static_ptr, // be returned. const void* dst_ptr = 0; // Initialize info struct for this search. - __dynamic_cast_info info = {dst_type, static_ptr, static_type, src2dst_offset, 0}; + __dynamic_cast_info info = {dst_type, static_ptr, static_type, src2dst_offset, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; // Find out if we can use a giant short cut in the search if (is_equal(dynamic_type, dst_type, false)) @@ -653,8 +673,8 @@ __dynamic_cast(const void* static_ptr, info.path_dynamic_ptr_to_static_ptr == unknown) { syslog(LOG_ERR, "dynamic_cast error 2: One or more of the following type_info's " - " has hidden visibility. They should all have public visibility. " - " %s, %s, %s.\n", static_type->name(), dynamic_type->name(), + "has hidden visibility. They should all have public visibility. " + "%s, %s, %s.\n", static_type->name(), dynamic_type->name(), dst_type->name()); // Redo the search comparing type_info's using strcmp info = {dst_type, static_ptr, static_type, src2dst_offset, 0}; @@ -689,9 +709,6 @@ __dynamic_cast(const void* static_ptr, #pragma clang diagnostic pop #endif -#pragma GCC visibility pop -#pragma GCC visibility push(hidden) - // Call this function when you hit a static_type which is a base (above) a dst_type. // Let caller know you hit a static_type. But only start recording details if // this is (static_ptr, static_type) -- the node we are casting from. @@ -1274,6 +1291,4 @@ __base_class_type_info::search_below_dst(__dynamic_cast_info* info, use_strcmp); } -#pragma GCC visibility pop - } // __cxxabiv1 diff --git a/lib/libcxxabi/src/private_typeinfo.h b/lib/libcxxabi/src/private_typeinfo.h index ef98c3ae60b..3922ae6ef56 100644 --- a/lib/libcxxabi/src/private_typeinfo.h +++ b/lib/libcxxabi/src/private_typeinfo.h @@ -16,7 +16,6 @@ #include <cstddef> namespace __cxxabiv1 { -#pragma GCC visibility push(hidden) class _LIBCXXABI_TYPE_VIS __shim_type_info : public std::type_info { public: @@ -67,7 +66,7 @@ enum class _LIBCXXABI_TYPE_VIS __class_type_info; -struct __dynamic_cast_info +struct _LIBCXXABI_HIDDEN __dynamic_cast_info { // const data supplied to the search: @@ -153,7 +152,7 @@ public: has_unambiguous_public_base(__dynamic_cast_info *, void *, int) const; }; -struct __base_class_type_info +struct _LIBCXXABI_HIDDEN __base_class_type_info { public: const __class_type_info* __base_type; @@ -206,7 +205,22 @@ public: __volatile_mask = 0x2, __restrict_mask = 0x4, __incomplete_mask = 0x8, - __incomplete_class_mask = 0x10 + __incomplete_class_mask = 0x10, + __transaction_safe_mask = 0x20, + // This implements the following proposal from cxx-abi-dev (not yet part of + // the ABI document): + // + // http://sourcerytools.com/pipermail/cxx-abi-dev/2016-October/002986.html + // + // This is necessary for support of http://wg21.link/p0012, which permits + // throwing noexcept function and member function pointers and catching + // them as non-noexcept pointers. + __noexcept_mask = 0x40, + + // Flags that cannot be removed by a standard conversion. + __no_remove_flags_mask = __const_mask | __volatile_mask | __restrict_mask, + // Flags that cannot be added by a standard conversion. + __no_add_flags_mask = __transaction_safe_mask | __noexcept_mask }; _LIBCXXABI_HIDDEN virtual ~__pbase_type_info(); @@ -233,8 +247,6 @@ public: _LIBCXXABI_HIDDEN bool can_catch_nested(const __shim_type_info *) const; }; -#pragma GCC visibility pop - } // __cxxabiv1 #endif // __PRIVATE_TYPEINFO_H_ diff --git a/lib/libcxxabi/src/stdlib_exception.cpp b/lib/libcxxabi/src/stdlib_exception.cpp new file mode 100644 index 00000000000..a8f71ab0ecc --- /dev/null +++ b/lib/libcxxabi/src/stdlib_exception.cpp @@ -0,0 +1,103 @@ +//===---------------------------- exception.cpp ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#define _LIBCPP_BUILDING_LIBRARY +#define _LIBCPP_BUILDING_NEW +#include <new> +#include <exception> + +namespace std +{ + +// exception + +exception::~exception() _NOEXCEPT +{ +} + +const char* exception::what() const _NOEXCEPT +{ + return "std::exception"; +} + +// bad_exception + +bad_exception::~bad_exception() _NOEXCEPT +{ +} + +const char* bad_exception::what() const _NOEXCEPT +{ + return "std::bad_exception"; +} + + +// bad_alloc + +bad_alloc::bad_alloc() _NOEXCEPT +{ +} + +bad_alloc::~bad_alloc() _NOEXCEPT +{ +} + +const char* +bad_alloc::what() const _NOEXCEPT +{ + return "std::bad_alloc"; +} + +// bad_array_new_length + +bad_array_new_length::bad_array_new_length() _NOEXCEPT +{ +} + +bad_array_new_length::~bad_array_new_length() _NOEXCEPT +{ +} + +const char* +bad_array_new_length::what() const _NOEXCEPT +{ + return "bad_array_new_length"; +} + +// bad_array_length + +#ifndef _LIBCPP_BAD_ARRAY_LENGTH_DEFINED + +class _LIBCPP_EXCEPTION_ABI bad_array_length + : public bad_alloc +{ +public: + bad_array_length() _NOEXCEPT; + virtual ~bad_array_length() _NOEXCEPT; + virtual const char* what() const _NOEXCEPT; +}; + +#endif // _LIBCPP_BAD_ARRAY_LENGTH_DEFINED + +bad_array_length::bad_array_length() _NOEXCEPT +{ +} + +bad_array_length::~bad_array_length() _NOEXCEPT +{ +} + +const char* +bad_array_length::what() const _NOEXCEPT +{ + return "bad_array_length"; +} + + +} // std diff --git a/lib/libcxxabi/src/stdlib_new_delete.cpp b/lib/libcxxabi/src/stdlib_new_delete.cpp new file mode 100644 index 00000000000..0e85f6ad299 --- /dev/null +++ b/lib/libcxxabi/src/stdlib_new_delete.cpp @@ -0,0 +1,264 @@ +//===--------------------- stdlib_new_delete.cpp --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// This file implements the new and delete operators. +//===----------------------------------------------------------------------===// + +#define _LIBCPP_BUILDING_NEW +#define _LIBCPP_BUILDING_LIBRARY +#include "__cxxabi_config.h" +#include <new> +#include <cstdlib> + +#if !defined(_THROW_BAD_ALLOC) || !defined(_NOEXCEPT) || !defined(_LIBCXXABI_WEAK) +#error The _THROW_BAD_ALLOC, _NOEXCEPT, and _LIBCXXABI_WEAK libc++ macros must \ + already be defined by libc++. +#endif +// Implement all new and delete operators as weak definitions +// in this shared library, so that they can be overridden by programs +// that define non-weak copies of the functions. + +_LIBCXXABI_WEAK +void * +operator new(std::size_t size) _THROW_BAD_ALLOC +{ + if (size == 0) + size = 1; + void* p; + while ((p = ::malloc(size)) == 0) + { + // If malloc fails and there is a new_handler, + // call it to try free up memory. + std::new_handler nh = std::get_new_handler(); + if (nh) + nh(); + else +#ifndef _LIBCXXABI_NO_EXCEPTIONS + throw std::bad_alloc(); +#else + break; +#endif + } + return p; +} + +_LIBCXXABI_WEAK +void* +operator new(size_t size, const std::nothrow_t&) _NOEXCEPT +{ + void* p = 0; +#ifndef _LIBCXXABI_NO_EXCEPTIONS + try + { +#endif // _LIBCXXABI_NO_EXCEPTIONS + p = ::operator new(size); +#ifndef _LIBCXXABI_NO_EXCEPTIONS + } + catch (...) + { + } +#endif // _LIBCXXABI_NO_EXCEPTIONS + return p; +} + +_LIBCXXABI_WEAK +void* +operator new[](size_t size) _THROW_BAD_ALLOC +{ + return ::operator new(size); +} + +_LIBCXXABI_WEAK +void* +operator new[](size_t size, const std::nothrow_t&) _NOEXCEPT +{ + void* p = 0; +#ifndef _LIBCXXABI_NO_EXCEPTIONS + try + { +#endif // _LIBCXXABI_NO_EXCEPTIONS + p = ::operator new[](size); +#ifndef _LIBCXXABI_NO_EXCEPTIONS + } + catch (...) + { + } +#endif // _LIBCXXABI_NO_EXCEPTIONS + return p; +} + +_LIBCXXABI_WEAK +void +operator delete(void* ptr) _NOEXCEPT +{ + if (ptr) + ::free(ptr); +} + +_LIBCXXABI_WEAK +void +operator delete(void* ptr, const std::nothrow_t&) _NOEXCEPT +{ + ::operator delete(ptr); +} + +_LIBCXXABI_WEAK +void +operator delete(void* ptr, size_t) _NOEXCEPT +{ + ::operator delete(ptr); +} + +_LIBCXXABI_WEAK +void +operator delete[] (void* ptr) _NOEXCEPT +{ + ::operator delete(ptr); +} + +_LIBCXXABI_WEAK +void +operator delete[] (void* ptr, const std::nothrow_t&) _NOEXCEPT +{ + ::operator delete[](ptr); +} + +_LIBCXXABI_WEAK +void +operator delete[] (void* ptr, size_t) _NOEXCEPT +{ + ::operator delete[](ptr); +} + +#if !defined(_LIBCPP_HAS_NO_ALIGNED_ALLOCATION) + +_LIBCXXABI_WEAK +void * +operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC +{ + if (size == 0) + size = 1; + if (static_cast<size_t>(alignment) < sizeof(void*)) + alignment = std::align_val_t(sizeof(void*)); + void* p; +#if defined(_LIBCPP_WIN32API) + while ((p = _aligned_malloc(size, static_cast<size_t>(alignment))) == nullptr) +#else + while (::posix_memalign(&p, static_cast<size_t>(alignment), size) != 0) +#endif + { + // If posix_memalign fails and there is a new_handler, + // call it to try free up memory. + std::new_handler nh = std::get_new_handler(); + if (nh) + nh(); + else { +#ifndef _LIBCXXABI_NO_EXCEPTIONS + throw std::bad_alloc(); +#else + p = nullptr; // posix_memalign doesn't initialize 'p' on failure + break; +#endif + } + } + return p; +} + +_LIBCXXABI_WEAK +void* +operator new(size_t size, std::align_val_t alignment, const std::nothrow_t&) _NOEXCEPT +{ + void* p = 0; +#ifndef _LIBCXXABI_NO_EXCEPTIONS + try + { +#endif // _LIBCXXABI_NO_EXCEPTIONS + p = ::operator new(size, alignment); +#ifndef _LIBCXXABI_NO_EXCEPTIONS + } + catch (...) + { + } +#endif // _LIBCXXABI_NO_EXCEPTIONS + return p; +} + +_LIBCXXABI_WEAK +void* +operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC +{ + return ::operator new(size, alignment); +} + +_LIBCXXABI_WEAK +void* +operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t&) _NOEXCEPT +{ + void* p = 0; +#ifndef _LIBCXXABI_NO_EXCEPTIONS + try + { +#endif // _LIBCXXABI_NO_EXCEPTIONS + p = ::operator new[](size, alignment); +#ifndef _LIBCXXABI_NO_EXCEPTIONS + } + catch (...) + { + } +#endif // _LIBCXXABI_NO_EXCEPTIONS + return p; +} + +_LIBCXXABI_WEAK +void +operator delete(void* ptr, std::align_val_t) _NOEXCEPT +{ + if (ptr) +#if defined(_LIBCPP_WIN32API) + ::_aligned_free(ptr); +#else + ::free(ptr); +#endif +} + +_LIBCXXABI_WEAK +void +operator delete(void* ptr, std::align_val_t alignment, const std::nothrow_t&) _NOEXCEPT +{ + ::operator delete(ptr, alignment); +} + +_LIBCXXABI_WEAK +void +operator delete(void* ptr, size_t, std::align_val_t alignment) _NOEXCEPT +{ + ::operator delete(ptr, alignment); +} + +_LIBCXXABI_WEAK +void +operator delete[] (void* ptr, std::align_val_t alignment) _NOEXCEPT +{ + ::operator delete(ptr, alignment); +} + +_LIBCXXABI_WEAK +void +operator delete[] (void* ptr, std::align_val_t alignment, const std::nothrow_t&) _NOEXCEPT +{ + ::operator delete[](ptr, alignment); +} + +_LIBCXXABI_WEAK +void +operator delete[] (void* ptr, size_t, std::align_val_t alignment) _NOEXCEPT +{ + ::operator delete[](ptr, alignment); +} + +#endif // !_LIBCPP_HAS_NO_ALIGNED_ALLOCATION diff --git a/lib/libcxxabi/src/stdlib_stdexcept.cpp b/lib/libcxxabi/src/stdlib_stdexcept.cpp new file mode 100644 index 00000000000..e3b7cd40658 --- /dev/null +++ b/lib/libcxxabi/src/stdlib_stdexcept.cpp @@ -0,0 +1,48 @@ +//===------------------------ stdexcept.cpp -------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "include/refstring.h" +#include "stdexcept" +#include "new" +#include <cstdlib> +#include <cstring> +#include <cstdint> +#include <cstddef> + +static_assert(sizeof(std::__libcpp_refstring) == sizeof(const char *), ""); + +namespace std // purposefully not using versioning namespace +{ + +logic_error::~logic_error() _NOEXCEPT {} + +const char* +logic_error::what() const _NOEXCEPT +{ + return __imp_.c_str(); +} + +runtime_error::~runtime_error() _NOEXCEPT {} + +const char* +runtime_error::what() const _NOEXCEPT +{ + return __imp_.c_str(); +} + +domain_error::~domain_error() _NOEXCEPT {} +invalid_argument::~invalid_argument() _NOEXCEPT {} +length_error::~length_error() _NOEXCEPT {} +out_of_range::~out_of_range() _NOEXCEPT {} + +range_error::~range_error() _NOEXCEPT {} +overflow_error::~overflow_error() _NOEXCEPT {} +underflow_error::~underflow_error() _NOEXCEPT {} + +} // std diff --git a/lib/libcxxabi/src/stdlib_typeinfo.cpp b/lib/libcxxabi/src/stdlib_typeinfo.cpp new file mode 100644 index 00000000000..9313be04a39 --- /dev/null +++ b/lib/libcxxabi/src/stdlib_typeinfo.cpp @@ -0,0 +1,53 @@ +//===----------------------------- typeinfo.cpp ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <typeinfo> + +namespace std +{ + +// type_info + +type_info::~type_info() +{ +} + +// bad_cast + +bad_cast::bad_cast() _NOEXCEPT +{ +} + +bad_cast::~bad_cast() _NOEXCEPT +{ +} + +const char* +bad_cast::what() const _NOEXCEPT +{ + return "std::bad_cast"; +} + +// bad_typeid + +bad_typeid::bad_typeid() _NOEXCEPT +{ +} + +bad_typeid::~bad_typeid() _NOEXCEPT +{ +} + +const char* +bad_typeid::what() const _NOEXCEPT +{ + return "std::bad_typeid"; +} + +} // std |